在linux系统编程中,子进程是父进程通过fork系统调用创建的副本,本身会继承父进程的地址空间、代码段等资源,因此可以通过特定的方式让子进程执行指定的函数逻辑。

linux子进程的基本原理
linux中创建子进程最常用的方式是调用fork()系统调用,调用后系统会复制父进程的几乎所有资源,生成一个子进程。fork调用一次会返回两次,在父进程中返回子进程的PID,在子进程中返回0,通过返回值可以判断当前执行的是父进程还是子进程分支。
子进程创建后会拥有和父进程相同的代码段,因此如果不在子进程中做特殊处理,子进程会继续执行父进程中fork之后的代码逻辑。如果希望子进程执行不同的函数,就需要在子进程的分支中添加对应的函数调用逻辑。
直接让子进程执行函数的方式
如果目标函数已经在父进程的代码中存在,不需要加载新的程序,那么可以直接在子进程分支中调用该函数,这是最简单的实现方式。
下面是一个简单的示例,父进程创建子进程后,子进程直接执行自定义的child_func函数:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
// 子进程要执行的函数
void child_func() {
printf("子进程正在执行自定义函数,PID: %dn", getpid());
}
int main() {
pid_t pid = fork();
if (pid < 0) {
// fork失败
perror("fork error");
return 1;
} else if (pid == 0) {
// 子进程分支,直接调用目标函数
child_func();
return 0;
} else {
// 父进程分支,等待子进程结束
wait(NULL);
printf("父进程等待子进程结束,PID: %dn", getpid());
}
return 0;
}
上述代码中,fork之后子进程的pid为0,进入子进程分支后直接调用child_func函数,就实现了子进程执行指定函数的需求。这种方式适合函数逻辑已经在当前程序中的场景。
结合exec系列函数执行外部函数
如果目标函数属于另一个独立的程序,那么需要先通过fork创建子进程,再在子进程中调用exec系列函数,加载新的程序映像,新的程序的main函数或者对应逻辑就会被执行,相当于间接执行了新程序中的函数。
exec系列函数有很多变体,比如execl、execv、execle等,它们的核心作用是替换当前进程的地址空间为新程序的地址空间,执行新程序的代码。
下面是一个示例,父进程创建子进程后,子进程通过execl执行另一个程序,另一个程序中的函数逻辑会被执行:
// 父进程代码 parent.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
perror("fork error");
return 1;
} else if (pid == 0) {
// 子进程分支,执行另一个程序
// 假设当前目录下有编译好的child_prog程序
execl("./child_prog", "child_prog", NULL);
// 如果execl执行成功,下面的代码不会被执行
perror("execl error");
return 1;
} else {
wait(NULL);
printf("父进程执行完毕n");
}
return 0;
}
对应的子程序child_prog.c代码如下:
// 子程序代码 child_prog.c
#include <stdio.h>
#include <unistd.h>
// 子程序中的函数
void target_func() {
printf("子进程加载新程序后执行的函数,PID: %dn", getpid());
}
int main() {
target_func();
return 0;
}
编译两个程序后运行父进程,子进程会通过exec加载child_prog程序,执行其中的target_func函数。这种方式适合需要执行外部程序函数的场景。
注意事项
- 子进程调用函数时,如果函数中使用了父进程的全局变量,需要注意子进程拥有的是变量的副本,修改不会影响父进程的变量值。
- 使用exec系列函数时,如果执行失败需要做好错误处理,避免子进程继续执行父进程的逻辑导致异常。
- 父进程最好通过
wait或者waitpid等待子进程结束,避免子进程变成僵尸进程占用系统资源。
总结
linux中可以通过子进程执行函数,有两种常见方式:如果函数在当前程序中,直接在子进程分支调用即可;如果函数在外部程序中,通过fork加exec的方式加载外部程序来实现。开发者可以根据实际的场景选择合适的方式,同时注意进程间的资源隔离和僵尸进程的问题。