nohup命令
當用戶注銷(logout)或者網絡斷開時,終端會收到 HUP(hangup)信號從而關閉其所有子進程。因此,我們的解決辦法就有兩種途徑:要么讓進程忽略 HUP 信號,要么讓進程運行在新的會話里從而成為不屬於此終端的子進程。
nohup 的用途就是讓提交的命令忽略 hangup 信號。
setsid的用途就是讓進程運行在新的會話里從而成為不屬於此終端的子進程(fork)。
在Linux中,當在前台運行某個作業時,終端被該作業占據;而在后台運行作業時,它不會占據終端。可以使用&命令把作業放到后台執行。實際上,這樣是將命令放入到一個作業隊列中了:
兩種方式:
1. command & : 后台運行,關掉終端會停止運行
2. nohup command & : 后台運行,關掉終端也會繼續運行
在后台運行作業時要當心:需要用戶交互的命令不要放在后台執行,因為這樣機器就會一直等待。不過,作業在后台運行一樣會將結果輸出到屏幕上,干擾工作。
nohup命令:用途:不掛斷地運行命令。
setsid命令
nohup 命令
用途:不掛斷地運行命令
語法:nohup Command [ Arg ... ] [& ]
描述:nohup 命令運行由 Command參數和任何相關的Arg參數指定的命令,忽略所有掛斷(SIGHUP)信號。在注銷后使用 nohup 命令運行后台中的程序:要運行后台中的 nohup 命令,需要添加&到命令的尾部。
日志記錄:
無論是否將 nohup 命令的輸出重定向到終端,輸出都將附加到當前目錄的nohup.out 文件中。如果當前目錄的nohup.out文件不可寫,輸出重定向到$HOME/nohup.out文件中。如果沒有文件能創建或打開以用於追加,那么 Command 參數指定的命令不可調用。
使用時注意:
在當shell中提示了nohup成功后,還需要按終端上鍵盤任意鍵退回到shell輸入命令窗口,然后通過在shell中輸入exit來退出終端;如果在nohup執行成功后直接點關閉程序按鈕關閉終端的話,這時候會斷掉該命令所對應的session,導致nohup對應的進程被通知需要一起shutdown,起不到關掉終端后調用程序繼續后台運行的作用。
nohup 無疑能通過忽略 HUP 信號來使我們的進程避免中途被中斷,但如果我們換個角度思考,如果我們的進程不屬於接受 HUP 信號的終端的子進程,那么自然也就不會受到 HUP 信號的影響了。setsid 就能幫助我們做到這一點。
Daemon(守護)進程
Daemon進程也就是守護進程,linux大多數的服務進程都是通過守護進程實現的。比如0號進程(調度進程) ,1號進程(init進程)。從其名字守護看出其一般就是機器啟動就運行,關機才停止。所以其應該不會受到終端的影響。同時其實在后台運行的。
Session(會話):每打開一次終端(本地或遠程)登錄Linux,都會生成一個新的會話;除此之外,程序中也可以調用函數setsid創建一個新的會話;腳本也可以調用命令setsid創建一個新的會話。新建的會話無控制終端。
unix中進程組織結構為 session 包含一個前台進程組及一個或多個后台進程組,一個進程組包含多個進程。
一個session可能會有一個session首進程,而一個session首進程可能會有一個控制終端。
一個進程組可能會有一個進程組首進程。進程組首進程的進程ID與該進程組ID相等。
這兒是可能會有,在一定情況之下是沒有的。
與終端交互的進程是前台進程,否則便是后台進程
進程組 :
每個進程也屬於一個進程組
每個進程主都有一個進程組號,該號等於該進程組組長的PID號 .
一個進程只能為它自己或子進程設置進程組ID號
會話期(session)是一個或多個進程組的集合。
setsid()函數可以建立一個對話期:
如果,調用setsid的進程不是一個進程組的組長,此函數創建一個新的會話期。
(1)此進程變成該對話期的首進程
(2)此進程變成一個新進程組的組長進程。
(3)此進程沒有控制終端,如果在調用setsid前,該進程有控制終端,那么與該終端的聯系被解除。 如果該進程是一個進程組的組長,此函數返回錯誤。
(4)為了保證這一點,我們先調用fork()然后exit(),此時只有子進程在運行
SIGHUP(掛斷)會在以下3種情況下被發送給相應的進程:
1、終端關閉時,該信號被發送到session首進程以及作為job提交的進程(即用 & 符號提交的進程)
2、session首進程退出時,該信號被發送到該session中的前台進程組中的每一個進程
3、若父進程退出導致進程組成為孤兒進程組,且該進程組中有進程處於停止狀態(收到SIGSTOP或SIGTSTP信號),該信號會被發送到該進程組中的每一個進程。
系統對SIGHUP(掛斷)信號的默認處理是終止收到該信號的進程。所以若程序中沒有捕捉該信號,當收到該信號時,進程就會退出。
在后台運行的進程不一定是守護進程!一個進程要成為守護進程,必須做到以下兩點:
1) 在后台運行
2) 脫離了終端
要使一個進程在后台運行,代碼中可以通過fork子進程來實現,而命令行或腳本中可以通過使用“&”來實現。
fork之后,子進程會繼承父進程的SessionID,調用execve()后的進程的SessionID不會改變。
子進程從父進程繼承了:SessionID、進程組ID和打開的終端。子進程如果要脫離這些,代碼中可通過調用setsid來實現。,而命令行或腳本中可以通過使用命令setsid來運行程序實現。setsid幫助一個進程脫離從父進程繼承而來的已打開的終端、隸屬進程組和隸屬的會話。
需要注意,代碼中調用setsid是有條件的:即調用進程自己不能是進程組長。因此,調用setsid之前需要先fork,然后由產生的子進程調用setsid。
現在我們來給出創建守護進程所需步驟:
編寫守護進程的一般步驟步驟:
(1)在父進程中執行fork並exit推出;
(2)在子進程中調用setsid函數創建新的會話;
(3)在子進程中調用chdir函數,讓根目錄 ”/” 成為子進程的工作目錄;
(4)在子進程中調用umask函數,設置進程的umask為0;
(5)在子進程中關閉任何不需要的文件描述符
說明:
1. 在后台運行。
為避免掛起控制終端將Daemon放入后台執行。方法是在進程中調用fork使父進程終止,讓Daemon在子進程中后台執行。
if(pid=fork())
exit(0);//是父進程,結束父進程,子進程繼續
2. 脫離控制終端,登錄會話和進程組
有必要先介紹一下Linux中的進程與控制終端,登錄會話和進程組之間的關系:進程屬於一個進程組,進程組號(GID)就是進程組長的進程號(PID)。登錄會話可以包含多個進程組。這些進程組共享一個控制終端。這個控制終端通常是創建進程的登錄終端。
控制終端,登錄會話和進程組通常是從父進程繼承下來的。我們的目的就是要擺脫它們,使之不受它們的影響。方法是在第1點的基礎上,調用setsid()使進程成為會話組長:
setsid();
說明:當進程是會話組長時setsid()調用失敗。但第一點已經保證進程不是會話組長。setsid()調用成功后,進程成為新的會話組長和新的進程組長,並與原來的登錄會話和進程組脫離。由於會話過程對控制終端的獨占性,進程同時與控制終端脫離。
3. 禁止進程重新打開控制終端
現在,進程已經成為無終端的會話組長。但它可以重新申請打開一個控制終端。可以通過使進程不再成為會話組長來禁止進程重新打開控制終端:
if(pid=fork())
exit(0);//結束第一子進程,第二子進程繼續(第二子進程不再是會話組長)
4. 關閉打開的文件描述符
進程從創建它的父進程那里繼承了打開的文件描述符。如不關閉,將會浪費系統資源,造成進程所在的文件系統無法卸下以及引起無法預料的錯誤。按如下方法關閉它們:
for(i=0;i 關閉打開的文件描述符close(i);>
5. 改變當前工作目錄
進程活動時,其工作目錄所在的文件系統不能卸下。一般需要將工作目錄改變到根目錄。對於需要轉儲核心,寫運行日志的進程將工作目錄改變到特定目錄如/tmpchdir("/")
6. 重設文件創建掩模
進程從創建它的父進程那里繼承了文件創建掩模。它可能修改守護進程所創建的文件的存取位。為防止這一點,將文件創建掩模清除:umask(0);
7. 處理SIGCHLD信號
處理SIGCHLD信號並不是必須的。但對於某些進程,特別是服務器進程往往在請求到來時生成子進程處理請求。如果父進程不等待子進程結束,子進程將成為僵屍進程(zombie)從而占用系統資源。如果父進程等待子進程結束,將增加父進程的負擔,影響服務器進程的並發性能。在Linux下可以簡單地將SIGCHLD信號的操作設為SIG_IGN。
signal(SIGCHLD,SIG_IGN);
這樣,內核在子進程結束時不會產生僵屍進程。這一點與BSD4不同,BSD4下必須顯式等待子進程結束才能釋放僵屍進程。
以“&”方式運行有何問題?
以“&”方式可以將一個前台進程以后台方式運行,但是如果它是一個終端的job,則如果向終端收到SIGHUP信號,終端也會向它的所有job發送SIGHUP,這樣以“&”方式運行的進程則會因為收到SIGHUP則退出。
當用戶注銷(logout)或者網絡斷開時,終端會收到 SIGHUP(hangup)信號從而關閉其所有子進程。
早期的Unix,終端通過Modem和系統通訊,當用戶注銷(logout)時,Modem就會掛斷(hangup)電話;當Modem斷開時,會給終端發SIGHUP來通知終端關閉所有子進程。
Fork:
fork只是使得進程可以以后台方式運行,但不能使進程完全獨立,因為fork出來的進程仍然繼承了父進程已打開的終端、會話和進程組。
按下“ctrl+z”會觸發SIGTSTP,注意不是SIGSTOP,這兩個信號的區別是前者可以捕獲,而后者不可以。進程收到這兩個信號后,都進入STOP狀態,使用ps aux看到的狀態值為“T”,可以通過發送信號SIGCONT重新回到運行狀態。