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. [SharePoint 2010]关于基于声明(Claims)的用户认证模式

    转:http://blog.csdn.net/zw_2011/article/details/7417132 SharePoint 2010在用户认证模式上,较之以前的版本有了非常大的改变.在Shar ...

  2. C 的 coroutine 库 via 云风的 BLOG

    今天实现了一个 C 用的 coroutine 库. 我相信这个东西已经被无数 C 程序员实现过了, 但是通过 google 找了许多, 或是接口不让我满意, 或是过于重量. 在 Windows 下, ...

  3. 《Python基础教程(第二版)》学习笔记 -> 第八章 异常

    什么是异常 Python用 异常对象(exception object)来表示异常情况.遇到错误后,会引发异常,如果异常对象并未被处理或者捕捉,程序就会用所谓的回溯(Traceback,一种错误信息) ...

  4. Linux Systemd——在RHEL/CentOS 7中启动/停止/重启服务

    RHEL/CentOS 7.0中一个最主要的改变,就是切换到了systemd.它用于替代红帽企业版Linux前任版本中的SysV和Upstart,对系统和服务进行管理.systemd兼容SysV和Li ...

  5. STM32F407 外扩SRAM

    字节控制功能.支持高/低字节控制. 看看实现 IS62WV51216 的访问,需要对 FSMC进行哪些配置. 这里就做一个概括性的讲解.步骤如下: 1)使能 FSMC 时钟,并配置 FSMC 相关的  ...

  6. 用Vmware安装centos5

    Vmware安装过程就不详述了,这里从创建虚拟机开始记录. 选择创建虚拟机 下一步 选择稍后安装 选择安装的操作系统版本,需要说明的是,CentOs 5 就是RHEL 5 设置虚拟机名称及虚拟机位置 ...

  7. 通过gdb调试分析Linux内核的启动过程

    作者:吴乐 山东师范大学 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验流程 1.打开环境 执 ...

  8. AtCoder Grand Contest 001

    B - Mysterious Light 题意:从一个正三角形边上一点出发,遇到边和已走过的边则反弹,问最终路径长度 思路:GCD 数据爆long long #pragma comment(linke ...

  9. 优雅地对泛型List 进行深拷贝

    public class People { public string Name; public int Age; public People(string name, int age) { this ...

  10. 详解 jupyter notebook 集成 spark 环境安装

    来自: 代码大湿 代码大湿 1 相关介绍 jupyter notebook是一个Web应用程序,允许你创建和分享,包含活的代码,方程的文件,可视化和解释性文字.用途包括:数据的清洗和转换.数值模拟.统 ...