Linux 里养僵尸是怎么回事呢? Linux 相信大家都很熟悉,但是 Linux 里养僵尸是怎么回事呢,下面就让小编带大家一起了解吧。
上一篇挖了个 SIGHUP 的坑,这篇试着填一下。
之前在《程序员面试指北:面试官视角》里面说过,在结构化面试中,我们会从各个方向去考查候选人,其中之一是操作系统。
上篇介绍了一套题,我还有另一套,一般这么开场:
在终端下启动一个命令,如果在命令结束前关掉终端,它还能正常运行吗?
这其实是一个很常见的 case,但凡 Linux 或者 Mac 用得多一点,都会遇到。
在我还是一个穷酸学生的 2009 年,每个月都需要支付 20 元巨款(当时能买 3 根鸭脖),通过一个禁止分享网络的认证客户端接入校园网。
为了共建和谐宿舍 <del>节省网费</del> ,我历经千辛万苦,交叉编译开源的 Linux 认证客户端,集成到固件里,并刷到了我的 NETGEAR 路由器上。
然后山水 BBS 的 Linux 版主把我的帖子置顶了 11 年。<del>可见他有多痛恨禁止共享网络</del>
这么一回忆,感觉自己的共享经济思维真是前卫,当时怎么就没想到去搞共享单车呢?
扯远了,在捣腾的过程中,我就踩了这么个坑:当我 ssh 到路由器上、刚启动认证时,能够正常联网;但是退出 ssh 后一会,网就断了。
经过一番捣腾后发现,只要一退出 ssh,认证程序就凉了,而不是继续在后台保持和认证服务器的通信。
所以前面那个问题,我以为大部分候选人应该会回答“否”,但没想到竟然还有不少人回答“是”。
其实回答“是”也没什么错,因为确实也有些命令不会随着终端关闭而结束。
问题是当我追问当时执行的是什么命令时,候选人往往又说不出个所以然来。
(借学长的表情一用)
然后我就感到很强的挫败感:这不按剧本来,没法问了啊……只好换题。
当然大部分候选人确实被坑过,于是我可以接着问:
如果确实需要在后台继续执行命令怎么办呢?
有些人只记得要在后面加个 & ;但也有不少人知道前面还得加个 nohup,就像这样:
$ nohup python process.py &
[1] 1806824
nohup: ignoring input and appending output to 'nohup.out'
注:其实我更喜欢 screen (或 tmux ),偶尔也用 setsid 。
然后就可以放心地关闭终端 <del>开始放羊</del> 了。
但我的套题还没结束:为什么加上 nohup 就可以让进程在后台继续运行呢?
(这表情熟悉吗)
铺垫了这么多,总算是可以开始填坑了。
答案其实很好找,man nohup 就能看到:
The nohup utility invokes utility with its arguments and at this time sets the signal SIGHUP to be ignored
nohup 工具在启动命令的同时会将 SIGHUP 信号设置为忽略。 而关于 SIGHUP,Wikipedia 原文是这样介绍的:
On POSIX-compliant platforms, SIGHUP ("signal hang up") is a signal sent to a process when its controlling terminal is closed.
对于 POSIX 兼容的平台(如 Unix 、Linux 、BSD 、Mac ),当进程所在的控制终端关闭时,系统会给进程发送 SIGHUP 信号( Signal Hang Up,挂断信号)。
为什么叫 SIGHUP 呢?(严正申明:这一问不在套题里[doge])
我们知道,在上古时代,捉 bug 就已经是码农的必备技能(更准确地说是 moth )。
(我总觉得这个图是假的)
到了远古时代,他们不再需要去机房,通过基于 RS-232 协议的串行线路连接到大型机的终端上,就可以开始收福报。
收完福报,程序员通知自己的猫( modem )挂断( Hang Up )连接;大型机的 OS 检测到连接断开,就会给进程发送信号 —— 所以这信号被称为 SIGHUP 。
这果然是毫无卵用的知识啊。
很多同学在操作系统的课程上学习了“进程间的通信方式有信号、管道、消息队列、共享内存……”,但是对信号到底是个什么东西,并没有现实的概念。
课堂教学的理论和实践往往是割裂的,在此特别推荐《 Unix 环境高级编程》(简称 APUE )。
APUE 在 1.9 - 信号 中写到:信号是通知进程已发生某种条件的一种技术。
而在 Linux/Unix 下,进程对信号的处理有三种选择:
以 SIGHUP 信号为例,系统默认处理方式就是结束进程。
当然终端下打开的第一个进程通常都是 shell (例如 bash )。shell 会给 SIGHUP 信号注册一个回调函数,用于给该 shell 下所有的子进程发送 SIGHUP 信号,然后再主动退出。
对于求生欲很强的程序(例如 nohup ),可以主动选择忽略该信号。
有一些进程本来就被设计成在后台运行,不需要控制终端,因此它们将 SIGHUP 挪作它用,一个常见的用法就是重新读取配置文件(例如 Apache 、Nginx ),上篇提到的 logrotate 正是利用了这一点。
终于填完了坑。
说了这么多都还是纸上谈兵,实操中如何主动忽略 SIGHUP 呢?
实际上也很简单,使用 Linux 的 signal 系统调用即可:
#include <signal.h>
#include <unistd.h>
int main() {
signal(SIGHUP, SIG_IGN);
sleep(1000);
return 0;
}
不妨试试看,编译运行起来,即使关闭终端,它也会在后台继续运行。
signal 也可以用于指定回调函数(或重置为系统默认处理方式),这里就不展开了,感兴趣的同学可以参考 APUE 里的代码,以及阅读 signal 的 manual 。
使用回调函数还需要注意一个坑:
由于回调函数可能在任意时刻被触发,因此要避免调用不可重入的函数(典型如 printf )。常见的做法是 set 一个 flag,然后在程序的主循环中检测该 flag,再按需执行相应任务。
SIGHUP 只是常见的一个信号,在 Linux 下,信号还有大量其他的场景和应用。
当你按下 Ctrl + C,就是给进程发送了一个 SIGINT 信号。
当你执行 kill -TERM $PID,就是给进程发送了一个 SIGTERM 信号。可能和你期望有出入的是,SIGTERM 是可以被进程忽略的。所以有时候你得用 SIGKILL (kill -9) 。
你还可以使用可自定义的 SIGUSR1 、SIGUSR2 、SIGURG 来实现一些功能,比如《踩坑记#2:Go 服务锁死》中提到 Golang 在其 goroutine 调度中使用了 SIGURG 。
这次就不总结了,最后再用一个和信号有关的 case 收尾。
Linux 内核会为每一个进程分配一个 task_struct 结构体,用于保存进程的相关信息。
在进程死亡后,系统会发送一个 SIGCHLD 信号给它的父进程。
正确的父进程实现,通常应当使用 wait 系统调用来给子进程收尸 —— 父进程往往需要知道子进程结束这个事件,而且可能还需要得知其退出原因( exit code )。
然后内核才会将对应的 task_struct 释放。
如果父进程没有收尸,task_struct 里的 state 会一直保持为 EXIT_ZOMBIE,这时在 ps 或 top 等命令里,就可以看到该进程的状态为 Z,而且无法被 kill 。
这就是所谓的僵尸进程,这时候你找九叔都没用。
(大半夜找这图还挺渗人的)
所以 Linux 里养僵尸,其实就是子进程死了父进程不收尸,大家可能会很惊讶 Linux 里怎么会养僵尸呢?但事实就是这样,小编也感到非常惊讶。
这就是关于 Linux 里养僵尸的事情了,大家有什么想法呢,欢迎在评论区告诉小编一起讨论哦!
推荐阅读
▄▄▄▄▄▄▄ ▄ ▄▄▄▄ ▄▄▄▄▄▄▄
█ ▄▄▄ █ ▄▀ ▄ ▀██▄ ▀█▄ █ ▄▄▄ █
█ ███ █ █ █ █▀▀▀█▀ █ ███ █
█▄▄▄▄▄█ ▄ █▀█ █▀█ ▄▀█ █▄▄▄▄▄█
▄▄▄ ▄▄▄▄█ ▀▄█▀▀▀█ ▄█▄▄ ▄
▄█▄▄▄▄▄▀▄▀▄██ ▀ ▄ █▀▄▄▀▄▄█
█ █▀▄▀▄▄▀▀█▄▀█▄▀█████▀█▀▀█ █▄
▀▀ █▄██▄█▀ █ ▀█▀ ▀█▀ ▄▀▀▄█
█▀ ▀ ▄▄▄▄▄▄▀▄██ █ ▄████▀▀ █▄
▄▀▄▄▄ ▄ ▀▀▄████▀█▀ ▀ █▄▄▄▀▄█
▄▀▀██▄▄ █▀▄▀█▀▀ █▀ ▄▄▄██▀ ▀
▄▄▄▄▄▄▄ █ █▀ ▀▀ ▄██ ▄ █▄▀██
█ ▄▄▄ █ █▄ ▀▄▀ ▀██ █▄▄▄█▄ ▀
█ ███ █ ▄ ███▀▀▀█▄ █▀▄ ██▄ ▀█
█▄▄▄▄▄█ ██ ▄█▀█ █ ▀██▄▄▄ █▄
1
pcbl 2020-07-11 13:27:47 +08:00 via iPhone 2
提个建议,铺垫部分比例过高,有点抓不住重点
|
2
cjpjxjx 2020-07-11 13:28:48 +08:00 5
开头就是老营销号了
|
3
Jirajine 2020-07-11 13:30:54 +08:00 via Android 4
|
6
ochatokori 2020-07-11 13:35:24 +08:00 via Android 2
看了开头,营销号
拉黑了 |
8
yangbonis 2020-07-11 13:41:55 +08:00 via iPhone
说了僵尸,不讲下 reaper 吗
|
9
k1z 2020-07-11 13:44:06 +08:00
结尾才是精髓,一看就是老编了
|
10
imnaive 2020-07-11 13:54:17 +08:00
写得很有趣,也有代码讲解,为啥要喷成营销号。
|
13
b0ttle 2020-07-11 14:24:14 +08:00 1
武大学长啊哈哈,现在校园网改成网页验证了
|
14
JB18CM 2020-07-11 14:27:56 +08:00 3
营销号...
已拉黑了 |
15
Nich0la5 2020-07-11 14:32:35 +08:00 via Android
没必要 内容挺好的开头结尾膈应人
|
17
viruser 2020-07-11 14:44:30 +08:00
@Jirajine 不如看 APUE 和老外的文章,这个博主竟然还在相信 tor+代理能保证安全,让我对他的技术水平产生了怀疑...
|
19
jydeng 2020-07-11 14:52:28 +08:00
放这么多图干嘛?
|
20
ysmood 2020-07-11 15:05:27 +08:00 1
|
21
yujiang 2020-07-11 15:15:26 +08:00 via Android
讲技术放锤子表情包?
|
23
sunrisewestern 2020-07-11 15:21:35 +08:00
看到 20 块钱和鸭脖就猜是我武的
|
24
jiangzhuo 2020-07-11 15:26:28 +08:00 1
发现全篇有用的跟标题有关的就最后倒数第二段半句
“所以 Linux 里养僵尸,其实就是子进程死了父进程不收尸” 建议把这句放到开头…… |
25
felix021 OP @jiangzhuo 原计划是放开头的,但后面就没法写了。。。标题党一次也是不容易,下次再试试其他套路。
|
26
hxsf 2020-07-11 15:38:06 +08:00
建议发到推广节点
|
28
luckycatio 2020-07-11 15:51:38 +08:00
@viruser 请问按他的使用方式哪里会出现安全隐患?
|
30
felix021 OP @luckycatio 瞎猜,比如 tor 节点本身可能是钓鱼的,貌似有过这样的干扰。
|
31
XanderChen 2020-07-11 16:13:43 +08:00
前排围观。
另外文章可以转载吗 |
32
felix021 OP @XanderChen 可以的,标明作者和出处就行
|
33
whileFalse 2020-07-11 18:03:22 +08:00
虽然看到最后有些不适,但还是学到了知识。
|
34
tlday 2020-07-11 18:30:32 +08:00
又是你,上次说你发的技术文章干货密度太低,这次说你发的频率太高吧,表情包太多不看,已 block 。
|
35
lwp2070809 2020-07-11 18:48:40 +08:00 via Android
在 block 楼主之前可以看看楼主之前发的文章
不过就这篇文章而言我也是非常讨厌的,看到第二张图片我就直接拉下来不看了 |
36
reus 2020-07-11 19:00:20 +08:00
实话说,这种文章真的很水
你看这本 2002 年出版的书,就有讲 nohup 了: http://www.oreilly.com.cn/index.php?func=book&isbn=7-5083-0947-2 18 年前啊,这种根本就没什么好说的东西,还加表情包来注水 稍微有点现代的环境都不用 nohup 的了,还拿来面试,实在是无语 |
37
felix021 OP @reus 我也觉得挺水的,但您这逻辑就有意思了,因为十几年前别人写过所以就不应该再写了?那您这辈子大概啥也不用写了。我自己写得开心就好,您看不惯的话 block 就好了,不劳烦帮我顶贴。
|
38
felix021 OP @whileFalse 最后那两句(包括开头)其实是吐槽这种风格的营销号,有些人能理解我的用意,读出了乐趣,有些人不理解,读出了不适,这个我确实没啥办法,这层意思如果写在原文里味道就不对了。
|
39
luckycatio 2020-07-11 20:21:10 +08:00
@felix021 钓鱼节点从 tor 发明的时候开始就一直存在,就算 3 跳连的都是蜜罐节点,也只能知道你的代理 IP,和访问的网站,这样如何抓到在国内的使用者?
|
40
felix021 OP @luckycatio 不了解,所以我说我是瞎猜。。我对 tor 不太熟悉, @viruser 同学来解个惑?
|
41
cvbnt 2020-07-11 20:37:07 +08:00 via Android
@viruser tor+代理只是基础中的基础,不是 tor+代理能保证安全,而是保证安全的环节中 tor+代理不可少,除此之外还有身份隔离外加虚拟环境方面的考量,真有问题人早就被抓了
|
42
lxilu 2020-07-11 22:39:51 +08:00 1
建议缩小图片,优化用户体验。占了半屏!占几行为妥。
|
43
eallion 2020-07-11 22:42:18 +08:00 via Android
在 v2 拉黑了还出现在 feed 里…
|
44
felix021 OP @lxilu 多谢建议,下次截图弄小点,在微信里可以调整尺寸,v2 直接按原图尺寸展示确实比较蛋疼
|
45
smilingsun 2020-07-11 22:50:08 +08:00
有意思!
|
47
ichao1214 2020-07-12 00:20:39 +08:00 via Android
果然难度降低,友好了很多。想看不收尸的情况。
|
48
stevefan1999 2020-07-12 02:02:55 +08:00 2
爲什麼會有殭屍進程,詳情可以看看 The Collapse of the UNIX Philosophy
https://kukuruku.co/post/the-collapse-of-the-unix-philosophy/ ``` This quote is from the early UNIX manual. Even then, the existence of zombie processes was considered a bug. But this bug was later simply forgotten. Needless to say, this problem was later solved anyway. So, there are tools in modern GNU/Linux for killing zombie processes, but few people know about them. You can’t get rid of a zombie with a regular kill. Everybody says about the existence of zombie processes that “It’s for design”. ``` 簡單說就是 bug 變成了 feature |
49
vk42 2020-07-12 03:08:04 +08:00
倒不是反对用比较调侃的方式科谱,不过文中的调侃有点生硬看着比较尬……
另外关于最后父进程没有 wait()的,只要父进程结束了 init 进程也会回收僵尸的 |
50
shanlanlan 2020-07-12 04:06:26 +08:00
Linux 养僵尸养僵尸是怎么回事呢? Linux 养僵尸相信大家都很熟悉,但是 Linux 养僵尸养僵尸是怎么回事呢,下面就让小编带大家一起了解吧。
Linux 养僵尸养僵尸,其实就是有鬼,大家可能会很惊讶 Linux 养僵尸怎么会养僵尸呢?但事实就是这样,小编也感到非常惊讶。 这就是关于 Linux 养僵尸养僵尸的事情了,大家有什么想法呢,欢迎在评论区告诉小编一起讨论哦! |
51
mageemeng 2020-07-12 08:13:30 +08:00
看了一眼地址栏,的确是 V 站啊
|
53
ydpro 2020-07-12 11:36:43 +08:00
厉害了👍,僵尸进程水出了花
|
54
Zien 2020-07-12 11:42:26 +08:00 via Android 1
开头一看就知道是武大华科了哈哈
|