Linux中的两种特殊进程:
孤儿进程:
当父进程在子进程退出之前退出,子进程就变成孤儿进程。此时子进程会被init进程收养,之后由init进程代替原来的父进程完成状态收集工作。
僵尸进程:
僵尸进程几乎放弃了退出前占用的所有内存资源,只在进程列表中保留一个位置,记载进程的退出状态码等信息共父进程收集。若父进程未回收,子进程将一直处于僵尸状态。
一、wait()函数:
功能:调用wait()函数的进程会被挂起, 进入阻塞状态,直到wait()捕捉到僵尸子进程并回收该子进程的资源,若没有僵尸子进程,wait()函数则会让进程一直处于阻塞状态。
若当前由多个进程, 只需要捕捉到一个僵尸子进程, wait()函数就会返回并是进程恢复执行。
注: wait()函数在<sys/wait.h>中
pid_t wait(int *status);
/*
参数说明:
status为int*类型的指针,用来保存进程退出时的状态信息
我们只想消灭僵尸进程,并不关心子进程如何终止, 因此一
般将该参数设置为NULL。
返回值说明:
调用成功:返回值为被回收子进程pid
调用失败:返回值为-1,error被设置为ECHILD
*/
应用实例一:
若子进程是其父进程的先决进程,调用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){ //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;
}
运行结果:
运行代码过后子进程睡眠了3秒才运行,父进程没有设置睡眠,但是父进程并没有先于子进程打印结果,而是等子进程执行完了被wait()回收了才运行。可以看出在此期间父进程处于阻塞状态。从程序中也可以看出调用成功后wait()的返回值为子进程的进程号。
思考:
wait()进程具有一定的局限性:若当前由多个子进程,那么该函数无法确保作为先决条件的子进程在父进程之前执行。我们应该如何解决这个问题呢?
waitpid() 函数很好地解决了这个问题。
二、waitpid()函数:
waitpid()函数同样位于<sys/wait.h>中。
pid_t waitpid(pid_t pid, int* status, int options);
参数说明:
pid:一般是进程的pid, 单也可以由其他的取值:
1.pid>0时,pid为指定等待的子进程的pid。
2.pid=-1时,waitpid()函数于wait()函数功能相同。
3.pid=0时,等待同一进程组的所有子进程。
4.pid<-1时,等待指定进程组中的任何子进程,进程组,id等于pid的绝对值。
options:提供控制waitpid()的选项,该选项是一个常量或由 | 链接的两个常量:
1.WONHANG: 即使进程没有终止,waitpid()也会立即返回,就是不会使父进程阻塞。
2.WUNTRACED: 如果子进程暂停执行,则waitpid()立即返回。
返回值说明:
1.调用正常返回子进程的pid。
2.调用出错返回-1.
3.当options的值为WONHANG时,但是没有已经退出的子进程可以收集,返回0。
功能描述:
waitpid()可以等待指定的子进程,也可以在父进程不阻塞的情况下获取子进程的状态。
应用实例二:waitpid()等待指定子进程
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{ pid_t tempPid, tempW, tempP;
tempPid = fork();
if(tempPid == -1){
perror("fork1 error.\n");
exit(1);
}
else if(tempPid == 0){
sleep(5); // 该进程睡眠5秒
printf("Create the first child process, pid = %d, ppid = %d\n", getpid(), getppid());
}
else{
tempP = tempPid; // tempP 存放 第一个子进程的pid
int i;
for (i = 0; i<3; i++){ // 创建三个新的子进程
if((tempPid = fork()) == 0){
break;
}
}
if(tempPid == -1){
perror("fork error\n");
exit(2);
}
else if(tempPid == 0){
printf("child process, pid = %d\n", getpid());
exit(0);
}
else{
tempW = waitpid(tempP, NULL, 0); // pid参数赋值为 第一个子进程的pid, 指定收集该进程
if(tempW == tempP){ // 若收集成功,则waitpid()返回值为被回收进程的pid
printf("Catch a child process:pid = %d\n", tempW);
}
else{
printf("wait error.\n");
}
}
}
return 0;
}
运行结果:
由运行结果可知,回收到了第一个子进程。说明了waitpid()函数可以等待指定的子进程。
实例三:waitpid()不断获取某进程中子进程的状态
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
pid_t pid, pidw;
pid = fork();
if(pid == -1){
perror("fork error.\n");
exit(1);
}
else if(pid == 0){
sleep(3);
printf("this is a process, pid = %d", getpid());
exit(0);
}
else{
do{
pidw = waitpid(pid, NULL, WNOHANG);
if(pidw == 0){
printf("no child exited.\n");
sleep(1);
}
}while(pidw == 0);
if(pid == pidw){
printf("Catch a child process, pid = %d\n", pidw);
}
else{
printf("waitpid error.\n");
}
}
return 0;
}
运行结果:
代码中给子进程设置了3的睡眠,但是后面的执行语句并没有因为子进程未被回收而停止执行,说明父进程没有被阻塞。可以看出,waitpid()可以在父进程不阻塞的情况下,获取子进程的状态。
特殊进程的危害:
僵尸进程虽然几乎不占用系统资源,但是它会占用系统的进程号,进程号是有限的,当进程太多时,僵尸进程可能会导致新的进程因为没有进程号而无法创建。
解决僵尸进程的方法是终止其父进程,由init进程代替回收。孤儿进程永远不会成为僵尸进程。