zoukankan      html  css  js  c++  java
  • Perl多进程

    perl作为一种解释性的语言,非常受广大系统管理员的欢迎,优点么就不多说了,坏处也有不少,比如对线程的支持,就一直不咋地,所以大多数情况下,我们都须要多个进程,来帮助我们完毕工作,闲话少说,上代码。

    1. #!/usr/bin/perl
    2. # test_proc.pl
    3. # test multi process
    4. # create by lianming: 2009-08-12
    5. use strict;
    6. use warnings;
    7. ## == fork a new process ==
    8. my $pid = fork();
    9. if (!defined($pid)) {
    10.     print "Error in fork: $!";
    11.     exit 1;
    12. }
    13. if ($pid == 0) {
    14.     ## == child proc ==
    15.     print "Child: My pid = $$ ";
    16.     sleep(5);
    17.     print "Child: end ";
    18.     exit 0;
    19. } else {
    20.     ## == parent proc ==
    21.     print "Parent My pid = $$, and my child's pid = $pid ";
    22.     sleep(5);
    23.     print "Parent: end ";
    24. }
    25. 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值。
    这仅仅是一个最简单的样例,一个父进程派生一个子进程,再略微复杂一点,一个父进程派生多个子进程,代码例如以下:

    1. #!/usr/bin/perl
    2. # test_proc_1.pl
    3. # test multi process
    4. # create by lianming: 2009-08-12
    5. use strict;
    6. use warnings;
    7.  
    8. for (my $i = 0$i < 10$i ++) {
    9.     ## == fork a new process ==
    10.     my $pid = fork();
    11.     if (!defined($pid)) {
    12.         print "Error in fork: $!";
    13.         exit 1;
    14.     }
    15.     if ($pid == 0) {
    16.         ## == child proc ==
    17.         print "Child $i : My pid = $$ ";
    18.         sleep(5);
    19.         print "Child $i : end ";
    20.         exit 0;
    21.     }
    22.     sleep(1);
    23. }
    24. exit 0;

    这个样例就是,父进程运行一个循环,每次循环都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] 
    root 20535 0.0 0.0 0 0 pts/2 Z+ 21:46 0:00 \_ [perl] 
    root 20536 0.0 0.0 0 0 pts/2 Z+ 21:46 0:00 \_ [perl] 
    root 20539 0.0 0.0 0 0 pts/2 Z+ 21:46 0:00 \_ [perl] 
    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,意思就是等待全部的子进程。调用方法例如以下:

    1. $procid = fork();
    2. if ($procid == 0) {
    3. # == child process ==
    4. print ("this line is printed first ");
    5. exit(0);
    6. } else {
    7. # == parent process ==
    8. waitpid ($procid0);
    9. print ("this line is printed last ");
    10. }

    事实上,最基本的是让父进程知道,什么时候才须要去回收已经退出的子进程,由于父进程也是有非常多活须要忙的。
    这个能够通过信号来实现,子进程在退出的时候,会向父进程发送一个信号,我们仅仅要捕获了这个信号,就知道,有些子进程须要回收啦。样例例如以下:

    1. #!/usr/bin/perl
    2. # test_proc_2.pl
    3. # test multi process
    4. # create by lianming: 2009-08-12
    5. use strict;
    6. use warnings;
    7. use POSIX ":sys_wait_h";
    8. ## == number of zombies proc ==
    9. my $zombies = 0;
    10. my $collect;
    11. ## == get the child signal ==
    12. $SIG{CHLD} = sub { $zombies++ };
    13.  
    14. for (my $i = 0$i < 10$i ++) {
    15.     ## == fork a new process ==
    16.     my $pid = fork();
    17.     if (!defined($pid)) {
    18.         print "Error in fork: $!";
    19.         exit 1;
    20.     }
    21.     if ($pid == 0) {
    22.         ## == child proc ==
    23.         print "Child $i : My pid = $$ ";
    24.         sleep(5);
    25.         print "Child $i : end ";
    26.         exit 0;
    27.     }
    28.     ## == if need to collect zombies ==
    29.     if ($zombies > 0) {
    30.         while (($collect = waitpid(-1, WNOHANG)) > 0) {
    31.             $zombies --;
    32.         }
    33.     }
    34.     sleep(1);
    35. }
    36. exit 0;

    运行结果和原先一样:
    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 0 : end
    Child 5 : My pid = 21558
    Child 1 : end
    Child 6 : My pid = 21570
    Child 2 : end
    Child 7 : My pid = 21572
    Child 3 : end
    Child 8 : My pid = 21574
    Child 4 : end
    Child 9 : My pid = 21575
    Child 5 : end
    [root@localhost /tmp]
    # Child 6 : end
    Child 7 : end
    Child 8 : end
    Child 9 : end
    可是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个进程,样例例如以下:

    1. #!/usr/bin/perl
    2. # test_proc_3.pl
    3. # test multi process
    4. # create by lianming: 2009-08-12
    5. use strict;
    6. use warnings;
    7. use POSIX ":sys_wait_h";
    8. ## == number of proc ==
    9. my $num_proc = 0;
    10. ## == number of collected ==
    11. my $num_collect = 0;
    12. my $collect;
    13. ## == get the child signal ==
    14. $SIG{CHLD} = sub { $num_proc-- };
    15. for (my $i = 0$i < 10$i ++) {
    16.     ## == fork a new process ==
    17.     my $pid = fork();
    18.     if (!defined($pid)) {
    19.         print "Error in fork: $!";
    20.         exit 1;
    21.     }
    22.     if ($pid == 0) {
    23.         ## == child proc ==
    24.         print "Child $i : My pid = $$ ";
    25.         sleep(5);
    26.         print "Child $i : end ";
    27.         exit 0;
    28.     }
    29.     $num_proc ++;
    30.     ## == if need to collect zombies ==
    31.     if (($i-$num_proc-$num_collect) > 0) {
    32.         while (($collect = waitpid(-1, WNOHANG)) > 0) {
    33.             $num_collect ++;
    34.         }
    35.     }
    36.     do {
    37.         sleep(1);
    38.     } until ($num_proc < 3);
    39. }
    40. exit 0;

    运行结果例如以下:
    Child 0 : My pid = 22641
    Child 1 : My pid = 22642
    Child 2 : My pid = 22643
    Child 0 : end
    Child 3 : My pid = 22645
    Child 1 : end
    Child 4 : My pid = 22647
    Child 2 : end
    Child 5 : My pid = 22658
    Child 3 : end
    Child 6 : My pid = 22660
    Child 4 : end
    Child 7 : My pid = 22661
    Child 5 : end
    Child 8 : My pid = 22663
    Child 6 : end
    Child 9 : My pid = 22664
    Child 7 : end
    [root@localhost /tmp]
    # Child 8 : end
    Child 9 : 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] 
    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的,假设不等于了,那就说明,有子进程须要回收了。

  • 相关阅读:
    【转】Math.Atan2 方法
    【转】Excel快捷键大全
    Tricks(四十七)—— 布尔矩阵(0-1矩阵)取反
    Tricks(四十七)—— 布尔矩阵(0-1矩阵)取反
    tensorflow 函数接口的理解
    tensorflow 函数接口的理解
    TensorFlow 学习(十一)—— 正则(regularizer)
    TensorFlow 学习(十一)—— 正则(regularizer)
    TensorFlow 需注意的细节问题
    TensorFlow 需注意的细节问题
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/3824662.html
Copyright © 2011-2022 走看看