目录
一、wait函数
#include <sys/wait.h>
pid_t wait(int *status);
功能:
调用wait()汉书的进程会被挂起,进入阻塞状态,直到子进程变为僵尸态,wait()函数捕获到该子进程的退出信息时才会转为运行态,回收子进程资源并返回;若没有变为僵尸态的子进程,wait()函数会让进程一直阻塞。若当前进程有多个子进程,只要捕获到一个变为僵尸态的子进程的信息,wait()函数就会返回并使进程恢复执行。
参数说明:
status是一个int *类型的指针,它用来保存子进程退出时的状态信息。通常这个参数值为NULL,表示不在意子进程如何终止。
返回值说明:
(1)成功:返回子进程的进程id
(2)失败:返回-1,errno被设置为ECHILD。
若wait函数的参数不为空,可以获取子进程的退出状态,退出状态存放在参数status的低八位中。Linux定义了一组判断进程退出状态的宏函数,其中最基础的两个是:
#include <sys/wait.h>
int WIFEXITED(int status);//判断子进程是否正常退出,若是,返回非0值,否则返回0
int WEXITSTATUS(int status);//和WIFEXITED配合使用,WIFEXITED返回非0值,则使用该宏提取子进程的返回值。
二、waitpid函数
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
功能:
wait函数的缺点:当前进程有很多个子进程,wait函数无法保证所有子进程在父进程之前执行。
waitpid函数:可以应对 wait函数面临的缺点。可以等待指定的子进程,也可以在父进程不阻塞的情况下获取子进程的状态。
参数说明:pid:一般是进程的pid,也有可能是其他取值。进一步说明如下:
(1)pid > 0:等待子进程(编号为pid)退出,若退出,函数返回;若未结束,则一直等待;
(2)pid = 0:等待同一进程组的所有子进程退出,若某子进程加入了其他进程组,则waitpid不再关心它的状态;
(1)pid = -1:waitpid函数退化为wait函数,阻塞等待并回收一个子进程;
(2)pid < -1:等待指定进程组中的任何子进程,进程组的id等于pid的绝对值。
options: 提供控制选项,可以是一个常量,也可以是|连接的两个常量,选项如下:
(1)WNOHANG:如果子进程没有终止,waitpid不会阻塞父进程,会立即返回;
(2)WUNTRACED:如果子进程暂停执行,waitpid立即返回;
(3)0:不使用选项。
返回值说明:
成功:返回捕捉到的子进程id;
0:options = WNOHANG, waitpid发现没有已退出的子进程可回收;
-1:出错,errno被设置。
三、特殊进程的危害
- 僵尸进程不能再次被运行,会占用一定的内存空间,并占据进程编号,当僵尸进程较多时,将会消耗系统内存,新进程可能因内存不足或无法获取pid而无法创建;
- 父进程通过wait()和waitpid()函数可以有效防止僵尸进程的产生,对于已存在的僵尸进程,则可通过杀死其父进程的方法解决;
- 当僵尸进程的父进程被终止后,僵尸进程将作为孤儿进程被init进程接收,init进程会不断调用wait()函数获取子进程状态,对已处于僵尸态的进程进行处理;
- 孤儿进程永远不会成为僵尸进程。
代码实现
1.使用wait函数实现进程同步
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
pid_t tempPid,tempW;
tempPid = fork();
if(tempPid == -1){
perror("fork error");
exit(1);
}
else if(tempPid == 0){//child
sleep(3);
printf("Child process,pid = %d,ppid = %d\n",getpid(),getppid());
}
else{//parent
tempW = wait(NULL);
printf("Catched a child process,pid = %d,ppid = %d\n",tempW,getppid());
}//of if
printf("..........finish..........\n");
return 0;
}//of main
运行截图:
若wait函数的参数不为空,可以获取子进程的退出状态,退出状态存放在参数status的低八位中。Linux定义了一组判断进程退出状态的宏函数,其中最基础的两个是:
#include <sys/wait.h>
int WIFEXITED(int status);//判断子进程是否正常退出,若是,返回非0值,否则返回0
int WEXITSTATUS(int status);//和WIFEXITED配合使用,WIFEXITED返回非0值,则使用该宏提取子进程的返回值。
2.使用wait同步进程,并使用宏获取子进程的返回值。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include<sys/wait.h>
int main()
{
int tempStatus;
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(5);
}
else{
tempW = wait(&tempStatus);
if(WIFEXITED(tempStatus)){
printf("Child process pid=%d exit normally.\n",tempW);
printf("Return Code:%d\n",WEXITSTATUS(tempStatus));
}
else{
printf("Child process pid=%d exit abnormally.\n",tempW);
}//of if
}//of if
return 0;
}//of main
运行截图:
3.父进程等待进程组中指定子进程,该进程不退出,则父进程一直阻塞。
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(){
pid_t tempPid, tempP, tempW;
tempPid= fork(); //创建第一个子进程
if (tempPid == -1){
perror("fork1 error");
exit(1);
} else if (tempPid == 0){ //子进程沉睡
sleep(5);
printf("First child process:pid=%d\n", getpid());
} else { //父进程继续创建进程
int i;
tempP = tempPid;
for (i = 0; i < 3; i++){ //由父进程创建3个子进程
if ((tempPid = fork()) == 0){
break;
}//of if
}//of for i
if (tempPid == -1){ //出错
perror("fork error");
exit(2);
} else if (tempPid == 0){ //子进程
printf("Child process:pid=%d\n", getpid());
exit(0);
} else { //父进程
tempW = waitpid(tempP, NULL, 0); //等待第一个子进程执行
if (tempW == tempP){
printf("Catch a child Process: pid=%d\n", tempW);
}else{
printf("waitpid error\n");
}//of if
}//of if
}//of if
return 0;
}//of main
运行截图:
4.基于waitpid函数不断获取子进程的状态。
#include <stdio.h>
#include <stdlib.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", w);
}else{
printf("waitpid error\n");
}//of if
}//of if
return 0;
}//of main
运行截图:
作业1:创建一个子进程,使父子进程分别打印不同的内容
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = fork();
if (pid == -1){
perror("fork error");
exit(1);
}
else if (pid > 0){
printf("parent process\n");
}
else if (pid == 0){
printf("child process\n");
}//of if
return 0;
}
运行截图:
作业2:创建一个子进程,使子进程通过exec更改代码段,执行cat命令
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
pid_t tempPid;
tempPid=fork();
if(tempPid == -1){
perror("fork error");
exit(1);
}
else if(tempPid > 0){
printf("parent process:pid=%d\n",getpid());
}
else{
printf("child process:pid=%d\n",getpid());
//execl("/bin/cat","cat","test1",NULL); //1
//execlp("cat","cat","test1",NULL); //2
char *arg[]={"cat","test1",NULL}; //3
execvp("cat",arg);
perror("error exec\n");
printf("child process:pid=%d\n",getpid());
}//of if
return 0;
}//of main
运行截图:
1、2、3的运行结果相同