我读过有关 fork 的文章,据我所知,进程被克隆了,但是是哪个进程呢?脚本本身还是启动脚本的进程?
例如:
我在自己的机器上运行 rTorrent,当一个 torrent 完成时,我会针对它运行一个脚本。此脚本从网络获取数据,因此需要几秒钟才能完成。在此期间,我的 rtorrent 进程被冻结。因此,我使用以下命令创建了脚本分支
my $pid = fork();
if ($pid) == 0) { blah blah blah; exit 0; }
如果我从 CLI 运行此脚本,它会在后台运行,一秒钟内返回到 shell,正如我所期望的那样。但是,当我从 rTorrent 运行它时,它似乎比以前更慢了。那么到底分叉了什么?rtorrent 进程克隆了自身,我的脚本在其中运行,还是我的脚本克隆了自身?我希望这是有道理的。
答案1
从UNIX环境高级编程作者:W. Richard Stevens(第 188 页):
8.3
fork
功能这仅有的Unix 内核创建新进程的方式是现有进程调用该
fork
函数。(这不适用于我们在上一节中提到的特殊进程 — swapper、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 */
由此创建的新进程
fork
称为子进程。该函数被调用一次但返回两次。返回值的唯一区别是子进程的返回值为 0,而父进程的返回值为新子进程的进程 ID。将子进程的进程 ID 返回给父进程的原因是因为一个进程可以有多个子进程,因此没有函数允许进程获取其子进程的进程 ID。将fork
0 返回给子进程的原因是因为一个进程只能有一个父进程,因此子进程始终可以调用getppid
以获取其父进程的进程 ID。(进程 ID 0 始终由交换器使用,因此 0 不可能是子进程的进程 ID。)子进程和父进程继续执行调用 之后的指令
fork
。子进程是父进程的副本。例如,子进程获得父进程数据空间、堆和堆栈的副本。请注意,这是子进程的副本 — 父进程和子进程不共享这些内存部分。如果文本段是只读的,父进程和子进程通常共享该文本段(第 7.6 节)。
在 Linux 上,Perl 的fork
操作员呼叫系统的fork
并在失败时返回undef
而不是 -1。
史蒂文斯给出了列表(第 192 页),列出了父进程与其分叉子进程之间的继承属性和差异:
除了打开的文件之外,子进程还可以继承父进程的许多其他属性:
- 真实用户ID, 真实组ID, 有效用户ID, 有效组ID
- 补充组 ID
- 进程组ID
- 会话 ID
- 控制终端
- 设置用户 ID 标志和设置组 ID 标志
- 当前工作目录
- 根目录
- 文件模式创建掩码
- 信号屏蔽和配置
- 任何打开的文件描述符的 close-on-exec 标志
- 环境
- 连接的共享内存段
- 资源限制
父母与孩子之间的区别是
- 返回值来自
fork
- 进程 ID 不同
- 两个进程有不同的父进程 ID——子进程的父进程 ID 就是父进程;父进程的父进程 ID 不变
- 孩子的
tms_utime
、tms_stime
、tms_cutime
和值tms_ustime
设置为 0- 父进程设置的文件锁不会被子进程继承
- 为孩子清除待处理的警报
- 子进程的待处理信号集设置为空集