提及這三個是因為我們在linux終端中運行程序,有希望不因為終端的退出或異常斷開導致運行的程序退出的需求。而之所以終端退出會異常斷開程序退出的原因是因為其會向終端中運行的程序發送SIGHUP信號。
SIGHUP
關於SIGHUP的介紹,如下為摘自百度百科
unix中進程組織結構為 session 包含一個前台進程組及一個或多個后台進程組,一個進程組包含多個進程。
一個session可能會有一個session首進程,而一個session首進程可能會有一個控制終端。
一個進程組可能會有一個進程組首進程。進程組首進程的進程ID與該進程組ID相等。
這兒是可能會有,在一定情況之下是沒有的。
與終端交互的進程是前台進程,否則便是后台進程
SIGHUP會在以下3種情況下被發送給相應的進程:
1、終端關閉時,該信號被發送到session首進程以及作為job提交的進程(即用 & 符號提交的進程)
2、session首進程退出時,該信號被發送到該session中的前台進程組中的每一個進程
3、若父進程退出導致進程組成為孤兒進程組,且該進程組中有進程處於停止狀態(收到SIGSTOP或SIGTSTP信號),該信號會被發送到該進程組中的每一個進程。
系統對SIGHUP信號的默認處理是終止收到該信號的進程。所以若程序中沒有捕捉該信號,當收到該信號時,進程就會退出。
可以看下SIGHUP信號和控制終端,分析的挺不錯的。
nohup
參見鏈接:nohup命令的用法clear
用途:不掛斷地運行命令。
語法:nohup Command [ Arg … ] [ & ]
描述:nohup 命令運行由 Command 參數和任何相關的 Arg 參數指定的命令,忽略所有掛斷(SIGHUP)信號。在注銷后使用 nohup 命令運行后台中的程序。要運行后台中的 nohup 命令,添加 & ( 表示”and”的符號)到命令的尾部。
無論是否將 nohup 命令的輸出重定向到終端,輸出都將附加到當前目錄的 nohup.out 文件中。如果當前目錄的 nohup.out 文件不可寫,輸出重定向到 $HOME/nohup.out 文件中。如果沒有文件能創建或打開以用於追加,那么 Command 參數指定的命令不可調用。如果標准錯誤是一個終端,那么把指定的命令寫給標准錯誤的所有輸出作為標准輸出重定向到相同的文件描述符。
退出狀態:該命令返回下列出口值:
126 可以查找但不能調用 Command 參數指定的命令。
127nohup 命令發生錯誤或不能查找由 Command參數指定的命令。
否則,nohup 命令的退出狀態是 Command 參數指定命令的退出狀態。
nohup命令及其輸出文件
nohup命令:如果你正在運行一個進程,而且你覺得在退出帳戶時該進程還不會結束,那么可以使用nohup命令。該命令可以在你退出帳戶/關閉終端之后繼續運行相應的進程。nohup就是不掛起的意思( n ohang up)。
該命令的一般形式為:nohup command &
使用nohup命令提交作業
如果使用nohup命令提交作業,那么在缺省情況下該作業的所有輸出都被重定向到一個名為nohup.out的文件中,除非另外指定了輸出文件:
nohupcommand > myout.file 2>&1 &
在上面的例子中,輸出被重定向到myout.file文件中。
可以通過Nohup的源碼分析,看出nohup 的本質是通過忽略SIGHUP信號,從而不會在終端關閉會退出時由於SIGHUP信號導致程序退出。nohup附加的特性為無論是否將 nohup 命令的輸出重定向到終端,輸出都將附加到當前目錄的 nohup.out 文件中。如果當前目錄的 nohup.out 文件不可寫,輸出重定向到 $HOME/nohup.out 文件中。
Nohup本身不會將程序放入后台運行,但是通過NOHUP方式啟動的程序,由於其忽略了SIGHUP信號,其接收到SIGHUP信號也不會退出。
&后台運行
就是將程序放在后台運行。對於shell來說,通過& 可以把一個程序放在后台運行。其就是作為一個job來運行的。此時如果終端斷開,程序還是會被SIGHUP信號導致退出的。即上面SIGHUP信號的第一種場景。
但是如果此時在終端中執行exit命令,就會使得當前主進程退出。從而使得后台執行的程序成為孤兒進程,從而被init進程接管。此時終端再斷開就 不會受到SIGHUP信號了。即即使終端斷開,程序也會運行。這也是為什么我們再一個shell腳本中通過&執行一個程序后,shell腳本執行 完,即使終端斷開,shell腳本啟動的后台程序也不會退出。因為shell執行完后,其啟動的程序就已經成為了孤兒進程,從而被init接管了。
Daemon
Daemon進程也就是守護進程,linux大多數的服務進程都是通過守護進程實現的。比如0號進程(調度進程) ,1號進程(init進程)。從其名字守護看出其一般就是機器啟動就運行,關機才停止。所以其應該不會受到終端的影響。同時其實在后台運行的。
在當前的linux下已經提供了一個api可以直接調用一下就可以將自己的進程變為守護進程了:該函數說明如下:可以參見http://man7.org/linux/man-pages/man3/daemon.3.html
int daemon ( int __nochdir, int __noclose);
如果__nochdir的值為0,則將切換工作目錄為根目錄;
如果__noclose為0,則將標准輸入,輸出和標准錯誤都重定向到/dev /null。
使用非常簡單:一個簡單示例如下:
#include <unistd.h>
#include <stdio.h>
int do_sth()
{
//Addwhat u want
return 0;
}
int main()
{
daemon(0,0);
while ( 1 )
{
do_sth();
sleep(1);
}
}
編譯並運行
[leconte@localhostdaemon]$ gcc -o test test.c
[leconte@localhostdaemon]$ ./test
而之前大部分都是需要自己實現,關於守護進程編程的要點網上很多,總結如下:主要參見Linux守護進程的編程方法
1. 屏蔽一些有關控制終端操作的信號。這是為了防止在守護進程沒有正常運轉起來時,控制終端受到干擾退出或掛起。示例如下:
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP,SIG_IGN);
所有的信號都有自己的名字。這些名字都以“SIG”開頭,只是后面有所不同。開發人員可以通過這些名字了解到系統中發生了什么事。當信號出現時,開發人員可以要求系統進行以下三種操作:
忽略信號。大多數信號都是采取這種方式進行處理的,這里就采用了這種用法。但值得注意的是對SIGKILL和SIGSTOP信號不能做忽略處理。
捕捉信號。最常見的情況就是,如果捕捉到SIGCHID信號,則表示子進程已經終止。然后可在此信號的捕捉函數中調用waitpid()函數取得 該子進程的進程ID和它的終止狀態。另外,如果進程創建了臨時文件,那么就要為進程終止信號SIGTERM編寫一個信號捕捉函數來清除這些臨時文件。
執行系統的默認動作。對絕大多數信號而言,系統的默認動作都是終止該進程。對這些有關終端的信號,一般采用忽略處理,從而保障了終端免受干擾。
這類信號分別是,SIGTTOU(表示后台進程寫控制終端)、SIGTTIN(表示后台進程讀控制終端)、SIGTSTP(表示終端掛起)和SIGHUP(進程組長退出時向所有會議成員發出的)。
2. 后台運行:為了避免其會掛起(或者說占用)終端,應該將其放在后台運行,實現的方式就是fork,然后將進程退出。
3. 脫離控制終端、登錄會話和進程組:進程屬於進程組,進程組號就是進程組長的進程號。登錄的會話可以包含多個進程組。這些進程組 共享一個控制終端。這個控制終端是創建進程的登錄終端。控制終端、登錄會話和進程組都是在fork的時候從父進程繼承下來的。我們需要擺脫他們,使之不受 他們的影響,方式就是在1的基礎上,調用setsid()使進程成為會話組長。
setsid();
說明:當進程是會話組長時setsid()調用失敗。但第一點已經保證進程不是會話組長。 setsid()調用成功后,進程成為新的會話組長和新的進程組長,並與原來的登錄會話和進程組脫離。由於會話過程對控制終端的獨占性,進程同時與控制終端脫離。
4. 禁止進程重新打開控制終端:(注:這一步是網上對於daemon的差異,有些認為不需要,有些認為需要) 現在的進程已經是無終端的會話組長,但是會話組長是可以重新申請打開一個控制終端的。而我們可以通過使進程不再成為會話組長來禁止進程重新打開控制終端。方式就是再次fork()再將父進程退出。使用其子進程(該子進程的父進程才是會話組長,它自己肯定不是會話組長了)
5. 關閉打開的文件描述符:由於fork的時候會繼承父進程打開的文件描述符,如果不關閉,則會浪費系統資源,造成進程所在的文件系統無法卸下以及引起其他無法預料的錯誤,所以要關閉他們:
6. 改變當前工作目錄:進程活動室,其工作目錄所在的文件系統不能卸載,一般需要將工作目錄改變到跟目錄,而對於需要轉儲核心,寫運行日志的進程則將工作目錄改變到特定目錄,比如/tmp。chdir("/tmp")
7. 重設文件掩碼:重設文件創建掩模 進程從創建它的父進程那里繼承了文件創建掩模。它可能修改守護進程所創建的文件的存取位。為防止這一點,將文件創建掩模清除:umask(0);
8. 處理SIGCHLD信號:處理SIGCHLD信號
處理SIGCHLD信號並不是必須的。但對於某些進程,特別是服務器進程往往在請求到來時生成子進程處理請求。如果父進程不等待子進程結束,子進程將成為 僵屍進程(zombie)從而占用系統資源。如果父進程等待子進程結束,將增加父進程的負擔,影響服務器進程的並發性能。在Linux下可以簡單地將 SIGCHLD信號的操作設為SIG_IGN。
signal(SIGCHLD,SIG_IGN);
這樣,內核在子進程結束時不會產生僵屍進程。這一點與BSD4不同,BSD4下必須顯式等待子進程結束才能釋放僵屍進程。
守護進程與用&結尾的后台運行程序有什么區別呢?
$ shopt
cdable_vars off
cdspell off
checkhash off
checkwinsize off
cmdhist on
dotglob off
execfail off
expand_aliases on
extdebug off
extglob off
extquote on
failglob off
force_fignore on
gnu_errfmt off
histappend off
histreedit off
histverify off
hostcomplete on
huponexit off
interactive_comments on
lithist off
login_shell on
mailwarn off
no_empty_cmd_completion off
nocaseglob off
nocasematch off
nullglob off
progcomp on
promptvars on
restricted_shell off
shift_verbose off
sourcepath on
xpg_echo off
上面的默認選項中,huponexit off,這個情況時候,當你退出shell時候,后台的程序還會繼續運行,
http://blog.csdn.net/hepeng597/article/details/9816751