红联Linux门户
Linux协助

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

发布时刻:2018-01-19 21:12:00来历:jessie-pang作者:linux.cn
本文是关于 fork 和 exec 是如安在 Unix 上作业的。你或许现已知道,也有人还不知道。几年前当我了解到这些时,我惊叹不已。
咱们要做的是发动一个进程。咱们现已在博客上评论了许多关于体系调用的问题,每逢你发动一个进程或许翻开一个文件,这都是一个体系调用。所以你或许会认为有这样的体系调用:
start_process(["ls", "-l", "my_cool_directory"])
这是一个合理的主意,明显这是它在 DOS 或 Windows 中的作业原理。我想说的是,这并不是 Linux 上的作业原理。但是,我查阅了文档,的确有一个 posix_spawn 的体系调用根本上是这样做的,不过这不在本文的评论范围内。
 
fork 和 exec
Linux 上的 posix_spawn 是经过两个体系调用完结的,分别是 fork 和 exec(实践上是 execve),这些都是人们常常运用的。虽然在 OS X 上,人们运用 posix_spawn,而 fork 和 exec 是不建议的,但咱们将评论的是 Linux。
Linux 中的每个进程都存在于“进程树”中。你能够经过运转 pstree 指令检查进程树。树的根是 init,进程号是 1。每个进程(init 在外)都有一个父进程,一个进程都能够有许多子进程。
所以,假定我要发动一个名为 ls 的进程来列出一个目录。我是不是只需建议一个进程 ls 就好了呢?不是的。
我要做的是,创立一个子进程,这个子进程是我(me)自身的一个克隆,然后这个子进程的“脑子”被吃掉了,变成 ls。
开端是这样的:
my parent
 |- me
然后运转 fork(),生成一个子进程,是我(me)自己的一份克隆:
my parent
 |- me
  |-- clone of me
然后我让该子进程运转 exec("ls"),变成这样:
my parent
 |- me
  |-- ls
当 ls 指令完毕后,我简直又变回了我自己:
my parent
 |- me
  |-- ls (zombie)
在这时 ls 其实是一个僵尸进程。这意味着它现已死了,但它还在等我,以防我需求检查它的返回值(运用 wait 体系调用)。一旦我获得了它的返回值,我将再次康复独自一人的状况。
my parent
 |- me
 
fork 和 exec 的代码完结
假如你要编写一个 shell,这是你有必要做的一个操练(这是一个十分风趣和有启发性的项目。Kamal 在 Github 上有一个很棒的研讨会:https://github.com/kamalmarhubi/shell-workshop)。
事实证明,有了 C 或 Python 的技术,你能够在几个小时内编写一个十分简略的 shell,像 bash 相同。(至少假如你周围能有个人多少懂一点,假如没有的话用时会久一点。)我现已完结啦,真的很棒。
这便是 fork 和 exec 在程序中的完结。我写了一段 C 的伪代码。请记住,fork 也或许会失利哦。
int pid = fork();
// 我要兼顾啦
// “我”是谁呢?或许是子进程也或许是父进程
if (pid == 0) {
// 我现在是子进程
// “ls” 吃掉了我脑子,然后变成一个彻底不相同的进程
exec(["ls"])
} else if (pid == -1) {
// 天啊,fork 失利了,简直是灾祸!
} else {
// 我是父进程耶
// 持续做一个酷酷的美男子吧
// 需求的话,我能够等候子进程完毕
}
 
上文说到的“脑子被吃掉”是什么意思呢?
进程有许多特点:
翻开的文件(包含翻开的网络连接)
环境变量
信号处理程序(在程序上运转 Ctrl + C 时会发作什么?)
内存(你的“地址空间”)
寄存器
可履行文件(/proc/$pid/exe)
cgroups 和命名空间(与 Linux 容器相关)
当时的作业目录
运转程序的用户
其他我还没想到的
当你运转 execve 并让另一个程序吃掉你的脑子的时分,实践上简直一切东西都是相同的!你们有相同的环境变量、信号处理程序和翻开的文件等等。
仅有改动的是,内存、寄存器以及正在运转的程序,这但是件大事。
 
为何 fork 并非那么消耗资源(写入时仿制)
你或许会问:“假如我有一个运用了 2GB 内存的进程,这是否意味着每次我发动一个子进程,一切 2 GB 的内存都要被仿制一次?这听起来要消耗许多资源!”
事实上,Linux 为 fork() 调用完结了写时仿制,关于新进程的 2GB 内存来说,就像是“看看旧的进程就好了,是相同的!”。然后,当假如任一进程企图写入内存,此刻体系才真实地仿制一个内存的副本给该进程。假如两个进程的内存是相同的,就不需求仿制了。
 
为什么你需求知道这么多
你或许会说,好吧,这些细节听起来很厉害,但为什么这么重要?关于信号处理程序或环境变量的细节会被承继吗?这对我的日常编程有什么实践影响呢?
有或许哦!比如说,在 Kamal 的博客上有一个很有意思的 bug。它评论了 Python 怎么使信号处理程序疏忽了 SIGPIPE。也便是说,假如你从 Python 里运转一个程序,默许情况下它会疏忽 SIGPIPE!这意味着,程序从 Python 脚本和从 shell 发动的体现会有所不同。在这种情况下,它会形成一个古怪的问题。
所以,你的程序的环境(环境变量、信号处理程序等)或许很重要,都是从父进程承继来的。知道这些,在调试时是很有用的。
 
linux下用fork和kill来完结7*24进程:http://www.138comgov138.com/linux/27898.html
linux c之创立进程fork和vfork函数之间的差异:http://www.138comgov138.com/linux/27831.html
函数fork()与vfork()的比照:http://www.138comgov138.com/linux/26657.html
经典的Fork解析:http://www.138comgov138.com/linux/12690.html
Linux进程之Fork函数:http://www.138comgov138.com/linux/11154.html