有些时候,进程之间需要协助才能完成一些任务,比如说,进程A和进程B共同完成一项任务,但是必须进程A先完成,然后进程B再开始,等进程B也完成了,任务也就完成了。
先给出一个有问题的实例,然后我们来分析并解决它:
aa.c
//父进程
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
int main(){
pid_t pid = fork();
if(pid == -1){
fprintf(stderr,"%s:%s\n","Can't fork process",strerror(errno));
exit(1);
}
if(!pid){
execl("bb","bb",NULL);
}
FILE *my_file = fopen("test.log","r");
char c[80];
fscanf(my_file,"%79[^\n]\n",c);
printf("%s\n",c);
return 0;
}
bb.c
//子进程
#include <stdio.h>
#include <unistd.h>
int main(){
FILE *my_file = fopen("test.log","w");
if(my_file == NULL){
fprintf(stderr,"error!!!!");
}
sleep(5);
fprintf(my_file,"Hello world!Good morning!");
fclose(my_file);
return 0;
}
编译运行:
~/Desktop/MyC$ touch test.log
~/Desktop/MyC$ gcc aa.c -o aa
~/Desktop/MyC$ gcc bb.c -o bb
~/Desktop/MyC$ ./aa
|
~/Desktop/MyC$ tail -f test.log
Hello world!Good morning!
原因分析:
首先说说这个程序要达到的目的,首先父进程aa打开子进程bb,子进程负责把“Hello world!Good morning!”写入文件test.log。然后父进程aa再把test.log内容读出来。但是这个有问题的实例,在运行后,并没有打印任何内容。使用tail命令查看test.log文件时,发现数据已成功写入。
为什么会这样呢?
其实是因为创建的子进程与父进程之间已没有任何关系了。也就是说父进程并没有等待子进程执行完,就自己执行完了。有没有办法让父进程等待子进程执行完毕再继续执行呢?答案是有的,使用waitpid()函数。
waitpid()函数会等待子进程结束以后才返回。因此我们调整一下主程序aa.c的代码:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
int main(){
pid_t pid = fork();
if(pid == -1){
fprintf(stderr,"%s:%s\n","Can't fork process",strerror(errno));
exit(1);
}
if(!pid){
execl("bb","bb",NULL);
}
int pid_status;
if(waitpid(pid,&pid_status,0) == -1){
fprintf(stderr,"error !!!");
return 2;
}
FILE *my_file = fopen("test.log","r");
char c[80];
fscanf(my_file,"%79[^\n]\n",c);
printf("%s\n",c);
return 0;
}
我们在主程序中添加了
if(waitpid(pid,&pid_status,0) == -1){
fprintf(stderr,"error !!!");
return 2;
}
这段代码的意思就是要等待子程序执行完毕。
编译运行一下:
~/Desktop/MyC$ gcc aa.c -o aa
~/Desktop/MyC$ ./aa
Hello world!Good morning!
结果符合我们的预期。
分析一下waitpid(pid,pid_status,options)函数:
pid:父进程克隆子进程时会得到子进程的ID。更多进程信息尽在进程与系统调用——fork、exec
pid_status: 用来保存进程的退出信息。因为waitpid()需要修改pid_status,因此它必须是个指针。
options:可以通过man waitpid查看它的选项,如果把选项设为0,函数将等待进程结束。
什么是pid_status?
waitpid()函数结束等待时会在pid_status中保存一个值,它告诉你进程的完成情况。为了得到子进程的退出状态,可以把pid_status的值传给WEXITSTATUS()宏:
if(WEXITSTATUS(pid_status)){ //如果退出状态不是0
puts("Error status non-zero");
}
谢谢阅读!