守護(hù)進(jìn)程解析
時(shí)間:2018-03-09作者:華清遠(yuǎn)見
一:守護(hù)進(jìn)程的簡介我們常用的進(jìn)程一般分為三類:<1>交互進(jìn)程 <2>批處理進(jìn)程<3>守護(hù)進(jìn)程。守護(hù)進(jìn)程(Daemon)是一種運(yùn)行在后臺(tái)的一種特殊的進(jìn)程,它獨(dú)立于控制終端并且周期性的執(zhí)行某種任務(wù)或等待處理某些發(fā)生的事件。守護(hù)進(jìn)程也叫精靈進(jìn)程。由于在linux中,每個(gè)系統(tǒng)與用戶進(jìn)行交流的界面成為終端,每一個(gè)從此終端開始運(yùn)行的進(jìn)程都會(huì)依附于這個(gè)終端,這個(gè)終端被稱為這些進(jìn)程的控制終端,當(dāng)控制終端被關(guān)閉的時(shí)候,相應(yīng)的進(jìn)程都會(huì)自動(dòng)關(guān)閉。但是守護(hù)進(jìn)程卻能突破這種限制,它脫離于終端并且在后臺(tái)運(yùn)行,并且它脫離終端的目的是為了避免進(jìn)程在運(yùn)行的過程中的信息在任何終端中顯示并且進(jìn)程也不會(huì)被任何終端所產(chǎn)生的終端信息所打斷。它從被執(zhí)行的時(shí)候開始運(yùn)轉(zhuǎn),知道整個(gè)系統(tǒng)關(guān)閉才退出(當(dāng)然可以認(rèn)為的殺死相應(yīng)的守護(hù)進(jìn)程)。如果想讓某個(gè)進(jìn)程不因?yàn)橛脩艋蛑袛嗷蚱渌兓绊懀敲淳捅仨毎堰@個(gè)進(jìn)程變成一個(gè)守護(hù)進(jìn)程。 二:相關(guān)概念 查看守護(hù)進(jìn)程第一行的信息。ps axj | head -1 ubuntu@farsight:/etc/profile.d$ ps axj | head -1 PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 父進(jìn)程ID 進(jìn)程ID 進(jìn)程組ID 會(huì)話期ID 終端ID 終端進(jìn)程組ID 狀態(tài) 用戶 運(yùn)行時(shí)間 指令 1:進(jìn)程組 每運(yùn)行一個(gè)程序或是命令就會(huì)產(chǎn)生一個(gè)進(jìn)程組,而每一個(gè)進(jìn)程組有一個(gè)組長進(jìn)程。 進(jìn)程組由進(jìn)程組號(hào)(GID)標(biāo)識(shí),進(jìn)程組號(hào)(GID)為組長進(jìn)程PID,一般進(jìn)程組的第一個(gè)進(jìn)程是組長進(jìn)程。 注:進(jìn)程組中的這些進(jìn)程之間不是孤立的,他們彼此之間或者存在者父子、兄弟關(guān)系,或者在功能有相近的聯(lián)系。 那linux為什么要有進(jìn)程組呢?其實(shí)提供進(jìn)程組就是方便管理這些進(jìn)程。 進(jìn)程組的概念有很多用途,最常見的是我們在終端上向前臺(tái)執(zhí)行程序發(fā)出終止信號(hào)(Ctrl-C),同時(shí)終止整個(gè)進(jìn)程組的所有進(jìn)程。 每個(gè)進(jìn)程必定屬于一個(gè)進(jìn)程組,也只能屬于一個(gè)進(jìn)程組。
函數(shù)getpgrp可以返回調(diào)用進(jìn)程的進(jìn)程組ID pid_t getpgrp(void); //得到進(jìn)程組的ID 返回值:成功則返回進(jìn)程組ID,失敗返回-1。
2:會(huì)話 一次登錄形成一個(gè)會(huì)話,一個(gè)會(huì)話可包含多個(gè)進(jìn)程組(前臺(tái)或后臺(tái)), 但只能有一個(gè)前臺(tái)進(jìn)程組,多個(gè)進(jìn)程組組成我們的會(huì)話。 setsid()可建立一個(gè)新的會(huì)話,注意進(jìn)程組的組長進(jìn)程不能調(diào)用,調(diào)用進(jìn)程是新會(huì)話的首進(jìn)程(session leader) 3:控制終端 會(huì)話的首進(jìn)程(session leader)打開一個(gè)終端之后, 該終端就成為該會(huì)話的控制終端 與控制終端建立連接的會(huì)話首進(jìn)程稱為控制進(jìn)程,一個(gè)會(huì)話只能有一個(gè)控制終端 在控制終端上產(chǎn)生的輸入和信號(hào)將發(fā)送給會(huì)話的前臺(tái)進(jìn)程組中的所有進(jìn)程 終端上的連接斷開時(shí) (比如網(wǎng)絡(luò)斷開或 Modem 斷開), 掛起信號(hào)將發(fā)送到控制進(jìn)程
linux是一個(gè)多用戶多任務(wù)的分時(shí)操作系統(tǒng),必須要支持多個(gè)用戶同時(shí)登陸同一個(gè)操作系統(tǒng),當(dāng)一個(gè)用戶登陸一次終端時(shí)就會(huì)產(chǎn)生一個(gè)會(huì)話。
每個(gè)會(huì)話有一個(gè)會(huì)話首進(jìn)程,即創(chuàng)建會(huì)話的進(jìn)程,建立與終端連接的就是這個(gè)會(huì)話首進(jìn)程,也被稱為控制進(jìn)程。 為什么要這么分呢?前臺(tái)進(jìn)程組是指需要與終端進(jìn)行交互的進(jìn)程組(只能有一個(gè)比如有些進(jìn)程是需要完成IO操作的,那么這個(gè)進(jìn)程就會(huì)被設(shè)置為前臺(tái)進(jìn)程組.當(dāng)我們鍵入終端的中斷鍵和退出鍵時(shí),就會(huì)將信號(hào)發(fā)送到前臺(tái)進(jìn)程組中的所有進(jìn)程。而后臺(tái)進(jìn)程組是指不需要與終端進(jìn)程交互的進(jìn)程組,比如:一些進(jìn)程不需要完成IO 操作,或者一些守護(hù)進(jìn)程就會(huì) 被設(shè)置為后臺(tái)進(jìn)程組(可以有多個(gè))。 如果終端接口檢測到網(wǎng)絡(luò)已經(jīng)斷開連接,則會(huì)將掛斷信號(hào)發(fā)送給會(huì)話首進(jìn)程。 三:創(chuàng)建守護(hù)進(jìn)程 創(chuàng)建一個(gè)守護(hù)進(jìn)程的步驟如下: <1>創(chuàng)建子進(jìn)程,父進(jìn)程退出 這是創(chuàng)建守護(hù)進(jìn)程的第一步。由于守護(hù)進(jìn)程是脫離控制終端的,因此,完成第一步后就會(huì)在Shell終端里造成一程序已經(jīng)運(yùn)行完畢的假象。之后的所有工作都在子進(jìn)程中完成,而用戶在Shell終端里則可以執(zhí)行其他命令,從而在形式上做到了與控制終端的脫離。在Linux 中父進(jìn)程先于子進(jìn)程退出會(huì)造成子進(jìn)程成為孤兒進(jìn)程,而每當(dāng)系統(tǒng)發(fā)現(xiàn)一個(gè)孤兒進(jìn)程是,就會(huì)自動(dòng)由1號(hào)進(jìn)程(init)收養(yǎng)它,這樣,原先的子進(jìn)程就會(huì)變成init進(jìn)程的子進(jìn)程。 <2>在子進(jìn)程中創(chuàng)建的新會(huì)話 [脫離控制終端] Linux是一個(gè)多用戶多任務(wù)系統(tǒng),每個(gè)進(jìn)程都有一個(gè)進(jìn)程ID,同時(shí)每個(gè)進(jìn)程還都屬于某一個(gè)進(jìn)程組,而每個(gè)進(jìn)程組都有一個(gè)組長進(jìn)程,組長進(jìn)程的標(biāo)識(shí)ID等于進(jìn)程組的ID,且該進(jìn)程組ID不會(huì)因組長進(jìn)程的退出而受到影響。會(huì)話期是一個(gè)或多個(gè)進(jìn)程組的集合,通常,一個(gè)會(huì)話開始與用戶登錄,終止于用戶退出,在此期間該用戶運(yùn)行的所有進(jìn)程都屬于這個(gè)會(huì)話期。我們這里要用到setsid()函數(shù)。 setsid()函數(shù)的作用:創(chuàng)建一個(gè)新的會(huì)話,并且擔(dān)任該會(huì)話組的組長。具體作用包括:讓一個(gè)進(jìn)程擺脫原會(huì)話的控制,讓進(jìn)程擺脫原進(jìn)程的控制,讓進(jìn)程擺脫原控制終端的控制。 pid_t setsid(void); (1)此進(jìn)程變成該對話期的首進(jìn)程。 (2)此進(jìn)程變成一個(gè)新進(jìn)程組的組長進(jìn)程。 (3)此進(jìn)程沒有控制終端,如果在調(diào)用setsid前,該進(jìn)程有控制終端,那么與該終端的聯(lián)系被解除。 (4)這個(gè)系統(tǒng)調(diào)用,只能由非組長進(jìn)程來調(diào)用。組長進(jìn)程不能調(diào)用。 思考:有了前邊兩個(gè)步驟,有沒有人有這樣的疑問,為什么要?jiǎng)?chuàng)建個(gè)子進(jìn)程然后父進(jìn)程退出,然后setsid()脫離當(dāng)前會(huì)話?直接就在之前的進(jìn)程中setsid()把當(dāng)前進(jìn)程脫離會(huì)話不可以么? 答案:組長進(jìn)程,一般是這個(gè)程序執(zhí)行的第一個(gè)進(jìn)程,那么我們的父進(jìn)程一般就是這組進(jìn)程的組長,而setsid()這個(gè)函數(shù)的使用,是不能由組長進(jìn)程來調(diào)用的。所以必須先創(chuàng)建一個(gè)非組長進(jìn)程來調(diào)用它。 <3>改變進(jìn)程的工作目錄到"/" 使用fork創(chuàng)建的子進(jìn)程繼承了父進(jìn)程的當(dāng)前工作目錄。 守護(hù)進(jìn)程不應(yīng)當(dāng)使用父進(jìn)程的工作目錄,應(yīng)該設(shè)置自己的工作目錄,通?梢酝ㄟ^ chdir()來完成,一般可以將其設(shè)置為根目錄。 chdir("/"); <4>重設(shè)文件掩碼 umask(0) 守護(hù)進(jìn)程從父進(jìn)程繼承來的文件創(chuàng)建方式掩碼可能會(huì)拒絕設(shè)置某些許可權(quán)限,文件權(quán)限掩碼是指屏蔽掉文件權(quán)限中的對應(yīng)位。 umask(0); <5>關(guān)掉所有不需要的文件描述符號(hào) 如果創(chuàng)建它的進(jìn)程之前打開了某個(gè)文件,然后創(chuàng)建這個(gè)守護(hù)進(jìn)程,這樣子進(jìn)程就繼承了 fd,如果守護(hù)進(jìn)程不關(guān)閉這個(gè)fd,一個(gè)是會(huì)占用資源,二個(gè)與改變工作目錄一樣,如果這個(gè)文件是位于掛載目錄,那么就無法umount了。 這里可以關(guān)閉所有當(dāng)前系統(tǒng)的文件描述符。怎么獲取當(dāng)前進(jìn)程最大的可以打開的文件描述符個(gè)數(shù)呢?sysconf(_SC_OPEN_MAX); <6>重新處理一下[0,1,2]==>標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)出錯(cuò)。 這里需要把0,1,2重新定位一下,定位到/dev/null。這樣后邊再使用open的時(shí)候,獲得的文件描述符還可以從3開始。不然就是從0開始的,而如果我們程序中,有從0中獲取標(biāo)準(zhǔn)輸入,從1中做輸出,那么就成了對那個(gè)文件的讀寫了,這顯然是錯(cuò)誤的了。
把/dev/null看作"黑洞"。 它非常等價(jià)于一個(gè)只寫文件, 所有寫入它的內(nèi)容都會(huì)永遠(yuǎn)丟失,而嘗試從它那兒讀取內(nèi)容則什么也讀不到.。 int main(int argc, const char *argv[]) { pid_t pid = fork(); if (pid ==-1) { perror("fork()"); exit(-1); } else if (0 == pid) { //當(dāng)前是子進(jìn)程 //1.重新設(shè)置會(huì)話 if (setsid() < 0) { perror("setsid()"); exit(-1); } //2.把工作目錄設(shè)置到根目錄 chdir("/"); //3.重新設(shè)置文件權(quán)限掩碼 umask(0); //4.關(guān)閉所有文件描述符 int i; for (i = 1; i < sysconf(_SC_OPEN_MAX); i++) close(i); //5. 特殊處理0,1,2文件描述符 open("/dev/null", O_RDWR); open("/dev/null", O_RDWR); open("/dev/null", O_RDWR); } else { / /當(dāng)前是父進(jìn)程,直接退出 return 0; } return 0; } 拓展:守護(hù)進(jìn)程創(chuàng)建之后,那么中間的調(diào)試信息不能通過屏幕輸出出來了,那么我們看不到中間的過程,怎么來調(diào)試呢?可以通過syslog來將調(diào)試信息寫到日志中。 頭文件:#include<syslog.h> 函數(shù)原型:void openlog(const char *ident,int option,int facil-ity); 函數(shù)功能:打開日志文件。 函數(shù)參數(shù):@ ident:對哪個(gè)進(jìn)程進(jìn)行日志記錄,為進(jìn)程名,如果不指定,則默認(rèn)也是進(jìn)程名。 @ option常用選項(xiàng): LOG_CONS:如果當(dāng)前寫日志失敗了,就將信息輸出到終端控制臺(tái)。 LOG_PID:打印的每一條日志信息包含當(dāng)前進(jìn)程的PID @ facil-ity常用選項(xiàng): LOG_USER:打印的每一條日志信息包含當(dāng)前用戶的等級(jí)信息 函數(shù)原型:void syslog(int priority, const char *format, ...); 函數(shù)功能:向日志文件中寫日志信息。 函數(shù)參數(shù):@ format:輸出日志信息的參數(shù)列表,用法類同printf @ priority:輸出日志的等級(jí)信息 LOG_EMERG system is unusable LOG_ALERT action must be taken immediately LOG_CRIT critical conditions LOG_ERR error conditions LOG_WARNING warning conditions LOG_NOTICE normal, but significant, condition LOG_INFO informational message LOG_DEBUG debug-level message 注意:寫完的日志信息在哪里呢?一般是在/var/log目錄下,不同的發(fā)行版對應(yīng)的具體的文件不一樣,ubuntu中對應(yīng)的是/var/log/syslog文件中。 函數(shù)原型:void closelog(void); 函數(shù)功能:關(guān)閉日志文件。 一般就是在進(jìn)程啟動(dòng)的時(shí)候打開日志,在進(jìn)程結(jié)束的時(shí)候去關(guān)閉日志,這樣中間就不同的記錄就可以了,不需要每次記錄都打開關(guān)閉,甚至打開和關(guān)閉也可以不需要,系統(tǒng)在第一次調(diào)用syslog記錄日志的時(shí)候,如果發(fā)現(xiàn)日志是沒有打開的,就按照自己默認(rèn)的方式幫我們先打開日志文件,但是默認(rèn)的記錄的內(nèi)容相對簡單,如果我們自己需要控制日志中都輸出什么信息,我們最好按照自己需要的方式打開一下。 相關(guān)資訊
發(fā)表評(píng)論
|
全國咨詢電話:400-611-6270,雙休日及節(jié)假日請致電值班手機(jī):15010390966
在線咨詢: 曹老師QQ(3337544669), 徐老師QQ(1462495461), 劉老師 QQ(3108687497)
企業(yè)培訓(xùn)洽談專線:010-82600901,院校合作洽談專線:010-82600350,在線咨詢:QQ(248856300)
Copyright 2004-2018 華清遠(yuǎn)見教育科技集團(tuán) 版權(quán)所有 ,京ICP備16055225號(hào),京公海網(wǎng)安備11010802025203號(hào)