L010Linux和androidNDK之linux避免僵尸进程,子进程退出的处理
如果你在程序中fork出一个子进程,没有好好处理子进程退出后的相关事宜,那么就有可能召唤出传说中进程界的僵尸---僵尸进程!!!
什么是僵尸进程
一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被 僵尸进程销毁, 而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是 使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)
僵尸进程是怎么样产生
在Linux进程的状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸。
如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。
但是如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程.
异步回收僵尸进程:
fork()之后,子进程从父进程获取了一份拷贝,和父进程分别独立运行,僵尸进程的产生是因为父进程没有给子进程“收尸”造成的,又可以根据危害程度分为下述两类:
总体来说:当子进程结束之后,但父进程未结束之前,子进程将成为僵尸进程。
(1)当子进程结束之后,但父进程未结束之前,子进程将成为僵尸进程,父进程结束后僵尸被init进程回收。
(2)如果子进程结束了,但是父进程始终没有结束,那么这个僵尸将一直存在,而且随着exec,僵尸越来越多。
相关回收进程的函数
#include<sys/types.h>
#include<sys/wait.h>
pid_t waitpid(pid_t pid,int * status,int options);
pid_t wait (int * status);
其中 wait(&status);等价于waitpid(-1, &status, 0);
waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束
如果在调用 waitpid()时子进程已经结束,则 waitpid()会立即
返回子进程结束状态值。 子进程的结束状态值会由参数 status 返回,
而子进程的进程识别码也会一起返回。如果不在意结束状态值,则
参数 status 可以设成 NULL。参数 pid 为欲等待的子进程识别码,
其他数值意义如下:
pid<-1 等待进程组识别码为 pid 绝对值的任何子进程。
pid=-1 等待任何子进程,相当于 wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为 pid 的子进程。
参数options提供了一些额外的选项来控制waitpid,参数 option 可以为 0 或可以用"|"运算符把它们连接起来使用
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
void handler(int num) {
//我接受到了SIGCHLD的信号啦
int status;
int pid = waitpid(-1, &status, WNOHANG);
if (WIFEXITED(status)) {
printf("The child %d exit with code %d\n", pid, WEXITSTATUS(status));
}
}
int main() {
//子进程的pid
int c_pid;
int pid;
signal(SIGCHLD, handler);
if ((pid = fork())) {
//父进程
c_pid = pid;
printf("The child process is %d\n", c_pid);
//父进程不用等待,做自己的事情吧~
int i=0;
for ( i = 0; i < 16; i++) {
printf("Do parent things.\n");
sleep(1);
}
printf("Do parent end\n");
exit(0);
} else {
//子进程
printf("I 'm a child.\n");
sleep(2);
exit(0);
}
}
子进程的结束状态
WIFEXITED/WEXITSTATUS/WIFSIGNALED
If the exit status value (*note Program Termination::) of the child
process is zero, then the status value reported by `waitpid' or `wait'
is also zero. You can test for other kinds of information encoded in
the returned status value using the following macros. These macros are
defined in the header file `sys/wait.h'.
-- Macro: int WIFEXITED (int STATUS)
This macro returns a nonzero value if the child process terminated
normally with `exit' or `_exit'.
-- Macro: int WEXITSTATUS (int STATUS)
If `WIFEXITED' is true of STATUS, this macro returns the low-order
8 bits of the exit status value from the child process. *Note
Exit Status::.
-- Macro: int WIFSIGNALED (int STATUS)
This macro returns a nonzero value if the child process terminated
because it received a signal that was not handled. *Note Signal
Handling::
子进程的结束状态返回后存于status,如下有几个宏可判别结束情况
WIFEXITED(status)如果子进程正常结束则为非0值。
WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED 来判断是否正常结束才能使用此宏。
WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真
WTERMSIG(status)取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED 来判断后才使用此宏。
WIFSTOPPED(status)如果子进程处于暂停执行情况则此宏值为真。一般只有使用WUNTRACED 时才会有此情况。
WSTOPSIG(status)取得引发子进程暂停的信号代码,一般会先用WIFSTOPPED 来判断后才使用此宏。
kill -STOP 1234 进程暂停。
kill -CONT 1234 进程继续