Linux / Perl - 当进程被分叉时会发生什么?

som*_*ody 5 linux perl forking posix

我已经阅读了有关 fork 的内容,据我所知,该进程已被克隆,但是哪个进程?脚本本身还是启动脚本的进程?

例如:

我在我的机器上运行 rTorrent,当一个 Torrent 完成时,我有一个针对它运行的脚本。此脚本从 Web 获取数据,因此需要几秒钟才能完成。在此期间,我的 rtorrent 进程被冻结。所以我使用以下内容制作了脚本分支

my $pid = fork();
if ($pid) == 0) { blah blah blah; exit 0; }
Run Code Online (Sandbox Code Playgroud)

如果我从 CLI 运行这个脚本,它会在一秒钟内返回到 shell,同时它在后台运行,完全符合我的预期。然而,当我从 rTorrent 运行它时,它似乎比以前更慢。那么究竟是什么被分叉了呢?rtorrent 进程是否克隆了自己并且我的脚本在其中运行,还是我的脚本克隆了自己?我希望这是有道理的。

小智 3

摘自W. Richard Stevens 的《UNIX 环境中的高级编程》(第 188 页):

8.3fork功能

Unix 内核创建新进程的唯一方式是现有进程调用该函数fork。(这不适用于我们在上一节中提到的特殊进程 - 交换器、init和 pagedaemon。这些进程是由内核在引导过程中专门创建的。)

#include <sys/types.h>
#include <unistd.h>

pid_t fork(void);
/* Returns: 0 in child, process ID of child in parent, -1 on error */
Run Code Online (Sandbox Code Playgroud)

创建的新进程fork称为子进程。该函数被调用一次但返回两次。返回值的唯一区别是子进程中的返回值为 0,而父进程中的返回值是新子进程的进程 ID。子进程ID返回给父进程的原因是因为一个进程可以有多个子进程,所以没有函数允许进程获取其子进程的进程ID。之所以fork向子进程返回0是因为一个进程只能有一个父进程,所以子进程总是可以调用getppid来获取其父进程的进程ID。(进程 ID 0 始终由交换器使用,因此 0 不可能是子进程的进程 ID。)

子级和父级都继续执行调用后的指令fork。孩子是父母的复制品。例如,子进程获取父进程的数据空间、堆和堆栈的副本。请注意,这是子级的副本 - 父级和子级不共享这些内存部分。如果文本段是只读的,通常父母和孩子会共享文本段(第 7.6 节)。

在 Linux 上,Perl 的fork运算符调用系统的运算符fork,并在失败时返回undef而不是 -1。

Stevens 列出了继承属性以及父进程与其分叉子进程之间的差异的列表(第 192 页):

除了打开的文件之外,子级还继承了父级的许多其他属性:

  • 真实用户ID、真实组ID、有效用户ID、有效组ID
  • 补充组 ID
  • 进程组ID
  • 会话ID
  • 控制终端
  • 设置用户 ID 标志和设置组 ID 标志
  • 当前工作目录
  • 根目录
  • 文件模式创建掩码
  • 信号掩码和处置
  • 任何打开的文件描述符的 close-on-exec 标志
  • 环境
  • 附加共享内存段
  • 资源限制

父母和孩子的区别在于

  • 返回值来自fork
  • 进程ID不同
  • 两个进程有不同的父进程ID——子进程的父进程ID是父进程;父进程的父进程ID不变
  • 子项tms_utimetms_stimetms_cutime和的值tms_ustime设置为 0
  • 父级设置的文件锁不会被子级继承
  • 为孩子清除待处理的警报
  • 子级的待处理信号集设置为空集