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 = $$\n";
  16.     sleep(5);
  17.     print "Child:
    end\n";
  18.     exit 0;
  19. } else {
  20.     ## == parent proc ==
  21.     print "Parent
    My pid = $$, and my child's pid = $pid\n";
  22.     sleep(5);
  23.     print "Parent:
    end\n";
  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 = $$\n";
  18.         sleep(5);
  19.         print "Child
    $i : end\n";
  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\n");
  5. exit(0);
  6. } else {
  7. # == parent process ==
  8. waitpid ($procid, 0);
  9. print ("this
    line is printed last\n");
  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 = $$\n";
  24.         sleep(5);
  25.         print "Child
    $i : end\n";
  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 = $$\n";
  25.         sleep(5);
  26.         print "Child
    $i : end\n";
  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的,假设不等于了,那就说明,有子进程须要回收了。

Perl多进程的更多相关文章

  1. Perl系列文章

    0.Perl书籍推荐 Perl书籍下载 密码:kkqx 下面是一些我学习Perl过程中读过完整的或部分章节的觉得好的书. 入门级别1:<Perl语言入门>即小骆驼 入门级别2:<In ...

  2. Perl的多进程框架(watcher-worker)

    关于perl的多进程,大家可能马上会想到Parallel::ForkManager这个模块.但是今天我们试着自己动手写一个类似的框架:) 该多进程开发模型从开源服务器框架Lighttpd发展而来,核心 ...

  3. 使用 GDB 调试多进程程序

    使用 GDB 调试多进程程序 GDB 是 linux 系统上常用的调试工具,本文介绍了使用 GDB 调试多进程程序的几种方法,并对各种方法进行比较. 3 评论 田 强 (tianq@cn.ibm.co ...

  4. GDB多进程调试(转)

    http://www.cnblogs.com/ggjucheng/archive/2011/12/15/2288710.html GDB 是 linux 系统上常用的 c/c++ 调试工具,功能十分强 ...

  5. Perl多线程(1):解释器线程的特性

    线程简介 线程(thread)是轻量级进程,和进程一样,都能独立.并行运行,也由父线程创建,并由父线程所拥有,线程也有线程ID作为线程的唯一标识符,也需要等待线程执行完毕后收集它们的退出状态(比如使用 ...

  6. Perl IO:文件锁

    文件锁 当多个进程或多个程序都想要修同一个文件的时候,如果不加控制,多进程或多程序将可能导致文件更新的丢失. 例如进程1和进程2都要写入数据到a.txt中,进程1获取到了文件句柄,进程2也获取到了文件 ...

  7. Perl IO:随机读写文件

    随机读写 如果一个文件句柄是指向一个实体文件的,那么就可以对它进行随机数据的访问(包括随机读.写),随机访问表示可以读取文件中的任何一部分数据或者向文件中的任何一个位置处写入数据.实现这种随机读写的功 ...

  8. Perl爬虫的简单实现

    由于工作中有个项目需要爬取第三方网站的内容,所以在Linux下使用Perl写了个简单的爬虫. 相关工具 1. HttpWatch/浏览器开发人员工具 一般情况下这个工具是用不到的,但是如果你发现要爬取 ...

  9. Python多线程、多进程和协程的实例讲解

    线程.进程和协程是什么 线程.进程和协程的详细概念解释和原理剖析不是本文的重点,本文重点讲述在Python中怎样实际使用这三种东西 参考: 进程.线程.协程之概念理解 进程(Process)是计算机中 ...

随机推荐

  1. window.history

    作者:zccst 旧版: forword() backword() go(number) HTML5中新增了 onhashchange  浏览器兼容性较好,用得较多 pushState / repla ...

  2. 修改Oracle 表空间名称 tablespace name

    修改表空间名称步骤如下: 1. 使用oracle用户登录执行 $sqlplus / as sysdba 2. 执行修改表空间命令如下 SQL> alter tablespace  TEST re ...

  3. 38、FragmentStatePagerAdapter分页

    [ ViewPager ] ViewPager 如其名所述,是负责翻页的一个 View.准确说是一个 ViewGroup,包含多个 View 页,在手指横向滑动屏幕时,其负责对 View 进行切换.为 ...

  4. IOS init initWith 等相关集中

    1.initWithCoder    当一个view从nib初始化的时候,会调用这个函数.  用keyedArchiver序列化一个类的实力,后面用keyedUnArchiver拿回来的时候会调用到 ...

  5. Selenium2Library系列 keywords 之 _SelectElementKeywords 之 get_selected_list_label(self, locator)

    def get_selected_list_label(self, locator): """Returns the visible label of the selec ...

  6. Eclipse编辑java文件报Unhandled event loop exception错误的解决办法

    原因:电脑中安装了杀毒软件,卸掉或者关掉就可以了.我的是直接退出,错误就不产生了.

  7. Java内部类this$0字段产生的一个bug

    首先查看下面一段代码,我指出了问题代码的所在,读者先自己思考一下这段代码会有什么问题. 这是用clone方法完整拷贝一个二项堆(BinomialHeap)结构的代码.二项堆中包含一个内部类Binomi ...

  8. 百家搜索:在网站中添加Google、百度等搜索引擎

    来源:http://www.ido321.com/1143.html 看到一些网站上添加了各种搜索引擎.如Google.百度.360.有道等,就有点好奇,这个怎么实现?研究了一各个搜索引擎怎么传送关键 ...

  9. VSim [a Racing-simulator by Vell001]

    VSim [a racing-simulator by vell001] This is my first project about Racing. I am a Chinese with bad ...

  10. 把JSON数据载入到页面表单的两种思路(对easyui自带方法进行改进)

    #把JSON数据载入到页面表单的两种思路(对easyui自带方法进行改进) ##背景 项目中经常需要把JSON数据填充到页面表单,一开始我使用easyui自带的form load方法,觉得效率很低,经 ...