僵尸进程:当父进程在子进程退出之前退出,子进程就变成孤儿进程。此时子进程会被init进程收养,之后由init进程代替原来的父进程完成状态收集工作。
一、wait()函数:僵尸进程几乎放弃了退出前占用的所有内存资源,只在进程列表中保留一个位置,记载进程的退出状态码等信息共父进程收集。若父进程未回收,子进程将一直处于僵尸状态。
功能:调用wait()函数的进程会被挂起, 进入阻塞状态,直到wait()捕捉到僵尸子进程并回收该子进程的资源,若没有僵尸子进程,wait()函数则会让进程一直处于阻塞状态。
若当前由多个进程, 只需要捕捉到一个僵尸子进程, wait()函数就会返回并是进程恢复执行。
注: wait()函数在
pid_t wait(int *status);应用实例一:
若子进程是其父进程的先决进程,调用wait()进程使进程同步。
#include运行结果:#include #include #include 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 = %dn", getpid(), getppid()); } else{ //parent process tempW = wait(NULL); // wait() returns child process's pid printf("Create a child process, pid = %d, ppid = %dn", tempW, getppid()); } printf("finish!!n"); return 0; }
运行代码过后子进程睡眠了3秒才运行,父进程没有设置睡眠,但是父进程并没有先于子进程打印结果,而是等子进程执行完了被wait()回收了才运行。可以看出在此期间父进程处于阻塞状态。从程序中也可以看出调用成功后wait()的返回值为子进程的进程号。
wait()进程具有一定的局限性:若当前由多个子进程,那么该函数无法确保作为先决条件的子进程在父进程之前执行。我们应该如何解决这个问题呢?
waitpid() 函数很好地解决了这个问题。
二、waitpid()函数:waitpid()函数同样位于
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运行结果:#include #include #include 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 = %dn", 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 errorn"); exit(2); } else if(tempPid == 0){ printf("child process, pid = %dn", getpid()); exit(0); } else{ tempW = waitpid(tempP, NULL, 0); // pid参数赋值为 第一个子进程的pid, 指定收集该进程 if(tempW == tempP){ // 若收集成功,则waitpid()返回值为被回收进程的pid printf("Catch a child process:pid = %dn", tempW); } else{ printf("wait error.n"); } } } return 0; }
由运行结果可知,回收到了第一个子进程。说明了waitpid()函数可以等待指定的子进程。
#include运行结果:#include #include #include 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 = %dn", pidw); } else{ printf("waitpid error.n"); } } return 0; }
代码中给子进程设置了3的睡眠,但是后面的执行语句并没有因为子进程未被回收而停止执行,说明父进程没有被阻塞。可以看出,waitpid()可以在父进程不阻塞的情况下,获取子进程的状态。
僵尸进程虽然几乎不占用系统资源,但是它会占用系统的进程号,进程号是有限的,当进程太多时,僵尸进程可能会导致新的进程因为没有进程号而无法创建。
解决僵尸进程的方法是终止其父进程,由init进程代替回收。孤儿进程永远不会成为僵尸进程。



