linux结束进程_生人勿近之Linux里养僵尸
Linux里養(yǎng)僵尸是怎么回事呢?Linux相信大家都很熟悉,但是Linux里養(yǎng)僵尸是怎么回事呢,下面就讓小編帶大家一起了解吧。
- 1?-
上一篇挖了個 SIGHUP 的坑,這篇試著填一下。
之前在《程序員面試指北:面試官視角》里面說過,在結(jié)構(gòu)化面試中,我們會從各個方向去考查候選人,其中之一是操作系統(tǒng)。
上篇介紹了一套題,我還有另一套,一般這么開場:
在終端下啟動一個命令,如果在命令結(jié)束前關(guān)掉終端,它還能正常運(yùn)行嗎?
- 2?-
這其實是一個很常見的case,但凡 Linux 或者 Mac 用得多一點(diǎn),都會遇到。
在我還是一個窮酸學(xué)生的2009年,每個月都需要支付 20 元巨款(當(dāng)時能買3根鴨脖),通過一個禁止分享網(wǎng)絡(luò)的認(rèn)證客戶端接入校園網(wǎng)。
為了共建和諧宿舍?節(jié)省網(wǎng)費(fèi),我歷經(jīng)千辛萬苦,交叉編譯開源的Linux認(rèn)證客戶端,集成到固件里,并刷到了我的 NETGEAR 路由器上。
然后山水 BBS 的 Linux 版主把我的帖子置頂了 11 年。可見他有多痛恨禁止共享網(wǎng)絡(luò)。
這么一回憶,感覺自己的共享經(jīng)濟(jì)思維真是前衛(wèi),當(dāng)時怎么就沒想到去搞共享單車呢?
扯遠(yuǎn)了,在搗騰的過程中,我就踩了這么個坑:當(dāng)我ssh到路由器上、剛啟動認(rèn)證時,能夠正常聯(lián)網(wǎng);但是退出ssh后一會,網(wǎng)就斷了。
經(jīng)過一番搗騰后發(fā)現(xiàn),只要一退出ssh,認(rèn)證程序就涼了,而不是繼續(xù)在后臺保持和認(rèn)證服務(wù)器的通信。
- 3 -
所以前面那個問題,我以為大部分候選人應(yīng)該會回答“否”,但沒想到竟然還有不少人回答“是”。
其實回答“是”也沒什么錯,因為確實也有些命令不會隨著終端關(guān)閉而結(jié)束。
問題是當(dāng)我追問當(dāng)時執(zhí)行的是什么命令時,候選人往往又說不出個所以然來。
(借學(xué)長的表情一用)
然后我就感到很強(qiáng)的挫敗感:這不按劇本來,沒法問了啊……只好換題。
當(dāng)然大部分候選人確實被坑過,于是我可以接著問:
如果確實需要在后臺繼續(xù)執(zhí)行命令怎么辦呢?
有些人只記得要在后面加個 & ;但也有不少人知道前面還得加個 nohup,就像這樣:
$ nohup python process.py &[1]?1806824nohup:?ignoring?input?and?appending?output?to?'nohup.out'注:其實我更喜歡 screen(或 tmux),偶爾也用 setsid。
然后就可以放心地關(guān)閉終端?開始放羊?了。
但我的套題還沒結(jié)束:為什么加上 nohup 就可以讓進(jìn)程在后臺繼續(xù)運(yùn)行呢?
(這表情熟悉嗎)
- 4 -
鋪墊了這么多,總算是可以開始填坑了。
答案其實很好找,man nohup 就能看到:
The?nohup?utility?invokes?utility?with?its?arguments?and?at?this?time?sets?the?signal?SIGHUP?to?be?ignored
nohup工具在啟動命令的同時會將 SIGHUP 信號設(shè)置為忽略。
而關(guān)于 SIGHUP,Wikipedia原文是這樣介紹的:
On POSIX-compliant platforms, SIGHUP ("signal hang up") is a signal sent to a process when its controlling terminal is closed.
wikipedia.org/wiki/SIGHUP對于 POSIX 兼容的平臺(如Unix、Linux、BSD、Mac),當(dāng)進(jìn)程所在的控制終端關(guān)閉時,系統(tǒng)會給進(jìn)程發(fā)送 SIGHUP 信號(Signal Hang Up,掛斷信號)。
為什么叫 SIGHUP 呢?(嚴(yán)正申明:這一問不在套題里)
我們知道,在上古時代,捉 bug 就已經(jīng)是碼農(nóng)的必備技能(更準(zhǔn)確地說是 moth)。
(我總覺得這個圖是假的)
到了遠(yuǎn)古時代,他們不再需要去機(jī)房,通過基于 RS-232 協(xié)議的串行線路連接到大型機(jī)的終端上,就可以開始收福報。
收完福報,程序員通知自己的貓(modem)掛斷(Hang Up)連接;大型機(jī)的 OS 檢測到連接斷開,就會給進(jìn)程發(fā)送信號 —— 所以這信號被稱為 SIGHUP 。
這果然是毫無卵用的知識啊。
- 5 -
很多同學(xué)在操作系統(tǒng)的課程上學(xué)習(xí)了“進(jìn)程間的通信方式有信號、管道、消息隊列、共享內(nèi)存……”,但是對信號到底是個什么東西,并沒有現(xiàn)實的概念。
課堂教學(xué)的理論和實踐往往是割裂的,在此特別推薦《Unix環(huán)境高級編程》(簡稱APUE)。
APUE在 1.9 - 信號 中寫到:信號是通知進(jìn)程已發(fā)生某種條件的一種技術(shù)。
而在 Linux/Unix 下,進(jìn)程對信號的處理有三種選擇:
按系統(tǒng)默認(rèn)方式處理
提供一個回調(diào)函數(shù)
或忽略該信號(有些信號例外,不允許被忽略)
以 SIGHUP 信號為例,系統(tǒng)默認(rèn)處理方式就是結(jié)束進(jìn)程。
當(dāng)然終端下打開的第一個進(jìn)程通常都是shell(例如bash)。shell會給 SIGHUP 信號注冊一個回調(diào)函數(shù),用于給該 shell 下所有的子進(jìn)程發(fā)送 SIGHUP 信號,然后再主動退出。
對于求生欲很強(qiáng)的程序(例如nohup),可以主動選擇忽略該信號。
有一些進(jìn)程本來就被設(shè)計成在后臺運(yùn)行,不需要控制終端,因此它們將?SIGHUP 挪作它用,一個常見的用法就是重新讀取配置文件(例如Apache、Nginx),上篇提到的 logrotate 正是利用了這一點(diǎn)。
終于填完了坑。
- 6 -
說了這么多都還是紙上談兵,實操中如何主動忽略 SIGHUP 呢?
實際上也很簡單,使用 Linux 的 signal 系統(tǒng)調(diào)用即可:
#include #include int main() { signal(SIGHUP, SIG_IGN); sleep(1000); return 0;}不妨試試看,編譯運(yùn)行起來,即使關(guān)閉終端,它也會在后臺繼續(xù)運(yùn)行。
signal 也可以用于指定回調(diào)函數(shù)(或重置為系統(tǒng)默認(rèn)處理方式),這里就不展開了,感興趣的同學(xué)可以參考 APUE 里的代碼,以及閱讀 signal 的manual。
使用回調(diào)函數(shù)還需要注意一個坑:
由于回調(diào)函數(shù)可能在任意時刻被觸發(fā),因此要避免調(diào)用不可重入的函數(shù)(典型如printf)。常見的做法是 set 一個 flag,然后在程序的主循環(huán)中檢測該 flag,再按需執(zhí)行相應(yīng)任務(wù)。
- 7 -
SIGHUP 只是常見的一個信號,在 Linux 下,信號還有大量其他的場景和應(yīng)用。
當(dāng)你按下 Ctrl + C ,就是給進(jìn)程發(fā)送了一個 SIGINT 信號。
當(dāng)你執(zhí)行 kill -9 $PID,就是給進(jìn)程發(fā)送了一個 SIGKILL 信號。可能和你期望有出入的是,SIGKILL 是可以被進(jìn)程忽略的。所以有時候你得用 SIGTERM。
你還可以使用可自定義的 SIGUSR1、SIGUSR2、SIGURG 來實現(xiàn)一些功能,比如《踩坑記#2:Go服務(wù)鎖死》中提到 Golang 在其 goroutine 調(diào)度中使用了 SIGURG 。
- 8 -
這次就不總結(jié)了,最后再用一個和信號有關(guān)的 case 收尾。
Linux 內(nèi)核會為每一個進(jìn)程分配一個 task_struct 結(jié)構(gòu)體,用于保存進(jìn)程的相關(guān)信息。
在進(jìn)程死亡后,系統(tǒng)會發(fā)送一個 SIGCHLD 信號給它的父進(jìn)程。
正確的父進(jìn)程實現(xiàn),通常應(yīng)當(dāng)使用 wait 系統(tǒng)調(diào)用來給子進(jìn)程收尸 —— 父進(jìn)程往往需要知道子進(jìn)程結(jié)束這個事件,而且可能還需要得知其退出原因(exit code)。
然后內(nèi)核才會將對應(yīng)的 task_struct 釋放。
如果父進(jìn)程沒有收尸,task_struct 里的 state 會一直保持為 EXIT_ZOMBIE,這時在 ps 或 top 等命令里,就可以看到該進(jìn)程的狀態(tài)為 Z ,而且無法被 kill 。
這就是所謂的僵尸進(jìn)程,這時候你找九叔都沒用。
(大半夜找這圖還挺滲人的)
所以Linux里養(yǎng)僵尸,其實就是子進(jìn)程死了父進(jìn)程不收尸,大家可能會很驚訝Linux里怎么會養(yǎng)僵尸呢?但事實就是這樣,小編也感到非常驚訝。
這就是關(guān)于Linux里養(yǎng)僵尸的事情了,大家有什么想法呢,歡迎在評論區(qū)告訴小編一起討論哦!
推薦閱讀
程序員面試指北:面試官視角
踩坑記:go服務(wù)內(nèi)存暴漲
TCP:學(xué)得越多越不懂
UTF-8:一些好像沒什么用的冷知識
[譯] C程序員該知道的內(nèi)存知識 (1)
總結(jié)
以上是生活随笔為你收集整理的linux结束进程_生人勿近之Linux里养僵尸的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: abaqus实例详解_Abaqus接触分
- 下一篇: aes 加密_Jmeter处理AES加密