使用fork()函数可以创建一个子进程,这个函数的特殊之处在于,调用一次会返回两次,父进程一次子进程一次,返回值可能的取值如下:
- 1)在父进程中,fork返回新创建子进程的进程ID;
- 2)在子进程中,fork返回0;
- 3)如果出现错误,fork返回一个负值;
执行过fork后,子进程中的操作就与父进程无关了,父进程可以通过wait/waitpid函数来获取子进程的状态。
父进程需要关注一下僵尸进程,通过signal函数监听SIGCHLD信号来调用wait/waitpid函数回收子进程是一种比较常用的解决方法,或者直接在父进程中wait/waitpid阻塞等待子进程退出也可以。
关于wait和waitpid函数,简单说就是wait是一种特殊的waitpid函数,waitpid提供了三个参数,比wait函数多了两个参数:pid和options,关于差别可以参考如下博文
进程同步
如果想要获取子进程中某些操作的执行结果,则需要进行进程间通信(IPC),linux中可以实现进程间通信的方法有很多,比如:管道、信号、消息队列、共享内存、socket套接字等,这里我需要通信的进程是父子进程,直接使用匿名通道比较便利,所以我采用匿名通道来进行简单的通信,示例代码如下:
int Test()
{
int fd[2]; // 创建子进程前定义两个文件描述符作为管道的读端和写端
int ret = pipe(fd); // 创建管道
if (ret == -1) {
return -1;
}
int childPid = fork(); // 创建子进程
if (childPid == 0) { // fork返回值为0对应的是子进程
printf("I am child!!!!\n");
char *buf = "hello, father";
close(fd[0]); // 关闭子进程读端
write(fd[1], buf, strlen(buf) + 1); // 往写端写入数据
close(fd[1]);
exit(0); // 退出子进程
} else if (childPid > 0) { // fork返回值大于0对应的是父进程,返回值为子进程pid
printf("I am father!\n");
printf("The pid of child is %d\n", childPid);
close(fd[1]); // 关闭父进程写端
char recvbuf[10] = { 0 };
read(fd[0], recvbuf, sizeof(recvbuf)); // 读取子进程写入管道的数据
printf("%s\n", recvbuf);
close(fd[0]);
} else { // fork返回值小于0表示出错
return -1;
}
return 0;
}
示例代码是子进程往父进程写消息,如果需要父进程往子进程写可以调换使用的描述符。
关于使用管道来实现进程通信的详细介绍,可以参考如下博文
匿名管道
设置子进程的工作目录
子进程中使用execv运行其他可执行程序,程序的工作目录是与父进程一致的,为了让工作目录改为子进程运行程序的实际运行位置,可以使用chdir函数来修改运行目录