轉載請注明:http://www.coder4.com/index.php /archives/151
首先特別感謝這篇文章給的啟發!
http://hi.baidu.com/%D3%EA%BA%E7%D1%F4/blog/item/6490202aaba49193023bf633.html
對原作者表示敬意和膜拜!
fork()之后,非阻塞(異步)等待子進程(回收僵尸)。
fork()之后,子進程和父進程分叉執行,僵尸進程的產生是因為父進程沒有給子進程“收尸”造成的,又可以根據危害程度分為下述兩類:
總體來說:當子進程結束之后,但父進程未結束之前,子進程將成為僵尸進程。
(1)當子進程結束之后,但父進程未結束之前,子進程將成為僵尸進程,父進程結束后僵尸被init進程回收。
(2)如果子進程結束了,但是父進程始終沒有結束,那么這個僵尸將一直存在,而且隨著exec,僵尸越來越多。
如下面的代碼,在父進程執行的5s內,子進程將為僵尸:
1 |
/* |
2 |
*
main.cc |
3 |
* |
4 |
*
Created on: 2009-12-3 |
5 |
*
Author: liheyuan |
6 |
*
Describe: |
7 |
* |
8 |
*
Last Date: 2009-12-3 |
9 |
*
CopyRight: 2009 @ ICT LiHeyuan |
10 |
*/ |
11 |
12 |
#include <stdio.h> |
13 |
#include
<stdlib.h> |
14 |
#include <signal.h> |
15 |
#include
<unistd.h> |
16 |
17 |
int main() { |
18 |
//子進程的pid |
19 |
int c_pid; |
20 |
int pid; |
21 |
22 |
if ((pid = fork())) { |
23 |
//父進程 |
24 |
c_pid = pid; |
25 |
printf ( "The child process is %d\n" , c_pid); |
26 |
sleep(5); |
27 |
exit (0); |
28 |
}
else { |
29 |
//子進程 |
30 |
printf ( "I 'm a child.\n" ); |
31 |
exit (0); |
32 |
} |
33 |
} |
如上面的代碼,在父進程的5s內,子進程一直是僵尸!
因此,需要對僵尸進程進行回收,傳統的回收方法是,使用wait()函數,等待子進程,wait()是阻塞模式的,當子進程沒有結束之前,wait一直等
待,不往下面的語句執行。
1 |
/* |
2 |
*
main.cc |
3 |
* |
4 |
*
Created on: 2009-12-3 |
5 |
*
Author: liheyuan |
6 |
*
Describe: |
7 |
* |
8 |
*
Last Date: 2009-12-3 |
9 |
*
CopyRight: 2009 @ ICT LiHeyuan |
10 |
*/ |
11 |
12 |
#include <stdio.h> |
13 |
#include
<stdlib.h> |
14 |
#include <signal.h> |
15 |
#include
<unistd.h> |
16 |
#include
<sys/wait.h> |
17 |
18 |
int main() { |
19 |
//子進程的pid |
20 |
int c_pid; |
21 |
int pid; |
22 |
23 |
if ((pid = fork())) { |
24 |
//父進程 |
25 |
c_pid = pid; |
26 |
printf ( "The child process is %d\n" , c_pid); |
27 |
//阻塞等待子進程 |
28 |
int status; |
29 |
if ((pid =
wait(&status)) != -1 && pid == c_pid) { |
30 |
//成功回收子進程 |
31 |
printf ( "The child exit with %d\n" , WEXITSTATUS(status)); |
32 |
fflush (stdin); |
33 |
} else { |
34 |
printf ( "wait() fail.\n" ); |
35 |
} |
36 |
printf ( "Now , The child has
been exit , and I will sleep.\n" ); |
37 |
sleep(20); |
38 |
exit (0); |
39 |
}
else { |
40 |
//子進程 |
41 |
printf ( "I 'm a child.\n" ); |
42 |
sleep(5); |
43 |
exit (0); |
44 |
} |
45 |
} |
如上面的代碼,在子進程執行5秒后,即被回收,在夫進程的20秒內,子進程已經被結束,不再是僵尸。
但是這種利用wait()阻塞等待的方法也有一定的缺陷,那就是父進程必須等待子進程,無法做其他事情,如何非阻塞的等待子進程呢?
man wait,查看NOTES章節,可以找到:
子進程退出的時候,會發送SIGCHLD信號,默認的POSIX不響應,所以,我們只需要把處理SIGCHLD的函數自己實現就OK了,怎么作呢?
signal用于設置處理信號量的規則(或跳轉到的函數)
1 |
signal (SIGCHLD,handler); |
2 |
void handler( int
num) |
3 |
{ |
4 |
//我接受到了SIGCHLD的信號啦 |
5 |
int status; |
6 |
int pid =
waitpid(-1,&status,WNOHANG); |
7 |
if (WIFEXITED(status)) |
8 |
{ |
9 |
printf ( "The child exit with code %d" ,WEXITSTATUS(status)); |
10 |
} |
11 |
} |
OK,全部代碼如下,注意父進程不要再用wait阻塞啦!
1 |
/* |
2 |
*
main.cc |
3 |
* |
4 |
*
Created on: 2009-12-3 |
5 |
*
Author: liheyuan |
6 |
*
Describe: |
7 |
* |
8 |
*
Last Date: 2009-12-3 |
9 |
*
CopyRight: 2009 @ ICT LiHeyuan |
10 |
*/ |
11 |
12 |
#include <stdio.h> |
13 |
#include
<stdlib.h> |
14 |
#include <signal.h> |
15 |
#include
<unistd.h> |
16 |
#include
<sys/wait.h> |
17 |
18 |
void handler( int num) { |
19 |
//我接受到了SIGCHLD的信號啦 |
20 |
int status; |
21 |
int pid = waitpid(-1, &status,
WNOHANG); |
22 |
if (WIFEXITED(status)) { |
23 |
printf ( "The child %d exit with code %d\n" , pid, WEXITSTATUS(status)); |
24 |
} |
25 |
} |
26 |
27 |
int main() { |
28 |
//子進程的pid |
29 |
int c_pid; |
30 |
int pid; |
31 |
32 |
signal (SIGCHLD, handler); |
33 |
34 |
if ((pid = fork())) { |
35 |
//父進程 |
36 |
c_pid = pid; |
37 |
printf ( "The child process is %d\n" , c_pid); |
38 |
39 |
//父進程不用等待,做自己的事
情吧~ |
40 |
for ( int i = 0; i < 10; i++) { |
41 |
printf ( "Do parent things.\n" ); |
42 |
sleep(1); |
43 |
} |
44 |
45 |
exit (0); |
46 |
}
else { |
47 |
//子進程 |
48 |
printf ( "I 'm a child.\n" ); |
49 |
sleep(2); |
50 |
exit (0); |
51 |
} |
52 |
} |