加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Perl多进程实践——fork

发布时间:2020-12-15 23:50:42 所属栏目:大数据 来源:网络整理
导读:perl作为一种解释性的语言,很受广大系统管理员的欢迎,好处么就不多说了,坏处也有不少,例如对线程的支持,就一直不咋地,所以大多数情况下,我们都需要多个进程,来帮助我们完成工作,闲话少说,上代码。 #!/usr/bin/perl # test_proc.pl # test multi pr
perl作为一种解释性的语言,很受广大系统管理员的欢迎,好处么就不多说了,坏处也有不少,例如对线程的支持,就一直不咋地,所以大多数情况下,我们都需要多个进程,来帮助我们完成工作,闲话少说,上代码。
#!/usr/bin/perl
# test_proc.pl
# test multi process
# create by lianming: 2009-08-12
use strict;
use warnings;
## == fork a new process ==
my $pid = fork();
if (!defined($pid)) {
? ? print "Error in fork: $!";
? ? exit 1;
}
if ($pid == 0) {
? ? ## == child proc ==
? ? print "Child: My pid = $$n";
? ? sleep(5);
? ? print "Child: endn";
? ? exit 0;
} else {
? ? ## == parent proc ==
? ? print "Parent My pid = $$,and my child's pid = $pidn";
? ? print "Parent: endn";
exit 0;
? ???执行结果如下:
Child: My pid = 19481
Parent My pid = 19480,and my child's pid = 19481
(5秒钟等待)
Child: end
Parent: end
? ???父进程派生子进程,之需要一条命令,那就是fork,fork函数的返回值赋给一个变量,上例中赋给了"$pid",接下来,就要根据$pid值的不同,来分三种情况了。
? ???1、fork失败的情况:这个时候,$pid处于未定义的状态,上例中做的一个"if (!defined($pid))"的判断,如果为真,说明$pid未定义,fork失败,这个时候就要打印错误信息,并且退出。
? ???2、子进程:如果是子进程,那么$pid的值为0,就是上例中"if ($pid == 0)"条件为真的状况,在"$pid == 0"的时候,那就都是子进程了,上例中,子进程将自己的pid打出来,为19481。
? ???3、父进程:如果是父进程,那么$pid的值为它派生出的子进程的pid,也就是不为0,就是else的情况,上例中把$pid打出来,可以看到,也是 19481,就是子进程的pid值。
? ???这只是一个最简单的例子,一个父进程派生一个子进程,再稍微复杂一点,一个父进程派生多个子进程,代码如下:
# test_proc_1.pl
for (my $i = 0; $i < 10; $i ++) {
? ? ## == fork a new process ==
? ? my $pid = fork();
? ? if (!defined($pid)) {
? ?? ???print "Error in fork: $!";
? ?? ???exit 1;
? ? }
? ? if ($pid == 0) {
? ?? ???## == child proc ==
? ?? ???print "Child $i : My pid = $$n";
? ?? ???sleep(5);
? ?? ???print "Child $i : endn";
? ?? ???exit 0;
? ? sleep(1);
? ???这个例子就是,父进程执行一个循环,每次循环都fork一个子进程,子进程执行完以后退出,每次循环都等待1s,循环10次。
Child 0 : My pid = 20499
Child 1 : My pid = 20500
Child 2 : My pid = 20501
Child 3 : My pid = 20502
Child 4 : My pid = 20503
Child 0 : end
Child 5 : My pid = 20506
Child 1 : end
Child 6 : My pid = 20507
Child 2 : end
Child 7 : My pid = 20508
Child 3 : end
Child 8 : My pid = 20509
Child 4 : end
Child 9 : My pid = 20510
Child 5 : end
[root@localhost /tmp]
# Child 6 : end
Child 7 : end
Child 8 : end
Child 9 : end
? ???每个子进程耗时5s,那么执行完总共需要的是15s。
? ???但是,这样的代码会导致一个问题,在执行的过程中,可以在另外的tty上输入ps auxf来查看当前的进程状态,会发现类似这样的东东:
root? ???20531 0.0 0.0 8460 1704 pts/2? ? S+? ?21:46? ?0:00? ?? ?? ? _ perl test_proc_1.pl
root? ???20532 0.0 0.0? ???0? ? 0 pts/2? ? Z+? ?21:46? ?0:00? ?? ?? ?? ???_ [perl] <defunct>
root? ???20535 0.0 0.0? ???0? ? 0 pts/2? ? Z+? ?21:46? ?0:00? ?? ?? ?? ???_ [perl] <defunct>
root? ???20536 0.0 0.0? ???0? ? 0 pts/2? ? Z+? ?21:46? ?0:00? ?? ?? ?? ???_ [perl] <defunct>
root? ???20539 0.0 0.0? ???0? ? 0 pts/2? ? Z+? ?21:46? ?0:00? ?? ?? ?? ???_ [perl] <defunct>
root? ???20541 0.0 0.0 8460 720 pts/2? ? S+? ?21:46? ?0:00? ?? ?? ?? ???_ perl test_proc_1.pl
root? ???20543 0.0 0.0 8460 720 pts/2? ? S+? ?21:46? ?0:00? ?? ?? ?? ???_ perl test_proc_1.pl
root? ???20545 0.0 0.0 8460 720 pts/2? ? S+? ?21:46? ?0:00? ?? ?? ?? ???_ perl test_proc_1.pl
root? ???20546 0.0 0.0 8460 720 pts/2? ? S+? ?21:46? ?0:00? ?? ?? ?? ???_ perl test_proc_1.pl
root? ???20548 0.0 0.0 8460 720 pts/2? ? S+? ?21:46? ?0:00? ?? ?? ?? ???_ perl test_proc_1.pl
? ???有4个进程,状态为Z,意思就是僵尸进程,而正常的程序,是不应该出现僵尸进程的。
? ???正常情况下,子进程的退出需要做两件事情,第一,子进程exit,发出一个信号给自己的父进程,第二,父进程对子进程进行回收,如果父进程已经不存在了,那子进程会将init,也就是linux中第一个进程作为自己的父进程,init会代替它的父进程对子进程进行回收。
? ???我们的情况就是,子进程已经调用了exit,但是父进程并没有对它进行回收,如果父进程持续fork子进程,那僵尸进程就会越来越多,越来越多,最后会导致什么后果,我就不说了。
? ???父进程回收子进程的函数有两个:
? ???wait,和waitpid
? ???wait函数比较简单,没有任何参数,调用以后,父进程会停住,然后等待子进程返回。如果没有子进程,返回-1
? ???waitpid有两个参数,第一个参数为要等待的子进程的pid值,另外一个是flag,一般来讲,第一个参数为-1,意思就是等待所有的子进程。调用方法如下:
$procid = fork();
if ($procid == 0) {
# == child process ==
print ("this line is printed firstn");
exit(0);
# == parent process ==
waitpid ($procid,0);
print ("this line is printed lastn");
? ???其实,最主要的是让父进程知道,什么时候才需要去回收已经退出的子进程,因为父进程也是有很多活需要忙的。
? ???这个可以通过信号来实现,子进程在退出的时候,会向父进程发送一个信号,我们只要捕获了这个信号,就知道,有些子进程需要回收啦。例子如下:
# test_proc_2.pl
use POSIX ":sys_wait_h";
## == number of zombies proc ==
my $zombies = 0;
my $collect;
## == get the child signal ==
$SIG{CHLD} = sub { $zombies++ };
? ? ## == if need to collect zombies ==
? ? if ($zombies > 0) {
? ?? ???while (($collect = waitpid(-1,WNOHANG)) > 0) {
? ?? ?? ?? ?$zombies --;
? ?? ???}
? ???执行结果和原先一样:
Child 0 : My pid = 21552
Child 1 : My pid = 21553
Child 2 : My pid = 21554
Child 3 : My pid = 21555
Child 4 : My pid = 21556
Child 5 : My pid = 21558
Child 6 : My pid = 21570
Child 7 : My pid = 21572
Child 8 : My pid = 21574
Child 9 : My pid = 21575
? ???但是ps auxf的结果就有很大差别了:
root? ???21551 0.1 0.0 8280 2672 pts/2? ? S+? ?22:06? ?0:00? ?? ?? ? _ perl test_proc_2.pl
root? ???21558 0.0 0.0 8280 1168 pts/2? ? S+? ?22:07? ?0:00? ?? ?? ?? ???_ perl test_proc_2.pl
root? ???21570 0.0 0.0 8280 1168 pts/2? ? S+? ?22:07? ?0:00? ?? ?? ?? ???_ perl test_proc_2.pl
root? ???21572 0.0 0.0 8280 1168 pts/2? ? S+? ?22:07? ?0:00? ?? ?? ?? ???_ perl test_proc_2.pl
root? ???21574 0.0 0.0 8280 1168 pts/2? ? S+? ?22:07? ?0:00? ?? ?? ?? ???_ perl test_proc_2.pl
root? ???21575 0.0 0.0 8280 1168 pts/2? ? S+? ?22:07? ?0:00? ?? ?? ?? ???_ perl test_proc_2.pl
? ? 僵尸进程不会存在了。
? ???$SIG{CHLD} = sub { $zombies++ }; 这条语句,其实就是捕获了子进程退出的时候,向父进程发出的信号,捕获以后,就给一个变量($zombies)加1。
? ???如果"$zombies"不为0的时候,那就说明,有子进程退出了,需要进行回收,那父进程就调用waidpid函数,进行一次回收,每回收一个子进程,就给这个变量减去1,这样当"$zombies"减为0的时候,就说明所有的僵尸进程都已经回收了。bingo!
? ? 有的时候,我们只是执行一定量的任务,只管fork就可以了,但是某些时候,我们有太多任务需要执行,要一直持续的fork好多子进程,但是我们希望把子进程的数目控制在一个范围内,比如说,我一个任务,需要有100个子进程来执行,但是我不能100个进程全部fork出去,这样太占用资源了,所以我希望把进程数量控制在10个以内,当第一个进程退出以后,我再fork第11个进程,例子如下:
# test_proc_3.pl
## == number of proc ==
my $num_proc = 0;
## == number of collected ==
my $num_collect = 0;
$SIG{CHLD} = sub { $num_proc-- };
? ? $num_proc ++;
? ? if (($i-$num_proc-$num_collect) > 0) {
? ?? ?? ?? ?$num_collect ++;
? ? do {
? ?? ???sleep(1);
? ? } until ($num_proc < 3);
Child 0 : My pid = 22641
Child 1 : My pid = 22642
Child 2 : My pid = 22643
Child 3 : My pid = 22645
Child 4 : My pid = 22647
Child 5 : My pid = 22658
Child 6 : My pid = 22660
Child 7 : My pid = 22661
Child 8 : My pid = 22663
Child 6 : end
Child 9 : My pid = 22664
# Child 8 : end
? ???同时,看到的ps auxf的输出如下:
root? ???22640 0.0 0.0 8116 2672 pts/2? ? S+? ?22:28? ?0:00? ?? ?? ? _ perl test_proc_3.pl
root? ???22660 0.0 0.0? ???0? ? 0 pts/2? ? Z+? ?22:29? ?0:00? ?? ?? ?? ???_ [perl] <defunct>
root? ???22661 0.0 0.0 8116 1168 pts/2? ? S+? ?22:29? ?0:00? ?? ?? ?? ???_ perl test_proc_3.pl
root? ???22663 0.0 0.0 8116 1168 pts/2? ? S+? ?22:29? ?0:00? ?? ?? ?? ???_ perl test_proc_3.pl
root? ???22664 0.0 0.0 8116 1168 pts/2? ? S+? ?22:29? ?0:00? ?? ?? ?? ???_ perl test_proc_3.pl
? ???第一个子进程需要5s才能退出,如果1s执行一次fork的话,那么同时应该有5个子进程,但是本例中只有三个,那就是说实现了对进程数量的控制。
? ???本例中定义了几个变量:
? ???$num_proc:正在活动的进程数量,控制在3个以内,所以在父进程每次fork完子进程后,都会检查这个变量,如果超出了3个,那就等一会。当父进程fork了新子进程的时候,这个数字会增加,当子进程退出以后,父进程捕获了信号,这个数字会减少。
? ???$num_collect:已回收的进程数量,每回收一个子进程,变量加一。
? ???$i:已经fork的进程数量。
? ???$num_proc和$num_collect的和应该是等于$i的,如果不等于了,那就说明,有子进程需要回收了。
? ???进程的控制还算简单吧?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读