首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

当你在 Linux 上启动一个进程时会发生什么?

本文是关于 fork 和 exec 是如何在 Unix 上工作的。你或许已经知道,也有人还不知道。几年前当我了解到这些时,我惊叹不已。

-- Julia Evans

本文导航

◈ fork 和 exec

13%

◈ fork 和 exec 的代码实现

41%

◈ 上文提到的“脑子被吃掉”是什么意思呢?

59%

◈ 为何 fork 并非那么耗费资源(写入时复制)

71%

◈ 为什么你需要知道这么多

80%

编译自 | https://jvns.ca/blog/2016/10/04/exec-will-eat-your-brain/

作者 | Julia Evans

译者 | jessie-pang

本文是关于 fork 和 exec 是如何在 Unix 上工作的。你或许已经知道,也有人还不知道。几年前当我了解到这些时,我惊叹不已。

我们要做的是启动一个进程。我们已经在博客上讨论了很多关于系统调用的问题,每当你启动一个进程或者打开一个文件,这都是一个系统调用。所以你可能会认为有这样的系统调用:

这是一个合理的想法,显然这是它在 DOS 或 Windows 中的工作原理。我想说的是,这并不是 Linux 上的工作原理。但是,我查阅了文档,确实有一个posix_spawn

[1]

的系统调用基本上是这样做的,不过这不在本文的讨论范围内。

fork 和 exec

Linux 上的 是通过两个系统调用实现的,分别是 和 (实际上是 ),这些都是人们常常使用的。尽管在 OS X 上,人们使用 ,而 和 是不提倡的,但我们将讨论的是 Linux。

Linux 中的每个进程都存在于“进程树”中。你可以通过运行 命令查看进程树。树的根是 ,进程号是 1。每个进程( 除外)都有一个父进程,一个进程都可以有很多子进程。

所以,假设我要启动一个名为 的进程来列出一个目录。我是不是只要发起一个进程 就好了呢?不是的。

我要做的是,创建一个子进程,这个子进程是我()本身的一个克隆,然后这个子进程的“脑子”被吃掉了,变成 。

开始是这样的:

然后运行 ,生成一个子进程,是我()自己的一份克隆:

然后我让该子进程运行 ,变成这样:

当 ls 命令结束后,我几乎又变回了我自己:

在这时 其实是一个僵尸进程。这意味着它已经死了,但它还在等我,以防我需要检查它的返回值(使用 系统调用)。一旦我获得了它的返回值,我将再次恢复独自一人的状态。

fork 和 exec 的代码实现

如果你要编写一个 shell,这是你必须做的一个练习(这是一个非常有趣和有启发性的项目。Kamal 在 Github 上有一个很棒的研讨会:https://github.com/kamalmarhubi/shell-workshop)。

事实证明,有了 C 或 Python 的技能,你可以在几个小时内编写一个非常简单的 shell,像 bash 一样。(至少如果你旁边能有个人多少懂一点,如果没有的话用时会久一点。)我已经完成啦,真的很棒。

这就是 和 在程序中的实现。我写了一段 C 的伪代码。请记住,fork 也可能会失败哦。

[3]

上文提到的“脑子被吃掉”是什么意思呢?

进程有很多属性:

◈ 打开的文件(包括打开的网络连接)

◈ 环境变量

◈ 信号处理程序(在程序上运行 Ctrl + C 时会发生什么?)

◈ 内存(你的“地址空间”)

◈ 寄存器

◈ 可执行文件()

◈ cgroups 和命名空间(与 Linux 容器相关)

◈ 当前的工作目录

◈ 运行程序的用户

◈ 其他我还没想到的

当你运行 并让另一个程序吃掉你的脑子的时候,实际上几乎所有东西都是相同的! 你们有相同的环境变量、信号处理程序和打开的文件等等。

唯一改变的是,内存、寄存器以及正在运行的程序,这可是件大事。

为何 fork 并非那么耗费资源(写入时复制)

你可能会问:“如果我有一个使用了 2GB 内存的进程,这是否意味着每次我启动一个子进程,所有 2 GB 的内存都要被复制一次?这听起来要耗费很多资源!”

事实上,Linux 为 调用实现了写时复制copy on write,对于新进程的 2GB 内存来说,就像是“看看旧的进程就好了,是一样的!”。然后,当如果任一进程试图写入内存,此时系统才真正地复制一个内存的副本给该进程。如果两个进程的内存是相同的,就不需要复制了。

为什么你需要知道这么多

你可能会说,好吧,这些细节听起来很厉害,但为什么这么重要?关于信号处理程序或环境变量的细节会被继承吗?这对我的日常编程有什么实际影响呢?

有可能哦!比如说,在 Kamal 的博客上有一个很有意思的bug

[4]

。它讨论了 Python 如何使信号处理程序忽略了 。也就是说,如果你从 Python 里运行一个程序,默认情况下它会忽略 !这意味着,程序从 Python 脚本和从 shell 启动的表现会有所不同。在这种情况下,它会造成一个奇怪的问题。

所以,你的程序的环境(环境变量、信号处理程序等)可能很重要,都是从父进程继承来的。知道这些,在调试时是很有用的。

via:https://jvns.ca/blog/2016/10/04/exec-will-eat-your-brain/

作者:Julia Evans

[6]

译者:jessie-pang校对:wxy

本文由LCTT原创编译,Linux中国荣誉推出

LCTT 译者

jessie-pang

共计翻译:3篇

贡献时间:10 天

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180120B0A2SE00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券