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: end\n";
    exit 0;
} else {
## == parent proc ==
    print "Parent My pid = $$, and my child's pid = $pid\n";
    sleep(5);
    print "Parent: end\n";
}
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值。
这只是一个最简单的例子,一个父进程派生一个子进程,再稍微复杂一点,一个父进程派生多个子进程,代码如下:
#!/usr/bin/perl
# test_proc_1.pl
# test multi process
# create by lianming: 2009-08-12
use strict;
use warnings;

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 : end\n";
        exit 0;
    }
    sleep(1);
}
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,意思就是等待所有的子进程。调用方法如下:
$procid = fork();
if ($procid == 0) {
  # == child process ==
  print ("this line is printed first\n");
  exit(0);
} else {
  # == parent process ==
  waitpid ($procid, 0);
  print ("this line is printed last\n");
}
其实,最主要的是让父进程知道,什么时候才需要去回收已经退出的子进程,因为父进程也是有很多活需要忙的。
这个可以通过信号来实现,子进程在退出的时候,会向父进程发送一个信号,我们只要捕获了这个信号,就知道,有些子进程需要回收啦。例子如下:
#!/usr/bin/perl
# test_proc_2.pl
# test multi process
# create by lianming: 2009-08-12
use strict;
use warnings;
use POSIX ":sys_wait_h";
## == number of zombies proc ==
my $zombies = 0;
my $collect;
## == get the child signal ==
$SIG{CHLD} = sub { $zombies++ };

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 : end\n";
        exit 0;
    }
## == if need to collect zombies ==
    if ($zombies > 0) {
        while (($collect = waitpid(-1, WNOHANG)) > 0) {
            $zombies --;
        }
    }
    sleep(1);
}
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个进程,例子如下:
#!/usr/bin/perl
# test_proc_3.pl
# test multi process
# create by lianming: 2009-08-12
use strict;
use warnings;
use POSIX ":sys_wait_h";
## == number of proc ==
my $num_proc = 0;
## == number of collected ==
my $num_collect = 0;
my $collect;
## == get the child signal ==
$SIG{CHLD} = sub { $num_proc-- };
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 : end\n";
        exit 0;
    }
$num_proc ++;
## == if need to collect zombies ==
    if (($i-$num_proc-$num_collect) > 0) {
        while (($collect = waitpid(-1, WNOHANG)) > 0) {
            $num_collect ++;
        }
    }
    do {
        sleep(1);
    } until ($num_proc < 3);
}
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的,如果不等于了,那就说明,有子进程需要回收了。
进程的控制还算简单吧?
有的时候,进程和进程之间是需要通讯的,进程不像线程,整个内存空间都是共享的,但是linux也提供了多种进程之间通信的方式,最简单的方式,就是存在文件里,下一篇文档就讨论一种文件存储的方法。

perl多进程实战之一相关推荐

  1. perl脚本实战总结

    # 获取环境变量的值 my $java_home = $ENV{"JAVA_HOME"}; 字符串 截取字符串 # 截取字符串,从索引为3的位置(含)开始,截取长度为3 my su ...

  2. python多进程实战——4k图片抓取

    技术交流贴:不可用于违法违规用途 成果图: 背景:疫情宅家太无聊了,练习了一下爬虫,里面用到了伪装请求头.多进程.代理IP等技术 开始: 首先我们来分析一下网页,只要找到我们需要的清析度的图片真实网址 ...

  3. python3多线程实战(python3经典编程案例)

    python3多进程实战(python3经典编程案例) python3多线程实战(python3经典编程案例) python3 协程实战(python3经典编程案例) 总结: python多线程适用在 ...

  4. Cpython解释器下实现并发编程——多进程、多线程、协程、IO模型

    一.背景知识 进程即正在执行的一个过程.进程是对正在运行的程序的一个抽象. 进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一.操作系统的其他所有内容都 ...

  5. 架构 php_十年PHP架构师的成长之路,程序员必备

    不知不觉自己做开发已经十年了,这十年中我获得了技术能力.CTO.大公司的经历.但再仔细一想,这十年中我至少浪费了五年时间走了很多弯路,这五年可以足够让自己成长为一个优秀的程序员,我用这五年时间和很多程 ...

  6. python logger handler_Python中的logger和handler到底是个什么鬼

    最近的任务经常涉及到日志的记录,特意去又学了一遍logging的记录方法.跟java一样,python的日志记录也是比较繁琐的一件事,在写一条记录之前,要写好多东西.典型的日志记录的步骤是这样的: 创 ...

  7. 服务器端PHP多进程编程实战

    服务器端PHP多进程编程实战 http://developer.51cto.com  2010-10-15 08:57  Bruce Dou  博客  我要评论(0) PHP是目前应用最广泛的Web开 ...

  8. python flask实战订餐系统微信小程序-60nginx + uwsgi 实现多进程访问

    python flask实战订餐系统微信小程序-60nginx + uwsgi 实现多进程访问 B站配套视频教程观看 uwsgi多进程配置 uwsgi.ini配置 nginx和uwsgi通过配置文件s ...

  9. 实战Perl脚本测试

    实战Perl脚本测试(转) 这是一篇关于perl脚本测试的总结性文章,其中提到了很多实用的模块,如果文中介绍的不够详细,请到cpan上搜索该模块并查阅其文档.  1基本语法检查 Perl语言的哲学是& ...

最新文章

  1. 哈佛牙学院博士后:教你口腔保健基本功之牙线篇
  2. 深度学习在自然语言处理研究上的进展
  3. 命令行工具开发:如何快速实现命令行提示?
  4. 力扣--- 滑动谜题
  5. mac android 投屏幕,将android/ios屏幕投射到windows/mac的良好参考
  6. [转]bookmark整理之.NET编程相关
  7. mysql 队列表设计_mysql主从同步操作,及队列设计
  8. linux 下的带宽、延时、吞吐率、PPS
  9. 【通信】基于matlab GUI循环码编译码器【含Matlab源码 692期】
  10. Qt之SQLite数据库可视化工具
  11. 如何把直播嵌入微信公众账号
  12. opencv学习笔记---如何看懂照片的直方图
  13. 简单的爬取某租房网站租房信息并存入MySQL数据库
  14. 教你使用Python爬虫获取电子书资源实战!喜欢学习的小伙伴过来看啦!
  15. 单片机语音模块JQ8900-16P的几种触发方式与源码配置
  16. 【生信】初探基因定位和全基因组关联分析
  17. Matlab TRL校准(简易版)
  18. 三层网络渗透测试实验
  19. Google研究总监Peter Norvig:人工智能的另一面
  20. 文正教务系统微信开发

热门文章

  1. 高仿快递100--实战之RadioGroup和RadioButton应用
  2. C Primer Plus 第13章 文件输入/输出 13.11 编程练习答案
  3. 20161120-安全测试
  4. Nginx服务器开启gzip压缩功能额必要性
  5. 如何找到 AWS 环境下应用程序中易于得手的漏洞?
  6. 移动互联网新协议 GTP 中被曝多个高危漏洞,影响4G和5G 用户
  7. 出于安全考虑,谷歌禁用三款 Linux web 浏览器登录其服务
  8. P2898 [USACO08JAN]haybale猜测Haybale Guessing
  9. SCOM2012SP1环境准备和安装
  10. Struts2标签库整理【完整】