在了解了关于进程的基础知识之后,进入了进程同步的学习,也学习到了几个关于进程同步的函数。
1.wait()函数
功能:挂起进程,进程进入阻塞状态,直到子进程变为僵尸态,如果捕获到子进程的退出信息就会转为运行态,然后回收子进程资源并返回;若没有变为僵尸态的子进程,wait函数就会让进程一直阻塞。若当前进程有多个子进程,只要捕获到一个变为僵尸态的子进程,wait函数就会恢复执行态。
#include <sys/wait.h>
pid_t wait(int *status);
/*
参数说明:
参数status是一个int *类型的指针,用来保存子进程退出时的状态信息。通常情况下该参数设为NULL,表示不关心进程时如何终止的。
返回值说明:
成功:返回子进程的进程id;
失败:返回-1,errno被设置为ECHILD。
*/
示例:阻塞代码进程,等待子进程结束再转为运行状态
代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
pid_t tempPid, tempW;
tempPid = fork();
if (tempPid == -1){ //error
perror("fork error.\n");
exit(1);
}
else if(tempPid == 0){ //child process
sleep(3);
printf("this is child process, pid = %d, ppid = %d\n", getpid(), getppid());
}
else{ //parent process
tempW = wait(NULL); // wait() returns child process's pid
printf("Create a child process, pid = %d, ppid = %d\n", tempW, getppid());
}
printf("finish!!\n");
return 0;
}
这段代码中设置了休眠时间子进程在代码结束运行后,等待了三秒才运行的,但是通过下面的运行图可以了解到父进程并没有先于子进程打印结果,这是为什么呢
答:因为wait()函数的作用,在没有接收到子进程结束的情况下,使程序处于阻塞状态,当子进程结束后,进程又变为运行态,父进程才得以运行,而且可以看到wait()成功执行后返回的是检测到结束的子进程的进程号。
运行结果:
若wait函数的参数不为空,可以获取子进程的退出状态,退出状态存放在参数status的低八位中。Linux定义了一组判断进程退出状态的宏函数,其中最基础的两个是:
#include <sys/wait.h>
int WIFEXITED(int status);//判断子进程是否正常退出,若是,返回非0值,否则返回0
int WEXITSTATUS(int status);//和WIFEXITED配合使用,WIFEXITED返回非0值,则使用该宏提取子进程的返回值。
示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(){
int status;
pid_t pid,w;
pid=fork();
if(pid==-1){
perror("fork error");
exit(-1);
}else if(pid==0){
sleep(3);//设置休眠时间
printf("child process:pid=%d\n", getpid());//子进程号
exit(5);
}else if(pid>0){
w=wait(&status);//子进程结束,以w获得返回的状态值
if(WIFEXITED(status)){//status值的判断语句
printf("child process pid=%d exit normally.\n", w);
printf("return code:%d\n",WEXITSTATUS(status));
}
else
printf("child process pid=%d exit abnormally.",w);
}
return 0;
}//of main
运行结果:
2.waitpid()函数
功能:
wait函数的缺点:当前进程有很多个子进程,wait函数无法保证所有子进程在父进程之前执行。
waitpid函数:可以应对 wait函数面临的缺点。可以等待指定的子进程,也可以在父进程不阻塞的情况下获取子进程的状态
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
*/
参数说明:
pid:一般是进程的pid,也有可能是其他取值。进一步说明如下:
– pid > 0:等待子进程(编号为pid)退出,若退出,函数返回;若未结束,则一直等待;
– pid = 0:等待同一进程组的所有子进程退出,若某子进程加入了其他进程组,则waitpid不再关心它的状态;
– pid = -1:waitpid函数退化为wait函数,阻塞等待并回收一个子进程;
– pid < -1:等待指定进程组中的任何子进程,进程组的id等于pid的绝对值。
options: 提供控制选项,可以是一个常量,也可以是|连接的两个常量,选项如下:
– WNOHANG:如果子进程没有终止,waitpid不会阻塞父进程,会立即返回;
– WUNTRACED:如果子进程暂停执行,waitpid立即返回;
– 0:不使用选项。
返回值说明:
成功:返回捕捉到的子进程id;
0:options = WNOHANG, waitpid发现没有已退出的子进程可回收;
-1:出错,errno被设置。
*/
示例:waitpid()等待指定的子进程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(){
pid_t pid,p,w;
pid =fork();
if (pid==-1){
perror("fork1 error");
exit(1);
}else if(pid==0){
sleep(3);
printf("Child process: pid=%d\n",getpid());
exit(5);
}else if(pid>0){
int i;
p =pid;
for(i=0;i<3;i++)
{
if((pid=fork())==0)
break;
}
if(pid==-1){
perror("fork error");
exit(2);
}
else if(pid==0){
printf("child process:pid=%d\n",getpid());
exit(0);
}
else if(pid>0){
w = waitpid(p,NULL,0);//等待指定进程结束,并接收返回值
if(w==p)
printf("catch a child process:pid=%d\n",w);
else
printf("waitpid error");
}
}
return 0;
}
运行结果: 由运行结果可知,回收到了第一个子进程。说明了waitpid()函数可以等待指定的子进程。
示例:waitpid()不断获得某进程中的子进程状态
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t tempPid, tempW;
tempPid = fork();
if (tempPid == -1){
perror("fork error");
exit(1);
} else if (tempPid == 0){
sleep(3);
printf("Child process:pid=%d\n", getpid());
exit(0);
} else {
do{
tempW = waitpid(tempPid, NULL, WNOHANG);
if (tempW == 0){
printf("No child exited\n");
sleep(1);
}//of if
} while (tempW == 0);
if (tempW == tempPid){
printf("Catch a Child process:pid=%d\n", tempW);
}else{
printf("waitpid error\n");
}//of if
}//of if
return 0;
}//of main
运行结果:
代码中给子进程设置了3的睡眠,但是后面的执行语句并没有因为子进程未被回收而停止执行,说明父进程没有被阻塞。可以看出,waitpid()可以在父进程不阻塞的情况下,获取子进程的状态。