Perl进程:僵尸进程和孤儿进程
概念
僵尸进程:当子进程退出时,父进程还没有(使用wait或waitpid)接收其退出状态时,子进程就成了僵尸进程
孤儿进程:当子进程还在运行时,父进程先退出了,子进程就会成为孤儿进程被pid=1的init/systemd进程收养
需要说明的是,僵尸进程的父进程死掉后,僵尸进程也会被pid=1的init/systemd进程收养,而init/systemd进程会定期清理其下僵尸进程,并在它的任意子进程退出时检查它的领土下是否有僵尸进程存在,从而保证init/systemd下不会有太多僵尸进程。
僵尸进程模拟
#!/usr/bin/perl
#
use strict;
use warnings;
defined(my $pid = fork) or die "fork failed: $!";
unless($pid){
# child process
print "I am child process\n";
exit;
}
# parent process
print "I am parent process\n";
sleep(2);
system("ps -o pid,ppid,state,tty,command");
print "parent process exiting\n";
exit;
执行结果:
I am parent process
I am child process
PID PPID S TT COMMAND
22342 22340 S pts/0 -bash
22647 22342 S pts/0 perl zombie2.pl
22648 22647 Z pts/0 [perl] <defunct>
22649 22647 R pts/0 ps -o pid,ppid,state,tty,command
parent process exiting
孤儿进程模拟
#!/usr/bin/perl
use strict;
use warnings;
defined(my $pid = fork) or die "fork failed: $!";
unless($pid){
# 子进程
print "second child, ppid=",getppid(),"\n";
sleep(5);
print "second child, ppid=",getppid(),"\n";
exit 0;
}
# 父进程
sleep 1;
结果:
second child, ppid=22683
# 5秒之后输出
second child, ppid=1
解决僵尸进程的方式
僵尸进程是因为没有使用wait/waitpid接收子进程的退出状态,只要使用wait/waitpid接收该子进程的退出状态,父进程就会为子进程收尸善后。
另外,当子进程退出时,内核会立即发送SIGCHLD信号给父进程告知其该子进程退出了。
有几种方式可以应对僵尸进程:
- 直接在父进程中使用wait/waitpid等待所有子进程退出(不能留下任一个子进程)
- 在父进程中定义SIGCHLD信号的处理程序,并在该信号处理程序中调用wait/waitpid为每个退出的子进程收尸
- 连续fork两次,在第二次fork中执行主代码,第一次fork的子进程立即退出并在父进程中被收尸。这使得第一个退出的子进程不会成为僵尸进程,也使得第二个子进程立即成为孤儿进程被pid=1的init/systemd收养,从而保证其不会成为僵尸进程。这样,需要想要成为孤儿的已经孤儿了,父进程却可以继续执行父进程的代码。如果只fork一次,想要子进程孤儿,父进程继续执行代码是不可能的,因为只有父进程退出,子进程才会孤儿
这三种方式中,前两种用的比较多,第三种比较技巧化,但是也有其用处,比如实现脱离终端的进程。
等待所有子进程退出
父进程中等待所有子进程退出的方式:
until(wait == -1){}
until(waitpid -1, 0 == -1){}
until(waitpid -1, WNOHANG == -1){}
例如:
#!/usr/bin/perl
use strict;
use warnings;
use POSIX qw(WNOHANG);
# fork 5个子进程
for (1..5) {
defined(my $pid = fork) or die "fork error: $!";
unless($pid){
# 子进程
print "I am child: $_\n";
sleep 1;
exit 0;
}
}
# 每秒非阻塞wait一次
until(waitpid(-1, WNOHANG) == -1){
print "any children still exists\n";
sleep 1;
}
print "all child exits\n";
system("ps -o pid,ppid,state,tty,command");
exit 0;
执行结果:
I am child: 1
I am child: 2
I am child: 3
any children still exists
I am child: 5
I am child: 4
any children still exists
any children still exists
any children still exists
any children still exists
any children still exists
any children still exists
all child exits
PID PPID S TT COMMAND
22342 22340 S pts/0 -bash
24547 22342 S pts/0 perl waitallchild.pl
24553 24547 R pts/0 ps -o pid,ppid,state,tty,command
这里输出了多个"any children...",是因为waitpid对于每个等待到的pid都返回一次,此外如果检查的时候没有任何退出的子进程,也会每秒返回一次。
最终的结果中显示没有僵尸进程的存在。
SIGCHLD处理程序收掉僵尸进程
#!/usr/bin/perl
use strict;
use warnings;
use POSIX qw(WNOHANG);
sub reap_child;
# 注册SIGCHLD信号的处理程序
$SIG{CHLD}=\&reap_child;
# fork 5个子进程
for (1..5){
defined(my $pid = fork) or die "fork failed: $!";
unless($pid){
# 子进程
print "I am child: $_\n";
sleep 1;
exit 0;
} else {
print "child $_: pid=$pid\n";
}
}
# 父进程
sleep 20;
system("ps -o pid,ppid,state,tty,command");
sub reap_child {
print "SIGCHLD triggered at:",~~localtime, "\n";
# 只要有子进程退出,就收尸
while((my $kid = waitpid -1, WNOHANG) > 0){
print "$kid reaped\n";
}
}
执行结果:
child 1: pid=24857
I am child: 1
child 2: pid=24858
I am child: 2
child 3: pid=24859
I am child: 3
child 4: pid=24860
I am child: 4
child 5: pid=24861
I am child: 5
SIGCHLD triggered at:Mon Feb 25 13:49:43 2019
24857 reaped
24859 reaped
24860 reaped
PID PPID S TT COMMAND
22342 22340 S pts/0 -bash
24856 22342 S pts/0 perl reap_zombie.pl
24858 24856 Z pts/0 [perl] <defunct>
24861 24856 Z pts/0 [perl] <defunct>
24862 24856 R pts/0 ps -o pid,ppid,state,tty,command
SIGCHLD triggered at:Mon Feb 25 13:49:43 2019
24858 reaped
24861 reaped
发现只需1-2秒程序就终止了,但父进程明明就sleep 20了,为什么?还有结果好像很奇怪?不仅有两个僵尸进程还只触发了两次SIGCHLD信号处理程序。
上面触发了两次SIGCHLD信号处理程序,因为第二次触发的是system()打开的子进程ps命令退出时触发的。
之所以1-2秒就结束,是因为子进程结束时,内核发送SIGCHLD信号给父进程,会中断父进程的sleep睡眠。
只触发两次信号处理程序就能收走5个子进程,其中第一次触发收走了3个子进程,第二次触发收走了2个子进程,是因为waitpid会返回所有等待到的子进程pid,第一次等待到了3个子进程的退出,第二次等待到了2个子进程的退出。
那么为什么system()中的ps退出时没有被SIGCHLD信号处理程序中的waitpid收走?这是因为system()函数自身就带有了wait阻塞函数,它自己会收走经过它fork出来的子进程,使得虽然ps的退出触发了SIGCHLD,但ps的退出状态值已经清空了,无法被信号处理程序中的waitpid处理。
fork两次收掉僵尸进程
代码如下:
#!/usr/bin/env perl
use strict;
use warnings;
defined(my $pid = fork) or die "fork failed: $!";
unless($pid){
# 第一个子进程
# 继续fork一个孙子进程:第二个子进程
defined(my $kid = fork) or die "fork failed: $!";
if($kid){
# 第一个子进程5秒后退出
sleep 5;
exit 0;
}
# 孙子进程
sleep(10);
print "second child, ppid=",getppid(),"\n";
exit 0;
}
# 为第一个子进程收尸
(waitpid $pid, 0 == $pid) or die "waitpid error: $!";
exit 0;
上面的代码中,在5秒后第一个子进程退出并被父进程收尸,第二个进程将成为孤儿进程被pid=1的进程收养。
Perl进程:僵尸进程和孤儿进程的更多相关文章
- linux系统编程之进程(三):进程复制fork,孤儿进程,僵尸进程
本节目标: 复制进程映像 fork系统调用 孤儿进程.僵尸进程 写时复制 一,进程复制(或产生) 使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文.进程堆栈. ...
- [Linux] 孤儿进程与僵尸进程[总结]
转载: http://www.cnblogs.com/Anker/p/3271773.html 1.前言 之前在看<unix环境高级编程>第八章进程时候,提到孤儿进程和僵尸进程,一直对这两 ...
- 进程——wait与waitpid、僵尸进程与孤儿进程
僵尸进程:子进程终止了,但是父进程没有回收子进程的资源PCB.使其成为僵尸进程 孤儿进程:父进程先与子进程结束了,使得子进程失去了父进程,这个时候子进程会被1号进程init进程领养,成为孤儿进程 为了 ...
- Linux-进程描述(3)之进程状态僵尸进程与孤儿进程
进程状态 进程状态反映进程执行过程的变化.这些状态随着进程的执行和外界条件的变化而转换.为了弄明正正在运行的进程是什么意思,我们需要知道进程的不同状态.一个进程可以有多个状态(在Linux内核中,进程 ...
- 020_Linux的孤儿进程与僵尸进程(Unix系统编程)
1.前言 之前在看<unix环境高级编程>第八章进程时候,提到孤儿进程和僵尸进程,一直对这两个概念比较模糊.今天被人问到什么是孤儿进程和僵尸进程,会带来什么问题,怎么解决,我只停留在概念上 ...
- [linux]孤儿进程与僵尸进程
转载自:http://www.cnblogs.com/Anker/p/3271773.html 一.前言 之前在看<unix环境高级编程>第八章进程时候,提到孤儿进程和僵尸进程,一直对这两 ...
- 孤儿进程VS僵尸进程
我们知道在unix/linux中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程.子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束. 当一个 进程完成 ...
- PHP7 网络编程(三)孤儿进程与僵尸进程
基本概念 我们知道在unix/linux中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程.子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束. 当一个 ...
- 二十三、Linux 进程与信号---进程链和进程扇、守护进程和孤儿进程以及僵尸进程
23.1 进程链和进程扇 23.1.1 概念 进程链:一个父进程构建出一个子进程,子进程再构建出子子进程,子子进程构建出子子子进程.... 这种就为进程链 进程扇:一个父进程构建出多个子进程,子进程都 ...
- Python脚本模拟僵尸进程与孤儿进程
最近一台机器的systemd内存高达30%多,一直不变,后来排查是僵尸进程,什么是僵尸进程呢,只能google,百度等先了解,然后自己总结了一下,虽然这是基础的东西,但是对于我来说就如新大陆一样.花了 ...
随机推荐
- 关于H5的一些杂思细想(一)
作为一名前端程序媛,虽然整天和代码打交道,但是还是有一颗小清新的内心,虽然有时候加起班来不是人,但是空闲的时候还是会整理一下思绪,顺便整理一下自己,两个多月的加班,一直没有更新,今天就把自己最近做的一 ...
- 动态规划-LIS
https://vjudge.net/contest/297216?tdsourcetag=s_pctim_aiomsg#problem/E #include<bits/stdc++.h> ...
- [LeetCode] Car Fleet 车队
N cars are going to the same destination along a one lane road. The destination is target miles awa ...
- Linux系统下如何运行.sh文件
在Linux系统下运行.sh文件有两种方法,比如我在root目录下有个datelog.sh文件 第一种(这种办法需要用chmod使得文件具备执行条件(x): chmod u+x datelog.sh) ...
- redis离线集群安装
用一个叫redis-trib.rb的ruby脚本.redis-trib.rb是redis官方推出的管理redis集群的工具,集成在redis的源码src目录下(redis-xxx/src/).是基于r ...
- 小程序页面跳转传参参数值为url时参数时 会出现丢失
当参数的值为url的时候, ?号 _ 下划线 等等 都会被 截取掉,看不到, 这样在 另一个页面 options中 截取的url就不完全 let url="http://ba ...
- android sdk 历史版本下载地址
https://developer.android.google.cn/studio/archive#android-studio-3-0?utm_source=androiddevtools& ...
- HTML5 部分新增语义化标签元素
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- R语言之Apriori算法
---恢复内容开始--- 1.概念 关联分析:用于发现隐藏在大型数据集中的有意义的联系 项集:0或多个项的集合.例如:{啤酒,尿布,牛奶,花生} 是一个4-项集,意义想象成爸爸去超市买啤酒和花生,给儿 ...
- Mesos源码分析(4) Mesos Master的启动之三
3. ModuleManager::load(flags.modules.get())如果有参数--modules或者--modules_dir=dirpath,则会将路径中的so文件load进来 ...