目录:
1.了解进程的相关概念
2.掌握fork/getpid/getppid函数使用
3.掌握ps/kill命令使用
4.掌握execl/execlp函数的使用
5.什么是孤儿进程、僵尸进程
6.掌握wait函数使用
7.掌握waitpid函数的使用
概念
孤儿进程:父进程死了,子进程被init进程领养
僵尸进程:子进程死了,父进程没有回收子进程的资源(PCB)存放于内核中,变成僵尸进程。
注意:僵尸进程是不能使用kill命令清除掉的,因为kill命令只是用来终止进程的,而僵尸进程已经终止。如何回收僵尸进程?杀死父进程、init领养并负责回收。
wait函数
一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止,则保留着退出信息;
如果是异常终止,则保存着导致该进程终止的那个信号是哪个,这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。
已知一个进程的退出状态可以在shell中用特殊变量$?查看,因为shell是它的父进程,当它终止时shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。
父进程调用wait函数可以回收子进程终止信息,该函数有三个功能:
- 阻塞等待子进程退出
- 回收子进程残留资源
- 获取子进程结束状态(退出原因)
函数原型:pid_t wait(int *status);
- status 传出参数
- 返回值 成功返回终止的子进程ID, 失败返回-1.
子进程死亡原因:
- 正常死亡:WIFEXITED,如果 WIFEXITED 为真,使用 WEXITSTATUS 得到退出状态
- 非正常死亡:WIFSIGNALED,如果 WIFSIGNALED 为真,使用 WTERMSIG 得到信号
例子:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
int main()
{
pid_t pid = fork();
if(pid == 0)
{
printf("I am child, will die!\n");
sleep(2);
//return 101; //正常return退出
exit(102); //正常exit退出
}
else if(pid > 0)
{
printf("I am parent,wait for child die!\n");
int status;
//pid_t wpid = wait(NULL);
pid_t wpid = wait(&status);
printf("wait ok, wpid=%d,pid=%d\n",wpid,pid);
if(WIFEXITED(status))
{
printf("child exit with %d\n",WEXITSTATUS(status));
}
if(WIFSIGNALED(status))
{
printf("child killed by %d\n",WTERMSIG(status));
}
while(1)
{
sleep(1);
}
}
return 0;
}
输出:
~$ gcc wait_.c
~$ ./a.out
I am parent,wait for child die!
I am child, will die!
wait ok, wpid=24871,pid=24871
child exit with 102
waitpid函数
pid_t waitpid(pid_t pid, int *status, int options);
参数:
- pid ,(若<-1,为组id;-1,为回收任意;0,回收和调用进程组id相同组内的子进程;>0,回收指定的pid)
- options,(0与wait相同,也会阻塞;WNOHANG 如果当前没有子进程退出的,会立即返回)
返回值:
- 如果设置了WNOHANG,那么如果没有子进程退出,返回0;如果有子进程退出,返回退出的pid。
- 失败返回-1。
例子:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/types.h>
int main()
{
int n = 5;
int i;
pid_t pid;
for(i=0;i<n;i++)
{
pid = fork();
if(pid == 0)
{
//每创建完一个子进程后退出,避免子进程又再创建子子进程
break;
}
}
if(i == 5)
{
//parent,如果i=5,说明子进程创建完毕,程序运行回到父进程
printf("I am parent!\n");
//如何使用waitpid回收? -1 代表子进程死了,都回收
while (1)
{
pid_t wpid=waitpid(-1,NULL,WNOHANG); //参数:-1,回收任意pid; 设置WNOHANG可返回回收的pid
if(wpid == -1)
{
//waitpid返回值,如果没有子进程退出返回-1。
break;
}
else if(wpid > 0)
{
//waitpid返回值,设置了WNOHANG,如果有子进程退出返回退出子进程的pid。
printf("waitpid wpid=%d\n",wpid);
}
}
while (1)
{
sleep(1);
}
}
if(i < 5)
{
sleep(i);
printf("I am child, i=%d, pid=%d\n",i,getpid());
}
return 0;
}
输出:
~$ gcc nfork_waitpid.c
~$ ./a.out
I am child, i=0, pid=11033
I am parent!
waitpid wpid=11033
I am child, i=1, pid=11034
waitpid wpid=11034
I am child, i=2, pid=11035
waitpid wpid=11035
I am child, i=3, pid=11036
waitpid wpid=11036
I am child, i=4, pid=11037
waitpid wpid=11037