免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 5604 | 回复: 12
打印 上一主题 下一主题

关于perl fork [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-03-19 09:27 |只看该作者 |倒序浏览
本帖最后由 picbhan 于 2013-03-19 09:28 编辑

最近测试perl fork函数能产生子进程的最大数目和子进程自动收割,结果发现貌似如果是fork失败了的话,就没法继续fork了,无论你是否在第一个失败的fork后把所有子进程都收割。也就是说,无论你还有没有子进程在运行,只要fork失败了,就无法恢复fork了,不知道有没有人想到办法解决这个问题?
代码贴出来,高手看看吧!求解
  1. #!/usr/bin/env perl
  2. use strict;
  3. use warnings;

  4. use POSIX ":sys_wait_h"; # for nonblocking read
  5. my %children;
  6. my $count = 0;

  7. my $num = 0;
  8. while ($num++ < 100) { # win7最多fork 64个
  9.     my $pid = fork();
  10.     until (defined $pid) {
  11.         die "Can't fork. count is $count.\n" if $count == 0;
  12.         for (keys %children) {
  13.             print "Reaping $_ now, count is $count\n";
  14.             waitpid(0, $_);
  15.             delete $children{$_};
  16.             $count--;
  17.         }
  18.         print "All reaped.\n";
  19.         $pid = fork();
  20.     }
  21.     $children{$pid} = 1;
  22.    
  23.     if ($pid == 0) {
  24.         sleep 1;
  25.         exit 0;
  26.     }
  27.     $children{$pid}=1;
  28.     $count++;
  29.     print("pid is $pid, num is $num\n");
  30. }
复制代码

论坛徽章:
46
15-16赛季CBA联赛之四川
日期:2018-03-27 11:59:132015年亚洲杯之沙特阿拉伯
日期:2015-04-11 17:31:45天蝎座
日期:2015-03-25 16:56:49双鱼座
日期:2015-03-25 16:56:30摩羯座
日期:2015-03-25 16:56:09巳蛇
日期:2015-03-25 16:55:30卯兔
日期:2015-03-25 16:54:29子鼠
日期:2015-03-25 16:53:59申猴
日期:2015-03-25 16:53:29寅虎
日期:2015-03-25 16:52:29羊年新春福章
日期:2015-03-25 16:51:212015亚冠之布里斯班狮吼
日期:2015-07-13 10:44:56
2 [报告]
发表于 2013-03-19 09:52 |只看该作者
waitpid 用错了

论坛徽章:
0
3 [报告]
发表于 2013-03-19 10:12 |只看该作者
回复 2# zhlong8


    其实我最开始用的是my $pid = waitpid(-1, WNOHANG);但是好像也不对。能否解释清楚一点呢?

论坛徽章:
46
15-16赛季CBA联赛之四川
日期:2018-03-27 11:59:132015年亚洲杯之沙特阿拉伯
日期:2015-04-11 17:31:45天蝎座
日期:2015-03-25 16:56:49双鱼座
日期:2015-03-25 16:56:30摩羯座
日期:2015-03-25 16:56:09巳蛇
日期:2015-03-25 16:55:30卯兔
日期:2015-03-25 16:54:29子鼠
日期:2015-03-25 16:53:59申猴
日期:2015-03-25 16:53:29寅虎
日期:2015-03-25 16:52:29羊年新春福章
日期:2015-03-25 16:51:212015亚冠之布里斯班狮吼
日期:2015-07-13 10:44:56
4 [报告]
发表于 2013-03-19 10:17 |只看该作者
回复 3# picbhan


    你这里应该用 waitpid($_, 0) PID 是第一个参数

论坛徽章:
0
5 [报告]
发表于 2013-03-19 10:27 |只看该作者
回复 4# zhlong8


    waitpid(0, $_)对于64以下的数据能够正常运行,waitpid($_, 0)能够在64以后继续fork,貌似perldoc官网上对waitpid解释太少了。
另外,如果我想用waitpid(-1, WHOHANG)这种形式该怎么改呢?因为我不确定waitpid(0, $_)是否会等待当前$_进程执行完才能收割下一个,waitpid(-1, WHOHANG)貌似不阻断。

论坛徽章:
46
15-16赛季CBA联赛之四川
日期:2018-03-27 11:59:132015年亚洲杯之沙特阿拉伯
日期:2015-04-11 17:31:45天蝎座
日期:2015-03-25 16:56:49双鱼座
日期:2015-03-25 16:56:30摩羯座
日期:2015-03-25 16:56:09巳蛇
日期:2015-03-25 16:55:30卯兔
日期:2015-03-25 16:54:29子鼠
日期:2015-03-25 16:53:59申猴
日期:2015-03-25 16:53:29寅虎
日期:2015-03-25 16:52:29羊年新春福章
日期:2015-03-25 16:51:212015亚冠之布里斯班狮吼
日期:2015-07-13 10:44:56
6 [报告]
发表于 2013-03-19 10:45 |只看该作者
回复 5# picbhan


    直接搜 waitpid 就行。另外 64 个的限制是存在的要改估计要重新编译才行,你先把它当定死的吧。

论坛徽章:
0
7 [报告]
发表于 2013-03-19 11:59 |只看该作者
回复 6# zhlong8


    我又搜了一遍perldoc.org,关于waitpid的介绍确实不够详细,不过看了unix的waitpid倒是挺好理解的,虽然参数有差别。刚刚测试发现my $pid = waitpid($_, 0)是阻塞的,但是我想要非阻塞的收割方式,因为我只需限制子进程的最大数目不超过一个定值,当超过后就收割任何僵死进程,因此这种阻塞的方式不太合适。而我下面的代码对于fork失败后貌似就无法收割已产生的子进程,但是对于未失败的fork能正常运行。我觉得原因还是在于fork失败后waitpid(-1, WNOHANG)不能正常运行,这种情况下有解决办法没?
  1. #!/usr/bin/env perl
  2. use strict;
  3. use warnings;

  4. use POSIX ":sys_wait_h"; # for nonblocking read
  5. my %children;
  6. my $count = 0;

  7. my $num = 0;
  8. while ($num++ < 70) {
  9.     my $pid = fork();
  10.     until (defined $pid) {
  11.         die "Can't fork. count is $count\n" if $count == 0;
  12.         unless (_reap_subprocess(\%children, \$count)) {
  13.             die "Can't fork subprocess even when no " .
  14.                 "subprocess is running.\n";
  15.         }
  16.         $pid = fork();
  17.     }
  18.     $children{$pid} = 1;
  19.    
  20.     if ($pid == 0) {
  21.         sleep 5;
  22.         exit 0;
  23.     }
  24.     $count++;
  25.     print("pid is $pid, num is $num\n");
  26.     _reap_subprocess(\%children, \$count) if $count > 63; # 62 works here
  27. }

  28. _reap_subprocess(\%children, \$count) while ($count);

  29. sub _reap_subprocess {
  30.     my ($child_ref, $count_ref) = @_;
  31.     return 0 unless $count_ref;
  32.     my %child = %$child_ref;
  33.    
  34.     my $reaped;
  35.     while (1) {
  36.         # last if reaped one valid subprocess
  37.         my $pid = waitpid(-1, WNOHANG);
  38.         if ($pid == -1 or $pid == 0) {
  39.             last if $reaped;
  40.             print "No HANG, wait 5, count is $count_ref.\n";
  41.             sleep 5;
  42.         }
  43.         elsif (defined $child{$pid}) {
  44.             print "pid is $pid, count is $count_ref\n";
  45.             delete $child_ref->{$pid};
  46.             $count_ref--;
  47.             $reaped = 1;
  48.         }
  49.     }
  50.    
  51.     return 1;
  52. }
复制代码

论坛徽章:
0
8 [报告]
发表于 2013-03-19 12:44 |只看该作者
我把这个程序放在linux系统上运行的时候是正常的,因为linux能fork更多子进程,所以修改了下一些数值和输出,代码如下:
  1. #!/usr/bin/env perl
  2. use strict;
  3. use warnings;

  4. use POSIX ":sys_wait_h"; # for nonblocking read
  5. my %children;
  6. my $count = 0;
  7. my $max = shift @ARGV || 100000;
  8. my $num = 0;
  9. while ($num++ < $max) {
  10.     my $pid = fork();
  11.     until (defined $pid) {
  12.         print "Failed to fork new subprocess, num is $num, try to reap zombies now.\n";
  13.         die "Can't fork. count is $count\n" if $count == 0;
  14.         unless (_reap_subprocess(\%children, \$count)) {
  15.             die "Can't fork subprocess even when no " .
  16.                 "subprocess is running.\n";
  17.         }
  18.         print "Zombies reaped, try to fork again.\n";
  19.         $pid = fork();
  20.     }
  21.     $children{$pid} = 1;
  22.    
  23.     if ($pid == 0) {
  24.         sleep 5;
  25.         exit 0;
  26.     }
  27.     $count++;
  28.     #print("pid is $pid, num is $num\n");
  29.     #_reap_subprocess(\%children, \$count) if $count > 63; # 62 works here
  30. }

  31. _reap_subprocess(\%children, \$count) while ($count);

  32. sub _reap_subprocess {
  33.     my ($child_ref, $count_ref) = @_;
  34.     return 0 unless $count_ref;
  35.     my %child = %$child_ref;
  36.    
  37.     my $reaped;
  38.     while (1) {
  39.         # last if reaped one valid subprocess
  40.         my $pid = waitpid(-1, WNOHANG);
  41.         if ($pid == -1 or $pid == 0) {
  42.             last if $reaped;
  43.             print "No HANG, wait 5, count is $count_ref, pid is $pid.\n";
  44.             sleep 5;
  45.         }
  46.         elsif (defined $child{$pid}) {
  47.             # print "pid is $pid, count is $count_ref\n";
  48.             delete $child_ref->{$pid};
  49.             $count_ref--;
  50.             $reaped = 1;
  51.         }
  52.     }
  53.    
  54.     return 1;
  55. }
复制代码
代码输出如下:
Failed to fork new subprocess, num is 31822, try to reap zombies.
Zombies reaped, try to fork again.
Failed to fork new subprocess, num is 54765, try to reap zombies.
Zombies reaped, try to fork again.
Failed to fork new subprocess, num is 54765, try to reap zombies.
No HANG, wait 5, count is 12656, pid is 0.
Zombies reaped, try to fork again.
Failed to fork new subprocess, num is 67421, try to reap zombies.
Zombies reaped, try to fork again.
Failed to fork new subprocess, num is 67421, try to reap zombies.
No HANG, wait 5, count is 12656, pid is 0.
Zombies reaped, try to fork again.
Failed to fork new subprocess, num is 80081, try to reap zombies.
Zombies reaped, try to fork again.
Failed to fork new subprocess, num is 80081, try to reap zombies.
No HANG, wait 5, count is 12656, pid is 0.
Zombies reaped, try to fork again.
Failed to fork new subprocess, num is 92737, try to reap zombies.
Zombies reaped, try to fork again.
Failed to fork new subprocess, num is 92737, try to reap zombies.
No HANG, wait 5, count is 12656, pid is 0.
Zombies reaped, try to fork again.
No HANG, wait 5, count is 7264, pid is 0.
No HANG, wait 5, count is 788, pid is 0.
整个代码正常结束。
因此,我觉得可能是win7系统中perl自己仿真的fork和waitpid与linux系统调用的有所差别,才导致在fork失败后waitpid(-1, WNOHANG)不能正常收割子进程。

论坛徽章:
46
15-16赛季CBA联赛之四川
日期:2018-03-27 11:59:132015年亚洲杯之沙特阿拉伯
日期:2015-04-11 17:31:45天蝎座
日期:2015-03-25 16:56:49双鱼座
日期:2015-03-25 16:56:30摩羯座
日期:2015-03-25 16:56:09巳蛇
日期:2015-03-25 16:55:30卯兔
日期:2015-03-25 16:54:29子鼠
日期:2015-03-25 16:53:59申猴
日期:2015-03-25 16:53:29寅虎
日期:2015-03-25 16:52:29羊年新春福章
日期:2015-03-25 16:51:212015亚冠之布里斯班狮吼
日期:2015-07-13 10:44:56
9 [报告]
发表于 2013-03-19 12:59 |只看该作者
回复 8# picbhan


    估计不支持第一个参数为 -1 吧,对某一个 fork 出来的“进程”是支持非阻塞模式的。

论坛徽章:
0
10 [报告]
发表于 2013-03-19 13:30 |只看该作者
回复 9# zhlong8


    我觉得应该不是不支持第一个参数为-1,要不然当fork没失败之前也不会运行成功了,比如说
  1. _reap_subprocess(\%children, \$count) if $count > 62; # 62 works here
复制代码
在fork失败之前就收割完僵死进程在win7上也能够正常运行。而收割方法是一样的$pid = waitpid(-1, WNOHANG);
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP