linux終端關閉時為什麼會導致在其上啟動的程式退出?
現象
經常在linux下開發的人應該都有這樣的經驗,就是在終端上啟動的程式,在關閉終端時,這個程式的程式也被一起關閉了。看下面這個程式,為了使程式永遠執行,在輸出helloworld後,迴圈呼叫sleep:
直接關閉這個終端,在另一個終端上查詢該程式,已經找不到了:
這個行為看起來似乎是理所當然的,也符合人的第一感覺:”在終端上啟動的程式是屬於終端的,所以當關閉終端時,這個終端裡的一包裹程式都一起被解決掉了”。但這種說法是不能使一個會思考且充滿好奇心的人信服的。
下面我們就從linux程式管理的細節來剖析其根本原因。
終端程式
linux系統是基於程式的,幾乎每個命令都可以在相應的目錄下找到它們的程式,執行一個命令相當於啟動一個或多個程式,終端也不例外,在我centos下面終端對應一個bash程式(不同作業系統終端的bash程式可能不一樣),它位於/usr/bin/下面:
每當開啟一個終端都會啟動一個bash程式,我這裡啟動了兩個終端,可以看到有兩個bash程式:
終端程式與啟動程式的關係
linux系統裡面所有的程式的關係可以看做一個樹形結構,系統持續執行,程式的不斷啟動就是不斷fork的過程(fork是linux系統api,作用是複製自己來生成子程式),從系統啟動、初始化、登入終端、到執行命令都是生成子程式的過程:
init程式是所有程式的祖先,它的pid(程式id)為1,ppid(父程式id)也為1,因為它沒有父程式,系統內的其他程式都是由它或者它的子程式fork而來。
我們在linux上作業的終端對應了一個bash程式,在其上執行的命令和程式都是bash的子程式,或由bash的子程式衍生。
用hw程式驗證一下,可以看到hw程式的父程式正好是bash程式:
但這並不能解釋為什麼終端關閉了在上面執行的程式也跟著退出,因為在linux下,程式之間的關係並不像執行緒那樣,當主執行緒退出時,子執行緒一起被強制退出。程式之間沒有主次的區別,但有父子關係,而父子程式的執行是相對獨立的,一方的退出不會導致另一方退出。
程式session-揭開真相
在linux下,一個session是由一組程式組構成的,每個程式組又由多個程式構成。
在一個bash上執行的程式都歸屬於一個session(除非特別處理),而這個bash就是這個session的leader。每個session又可以關聯一個控制終端(Controlling Terminal)。
圖片:
- hw程式的ppid=5933,說明父程式為第一個bash,這個bash的父程式為gnome-ternimal程式,gnome-ternimal是centos視覺化介面的終端管理程式,每開啟一個終端,它都會啟動一個bash程式,而使用者的命令也是直接由bash程式執行的。
- hw程式和第一個bash同屬於一個session(sid=5933),這個sid等於bash的pid,所以第一個bash是這個session的leader。
- 圖片中還顯示了bash和hw程式擁有共同的終端裝置pts/2,它是一種字元裝置,不同於上面提到的gnome-ternimal程式。
- 當控制終端(對應gnome-ternimal)檢測到終端裝置斷(對應pts/2)開連線時,會通知裝置的控制程式,即傳送SIGHUP訊號給session leader(對應bash程式)。
- bash程式在收到SIGHUP後,將訊號發給session下的所有程式,導致使用者啟動的程式退出。
下面通過strace命令來驗證以上結論:
跟蹤hw程式(命令意為跟蹤pid為6367的程式上與signal有關的系統呼叫):
strace -e trace=signal -p 6367
跟蹤bash程式(命令意為跟蹤pid為5933的程式上與signal有關的系統呼叫):
strace -e trace=signal -p 5933
關閉啟動hw程式的終端,觀察strace輸出.
hwd的strace如下,si_pid=5933說明是5933這個程式發了SIGHUP給它,也就是bash程式:
bash的strace略微複雜:
kill(4294960929, SIGHUP)
kill第一個引數是32位有符號整數,轉換成int就是-6367,當引數為負時表示傳送給這個數絕對值的程式組,即pgrp=6367的所有程式,在上面的圖片中可以看到hw程式正好屬於該程式組。
kill(5933, SIGHUP)
5933是自己的pid,bash在第一次收到SIGHUP時先把訊號發給session內其他程式,然後再次傳送SIGHUP命令給自己,將自己殺死,後面的si_pid=5933也證實了這一點。
如何讓終端關閉時程式不退出
根據上面的結論,要使終端關閉時程式不退出,有以下幾種情況:
- 使用者程式攔截SIGHUP訊號。
- 使用者程式和bash程式不在一個session。
下面依次驗證這兩種情況
攔截SIGHUP
修改hw程式,忽略SIGHUP訊號:
signal(SIGHUP, SIG_IGN);
- 1
- 2
執行hw程式,並檢視程式,可以看到hw程式和父程式bash:
關閉終端,在另一個終端檢視程式:
bash程式已經退出,但hw程式還在,符合預期!!而且hw程式的ppid變成了1,說明hw在父程式bash退出後變成孤兒程式被init程式收養。
新建session&setsid
為了使使用者程式和bash不在同一個session,需要呼叫setsid方法,該方法的作用是新建一個新的session,並使自己成為leader。
// 先fork
int pid = fork();
if(pid > 0){
// 父程式, 直接退出
return 1;
}else if(pid == 0){
// 子程式
// 建立新的session
setsid();
//
printf("Hello World!\n");
printf("sleeping...\n");
while(1){
sleep(1);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
呼叫setsid前先fork,因為若不fork,hw作為程式組的leader,是不允許重建session的,原因留給讀者自己思考。
編譯並執行hw,檢視程式:
可以看到,相比之前,有幾個不同的地方:
- 程式啟動完,返回終端,hw切換到後臺執行。
- hw程式的父程式不再是bash,而是init程式。
- hw沒有關聯的終端裝置(pts/2)。
關閉終端,看到bash已經消失,但對hw程式沒有任何影響:
更簡單的方法
setsid命令,用setsid來啟動程式,這樣就不用修改任何程式碼也可以做到使啟動的程式在新的session中,並且終端關閉時,程式不退出。
setsid ./hw
nohup命令,被nohup啟動的程式會忽略SIGHUP訊號。
nohup ./hw
其他
命令列中&的作用:
./hw &
- 1
- 2
&的作用是使程式在後臺執行,輸入fg命令又可以使程式切換到前臺。雖然在後臺執行,但並不能保證程式在終端關閉時不退出。
總結
簡而言之,終端在關閉時會傳送SIGHUP給對應的bash程式,bash程式收到這個訊號後首先將它發給session下面的程式,如果你的程式沒有對SIGHUP訊號做特殊處理,那麼程式就會隨著終端關閉而退出。
相關文章
- Linux終端退出後導致nohup程式退出Linux
- 在Linux上啟動程式時會發生什麼?Linux
- 當你在 Linux 上啟動一個程式時會發生什麼?Linux
- 你在終端啟動的程式,最後都是什麼下場?(上)
- 當你開啟終端並輸入命令時會發生什麼?(上)
- 重啟和關閉 Linux 系統的 6 個終端命令Linux
- with open為什麼會自動關閉檔案流
- Linux終端是什麼?終端有什麼作用?Linux
- 為什麼float會導致父元素塌陷?
- 你在終端啟動的程式,最後都是什麼下場?(下)
- Linux中如何啟動程式?啟動程式的方法是什麼?Linux
- 在 Linux 上記錄和重放終端會話活動Linux會話
- Linux終端會話實時共享(kibitz)Linux會話
- Linux下的MongoDB安裝&啟動&關閉LinuxMongoDB
- node啟動程式-清理由於崩潰導致的沒有關掉的程式
- 什麼原因會導致raid掉陣AI
- springboot在lunix後臺啟動,退出賬號也不關閉Spring Boot
- 在duplicate時,出現監聽BLOCKED的情況,導致監聽自動關閉BloC
- PHP 原始碼探祕 – 為什麼 trim 會導致亂碼PHP原始碼
- linux程式啟動方式分為幾種?主要作用是什麼?Linux
- 終端如何退出pythonPython
- windows10怎麼關閉快速啟動_windows10關閉快速啟動的方法Windows
- 為什麼js會有閉包JS
- 你有什麼喜歡的 Linux 終端技巧?Linux
- win10系統快速啟動怎麼關閉 關閉Windows快速啟動Win10Windows
- 執行緒崩潰為什麼不會導致 JVM 崩潰執行緒JVM
- linux關閉防火牆命令是什麼 linux永久關閉防火牆命令分享Linux防火牆
- win10開機自啟動程式關閉方法_win10怎麼關閉開機啟動項Win10
- 為什麼Linux會在桌面端完敗給Windows?LinuxWindows
- mac 下終端啟動 appium,執行程式碼時報錯MacAPP行程
- win10快速啟動怎麼關閉_win10如何關閉快速啟動Win10
- 什麼是seLinux?Linux關閉seLinux有什麼影響?Linux
- 為什麼我開啟 testhome 都是移動端的樣式
- 關閉Windows中每天都會自啟的Adobe更新程式Windows
- Qt啟動子程式,子程式關閉時通知主程式,實現主程式對子程式的管理QT
- 為什麼動態CMOS沒有上拉延時?
- mac os 關閉sip保護有什麼好處?有什麼壞處?macOS為什麼關閉sip?Mac
- 為什麼電池正負極短接會導致電池燒燬