- fork
这个函数通过调用 fork(2) 系统调用,从一个进程中创建两个进程。如果它成功,该函数给父进程返回新创建的子进程 ID,而给子进程返回 0。如果系统没有足够的资源分配一个新的进程,那么调用失败并返回 undef。文件描述符(以及有时候还有在那些描述符上的锁)是共享的,而所有其他的东西都是拷贝的——或者至少看起来是那样的。
在早于 5.6 版本的 Perl 里,未冲刷的缓冲区在两个进程里都是没有冲刷的,这就意味着你需要在程序的早些时候在一个或多个文件句柄上设置 $| 以避免输出重复。
一个产生子进程然而有检查“cannot fork”错误的近乎没有毛病的方法是:
use Errno qw(EAGAIN); FORK: { if ($pid = fork) { # 父进程在此 # 在 $pid 里可以看到子进程的进程 id } elsif (defined $pid) { # 如果定义了,$pid 在这里是 0 # 子进程在此 # 你可以用 getppid 在这里获取父进程的 pid } elsif ($! == EAGAIN) { # EAGAIN 是认为可以恢复的 fork 错误 sleep 5; redo FORK; } else { # 奇怪的 fork 错误 die "Can't fork: $!\n"; } }
这些预防措施在那些做隐含的 fork(2) 的操作上是不必要的,比如 system,反勾号,或者把一个进程当作一个文件句柄打开,因为 Perl 在为你做 fork 的时候碰到临时的失败会自动重新尝试 fork。要注意记得使用 exit 结束子进程的代码,否则子进程会不小心地离开条件块并且开始执行原来只是想让父进程执行的代码。
如果你 fork 以后再也不等待你的子进程,那么你就会积累僵死进程(那些父进程还没等待它们的退出进程)。在一些系统上,你可以通过设置 $SIG{CHLD} 为“IGNORE”来避免这些;在大多数系统上,你必须 wait 你的垂死的子进程。参阅 wait 函数获做这些的例子,或则后参阅第十六章的“信号”一节获取更多关于 SIGCHLD 的信息。
如果一个派生出来的子进程继承了系统文件描述符,象 STDIN 和 STDOUT 等,它们又和一个远程的管道或者套接字连接,那么你可能不得不在子进程里把他们重新打开到 /dev/null。这是因为即使父进程退出,子进程仍将带着这些文件句柄的拷贝继续生存。而远端服务器(比如说,一个 CGI 脚本或者一个从远程 shell 发起的后台任务。)就会挂起,因为它仍然等待所有拷贝关闭。重新把系统文件句柄打开成别的什么东西可以修补这个问题。
在大多数支持 fork(2) 的系统上,人们做了大量努力把它变得尽可能地高效(比如,数据页的写时拷贝(copy-on-write)技术),而它也成了过去几十年来多任务领域的典范。但是 fork 函数可能并没有有效地(甚至可能是根本没有)在那些不象 Unix 的系统上实现。比如, Perl 5.6 甚至在 Microsoft 系统上都模拟了一个合适的 fork,但是并不能保证可以达到很好的性能。可能用 Win32::Process 模块的时候,你的运气会好一些。