|  | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Linux守護進程 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1.守護進程概述 守(shou)(shou)護進(jin)程(cheng)(cheng)(cheng),也就是(shi)(shi)通常(chang)(chang)所說的(de)daemon進(jin)程(cheng)(cheng)(cheng),是(shi)(shi)Linux中(zhong)的(de)后臺(tai)服(fu)(fu)務(wu)(wu)進(jin)程(cheng)(cheng)(cheng)。它是(shi)(shi)一個生存(cun)期較長的(de)進(jin)程(cheng)(cheng)(cheng),通常(chang)(chang)獨立于控制終端并且(qie)周期性地執行某種任務(wu)(wu)或等(deng)待處理(li)某些發生的(de)事件。守(shou)(shou)護進(jin)程(cheng)(cheng)(cheng)常(chang)(chang)常(chang)(chang)在(zai)系(xi)統引導載(zai)入時啟(qi)動,在(zai)系(xi)統關閉時終止。Linux有(you)很多系(xi)統服(fu)(fu)務(wu)(wu),大多數服(fu)(fu)務(wu)(wu)都(dou)是(shi)(shi)通過守(shou)(shou)護進(jin)程(cheng)(cheng)(cheng)實現的(de)。同時,守(shou)(shou)護進(jin)程(cheng)(cheng)(cheng)還(huan)能完(wan)成(cheng)許多系(xi)統任務(wu)(wu),例如(ru),作業規(gui)劃進(jin)程(cheng)(cheng)(cheng)crond、打印進(jin)程(cheng)(cheng)(cheng)lqd等(deng)(這里的(de)結(jie)尾字母d就是(shi)(shi)daemon的(de)意思)。 由于(yu)(yu)在Linux中,每(mei)一個(ge)系統與用戶進(jin)(jin)(jin)行交(jiao)流的(de)界面稱為終(zhong)(zhong)端(duan),每(mei)一個(ge)從此終(zhong)(zhong)端(duan)開(kai)始運(yun)行的(de)進(jin)(jin)(jin)程(cheng)(cheng)都(dou)會(hui)依附于(yu)(yu)這個(ge)終(zhong)(zhong)端(duan),這個(ge)終(zhong)(zhong)端(duan)稱為這些進(jin)(jin)(jin)程(cheng)(cheng)的(de)控制(zhi)(zhi)終(zhong)(zhong)端(duan),當控制(zhi)(zhi)終(zhong)(zhong)端(duan)被關閉(bi)時,相應(ying)的(de)進(jin)(jin)(jin)程(cheng)(cheng)都(dou)會(hui)自動(dong)關閉(bi)。但(dan)是守護(hu)進(jin)(jin)(jin)程(cheng)(cheng)卻能(neng)夠突破這種(zhong)限制(zhi)(zhi),它從被執行開(kai)始運(yun)轉,直到接收到某種(zhong)信(xin)號或(huo)者(zhe)整個(ge)系統關閉(bi)時才會(hui)退出。如果想讓某個(ge)進(jin)(jin)(jin)程(cheng)(cheng)不因(yin)為用戶、終(zhong)(zhong)端(duan)或(huo)者(zhe)其他(ta)的(de)變化而受到影響,那么就必須把這個(ge)進(jin)(jin)(jin)程(cheng)(cheng)變成(cheng)一個(ge)守護(hu)進(jin)(jin)(jin)程(cheng)(cheng)。可(ke)見,守護(hu)進(jin)(jin)(jin)程(cheng)(cheng)是非常(chang)重要的(de)。 2.編寫守護進程 編寫(xie)守(shou)(shou)護(hu)進(jin)程(cheng)(cheng)看(kan)似(si)復雜,但實際上也是(shi)遵循一個(ge)(ge)(ge)特定的(de)(de)流(liu)程(cheng)(cheng),只要將此(ci)流(liu)程(cheng)(cheng)掌握了,就能很(hen)方便地編寫(xie)出自(zi)己的(de)(de)守(shou)(shou)護(hu)進(jin)程(cheng)(cheng)。下面就分4個(ge)(ge)(ge)步驟來講解(jie)怎樣創(chuang)建一個(ge)(ge)(ge)簡單的(de)(de)守(shou)(shou)護(hu)進(jin)程(cheng)(cheng)。在講解(jie)的(de)(de)同時,會配(pei)合介(jie)紹與創(chuang)建守(shou)(shou)護(hu)進(jin)程(cheng)(cheng)相(xiang)關的(de)(de)幾個(ge)(ge)(ge)系統(tong)函數,希望讀者能很(hen)好地掌握。   (1)創(chuang)建子進(jin)(jin)程(cheng)(cheng),父(fu)進(jin)(jin)程(cheng)(cheng)退出(chu)。這是編寫守護進(jin)(jin)程(cheng)(cheng)的(de)第一(yi)步。由于守護進(jin)(jin)程(cheng)(cheng)是脫離(li)控制(zhi)終(zhong)端的(de),因此,完成第一(yi)步后(hou)就會(hui)在(zai)(zai)(zai)shell終(zhong)端造成一(yi)種(zhong)程(cheng)(cheng)序已經運行完畢的(de)假象,之后(hou)的(de)所有工(gong)作都在(zai)(zai)(zai)子進(jin)(jin)程(cheng)(cheng)中完成,而用(yong)戶在(zai)(zai)(zai)shell終(zhong)端則可(ke)以(yi)執行其他的(de)命令,從(cong)而在(zai)(zai)(zai)形式上做到與(yu)控制(zhi)終(zhong)端的(de)脫離(li)。  到(dao)這(zhe)里(li),有(you)心的(de)(de)讀(du)者可能(neng)會(hui)問,父進(jin)程(cheng)創建了子(zi)進(jin)程(cheng)后退出(chu),此時(shi)該子(zi)進(jin)程(cheng)不就(jiu)(jiu)沒(mei)有(you)父進(jin)程(cheng)了嗎?守護進(jin)程(cheng)中確(que)實會(hui)出(chu)現這(zhe)么(me)一個有(you)趣的(de)(de)現象:由(you)于(yu)父進(jin)程(cheng)已經先于(yu)子(zi)進(jin)程(cheng)退出(chu),就(jiu)(jiu)會(hui)造成(cheng)子(zi)進(jin)程(cheng)沒(mei)有(you)父進(jin)程(cheng),從而變成(cheng)一個孤兒進(jin)程(cheng)。在Linux中,每當系統發(fa)現一個孤兒進(jin)程(cheng)時(shi),就(jiu)(jiu)會(hui)自動(dong)由(you)1號進(jin)程(cheng)(也(ye)就(jiu)(jiu)是init進(jin)程(cheng))收(shou)養它,這(zhe)樣,原先的(de)(de)子(zi)進(jin)程(cheng)就(jiu)(jiu)會(hui)變成(cheng)init進(jin)程(cheng)的(de)(de)子(zi)進(jin)程(cheng)。其關鍵代(dai)碼如下:     pid = fork(); (2)在子進程中(zhong)創建(jian)(jian)新(xin)會話。這個步(bu)驟是創建(jian)(jian)守護進程重(zhong)(zhong)要的一步(bu),雖(sui)然實(shi)現非常簡單,但意義卻(que)非常重(zhong)(zhong)大。在這里使(shi)用(yong)的是系(xi)統函數setsid(),在具(ju)體介(jie)紹setsid()之前,讀者首先要了解兩個概念:進程組(zu)和會話期。 ● 進程組。進程組是(shi)一(yi)(yi)個或多個進程的集(ji)合。進程組由進程組ID來唯一(yi)(yi)標識(shi)。除了進程號(hao)(PID)之外(wai),進程組ID也是(shi)一(yi)(yi)個進程的必備屬性。 每個(ge)進(jin)(jin)程(cheng)(cheng)組(zu)都有一個(ge)組(zu)長進(jin)(jin)程(cheng)(cheng),其組(zu)長進(jin)(jin)程(cheng)(cheng)的(de)進(jin)(jin)程(cheng)(cheng)號等于進(jin)(jin)程(cheng)(cheng)組(zu)ID,且該進(jin)(jin)程(cheng)(cheng)ID不會因組(zu)長進(jin)(jin)程(cheng)(cheng)的(de)退出而(er)受到影響。 ● 會(hui)話期。會(hui)話組(zu)是一(yi)個或(huo)多(duo)個進(jin)程(cheng)組(zu)的集合。通常,一(yi)個會(hui)話開始于用戶(hu)(hu)登錄,終(zhong)止于用戶(hu)(hu)退出(chu),在此期間(jian)該(gai)用戶(hu)(hu)運(yun)行的所(suo)有進(jin)程(cheng)都屬于這個會(hui)話期。進(jin)程(cheng)組(zu)和(he)會(hui)話期之間(jian)的關系如圖1所(suo)示。 
 接下來就可以具體介(jie)紹setsid()的相關內容。     ① setsid()函數的作用。setsid()函數用于創建一個新的會話組,并擔任該會話組的組長。調用setsid()有以下3個作用: 那(nei)么(me),在(zai)創(chuang)建守(shou)護(hu)進(jin)(jin)程(cheng)(cheng)(cheng)時為什么(me)要調用setsid()函數呢?讀者可以回憶一(yi)下創(chuang)建守(shou)護(hu)進(jin)(jin)程(cheng)(cheng)(cheng)的(de)第一(yi)步(bu),在(zai)那(nei)里(li)調用了fork()函數來(lai)創(chuang)建子進(jin)(jin)程(cheng)(cheng)(cheng)再令父進(jin)(jin)程(cheng)(cheng)(cheng)退出(chu)(chu)(chu)。由于在(zai)調用fork()函數時,子進(jin)(jin)程(cheng)(cheng)(cheng)全(quan)盤復制(zhi)了父進(jin)(jin)程(cheng)(cheng)(cheng)的(de)會話(hua)(hua)期、進(jin)(jin)程(cheng)(cheng)(cheng)組(zu)(zu)和控制(zhi)終端等(deng)(deng),雖然父進(jin)(jin)程(cheng)(cheng)(cheng)退出(chu)(chu)(chu)了,但原先的(de)會話(hua)(hua)期、進(jin)(jin)程(cheng)(cheng)(cheng)組(zu)(zu)和控制(zhi)終端等(deng)(deng)并沒有改變(bian),因此,還不是真正(zheng)意義上的(de)獨立。而setsid()函數能夠使進(jin)(jin)程(cheng)(cheng)(cheng)完全(quan)獨立出(chu)(chu)(chu)來(lai),從而脫離所(suo)有其他進(jin)(jin)程(cheng)(cheng)(cheng)的(de)控制(zhi)。 ② setsid()函(han)數(shu)(shu)格式。表1列出了setsid()函(han)數(shu)(shu)的(de)語法要點。 表1 setsid()函數語法要點 
 (3)改變當前(qian)目(mu)(mu)錄(lu)(lu)為根目(mu)(mu)錄(lu)(lu)。這一(yi)步(bu)也是必(bi)要(yao)的(de)步(bu)驟(zou)。使(shi)用fork()創建的(de)子進程(cheng)繼承(cheng)了父進程(cheng)的(de)當前(qian)工(gong)作目(mu)(mu)錄(lu)(lu)。由于在進程(cheng)運行(xing)過程(cheng)中(zhong),當前(qian)目(mu)(mu)錄(lu)(lu)所在的(de)文件系(xi)統(如“/mnt/usb”等)是不能卸載的(de),這對以(yi)后的(de)使(shi)用會造成諸多的(de)麻煩(如系(xi)統由于某種原因要(yao)進入單用戶模式)。因此,通常(chang)(chang)的(de)做法是讓(rang)“/”作為守護進程(cheng)的(de)當前(qian)工(gong)作目(mu)(mu)錄(lu)(lu),這樣就(jiu)可以(yi)避免上(shang)述問題。當然,如有特(te)殊需要(yao),也可以(yi)把當前(qian)工(gong)作目(mu)(mu)錄(lu)(lu)換成其他的(de)路徑(jing),如/tmp。改變工(gong)作目(mu)(mu)錄(lu)(lu)的(de)常(chang)(chang)見函數是chdir()。 (4)重(zhong)設(she)(she)文(wen)(wen)件(jian)(jian)(jian)權(quan)(quan)限(xian)掩(yan)碼(ma)。文(wen)(wen)件(jian)(jian)(jian)權(quan)(quan)限(xian)掩(yan)碼(ma)是(shi)指屏蔽掉文(wen)(wen)件(jian)(jian)(jian)權(quan)(quan)限(xian)中的(de)(de)對應位(wei)。例如,有一(yi)個(ge)文(wen)(wen)件(jian)(jian)(jian)權(quan)(quan)限(xian)掩(yan)碼(ma)是(shi)050,它就屏蔽了文(wen)(wen)件(jian)(jian)(jian)組擁有者的(de)(de)可(ke)讀與可(ke)執行權(quan)(quan)限(xian)。由(you)于(yu)使用(yong)fork()函數新(xin)建的(de)(de)子(zi)(zi)進(jin)程(cheng)繼承了父進(jin)程(cheng)的(de)(de)文(wen)(wen)件(jian)(jian)(jian)權(quan)(quan)限(xian)掩(yan)碼(ma),這就給(gei)該子(zi)(zi)進(jin)程(cheng)使用(yong)文(wen)(wen)件(jian)(jian)(jian)帶來了諸(zhu)多(duo)的(de)(de)麻煩。因此,把文(wen)(wen)件(jian)(jian)(jian)權(quan)(quan)限(xian)掩(yan)碼(ma)設(she)(she)置為(wei)0,可(ke)以(yi)大大增強該守護進(jin)程(cheng)的(de)(de)靈活性。設(she)(she)置文(wen)(wen)件(jian)(jian)(jian)權(quan)(quan)限(xian)掩(yan)碼(ma)的(de)(de)函數是(shi)umask()。在(zai)這里,通常的(de)(de)使用(yong)方法為(wei)umask(0)。 (5)關(guan)閉文件描述符。同文件權限掩碼(ma)一樣,用fork()函數新建的(de)子進程(cheng)會從父進程(cheng)那里繼承一些已經打(da)開(kai)的(de)文件。這些被(bei)(bei)打(da)開(kai)的(de)文件可能(neng)永遠不會被(bei)(bei)守護進程(cheng)讀或寫,但它(ta)們(men)一樣消(xiao)耗(hao)系(xi)統資源,而(er)且(qie)可能(neng)導致所在的(de)文件系(xi)統無法被(bei)(bei)卸載。 在(zai)上面的(de)(de)(de)第(2)步之后,守護(hu)進程(cheng)已(yi)(yi)經(jing)與所屬(shu)的(de)(de)(de)控制(zhi)終(zhong)端(duan)(duan)失去了聯系,因此,從(cong)終(zhong)端(duan)(duan)輸入的(de)(de)(de)字符(fu)不可能達到守護(hu)進程(cheng),守護(hu)進程(cheng)中用常規方(fang)法(如(ru)printf())輸出的(de)(de)(de)字符(fu)也不可能在(zai)終(zhong)端(duan)(duan)上顯示出來。所以,文(wen)(wen)件描述符(fu)為0、1和(he)2的(de)(de)(de)3個文(wen)(wen)件(常說的(de)(de)(de)輸入、輸出和(he)報錯這(zhe)3個文(wen)(wen)件)已(yi)(yi)經(jing)失去了存在(zai)的(de)(de)(de)價值,也應被關閉。通常按如(ru)下方(fang)式關閉文(wen)(wen)件描述符(fu):     for(i = 0; i < MAXFILE; i++) 這(zhe)樣,一(yi)個簡單的(de)(de)守護(hu)進程就建立(li)起來了。創(chuang)建守護(hu)進程的(de)(de)流程圖如(ru)圖2所(suo)示。 
  下面是實現守護(hu)進(jin)程(cheng)的一個完整實例,該(gai)(gai)實例首先按照以上的創建(jian)流程(cheng)建(jian)立了(le)一個守護(hu)進(jin)程(cheng),然后讓該(gai)(gai)守護(hu)進(jin)程(cheng)每隔(ge)10s向日志(zhi)文件/tmp/daemon.log寫入一句話(hua)。     /* daemon.c 創建守護進程實例 */ 將該(gai)程序(xu)下(xia)載到(dao)開發板上,可以(yi)看(kan)到(dao)該(gai)程序(xu)每隔10s就會在對應的(de)文件中輸入(ru)相關內容,并且使用ps可以(yi)看(kan)到(dao)該(gai)進(jin)程在后(hou)臺(tai)運(yun)行,結果如下(xia):     $ tail -f /tmp/daemon.log 3.守護進程的出錯處理 讀者在(zai)前(qian)面(mian)編(bian)寫守(shou)護進(jin)(jin)程的(de)(de)具體調(diao)(diao)(diao)試過程中(zhong)會發(fa)現,由(you)于守(shou)護進(jin)(jin)程完全(quan)脫離了(le)控制終(zhong)端(duan),因此(ci),不(bu)能(neng)像(xiang)其他普通進(jin)(jin)程一樣將錯(cuo)誤信(xin)(xin)息(xi)輸(shu)出到(dao)控制終(zhong)端(duan)來通知程序(xu)員,即使(shi)使(shi)用(yong)gdb也無(wu)法(fa)正(zheng)常調(diao)(diao)(diao)試。那么,守(shou)護進(jin)(jin)程的(de)(de)進(jin)(jin)程要如何(he)調(diao)(diao)(diao)試呢(ni)?一種通用(yong)的(de)(de)辦法(fa)是(shi)使(shi)用(yong)syslog服(fu)務(wu),將程序(xu)中(zhong)的(de)(de)出錯(cuo)信(xin)(xin)息(xi)輸(shu)入到(dao)系(xi)統(tong)日(ri)(ri)志(zhi)文件(jian)中(zhong)(如“/var/log/messages”),從而可(ke)以直觀地(di)看到(dao)程序(xu)的(de)(de)問(wen)題所(suo)在(zai)(“/var/log/message”系(xi)統(tong)日(ri)(ri)志(zhi)文件(jian)只能(neng)由(you)擁有(you)root權限的(de)(de)超級用(yong)戶查看。在(zai)不(bu)同(tong)Linux發(fa)行版本(ben)中(zhong),系(xi)統(tong)日(ri)(ri)志(zhi)文件(jian)路徑全(quan)名可(ke)能(neng)有(you)所(suo)不(bu)同(tong),例如,可(ke)能(neng)是(shi)“/var/log/syslog”)。 syslog是Linux中(zhong)的系(xi)統日志(zhi)管理(li)服務,通過守護進程syslogd來維護。該(gai)守護進程在啟動時會讀一個(ge)配置文(wen)(wen)件(jian)“/etc/syslog.conf”,該(gai)文(wen)(wen)件(jian)決定(ding)了不(bu)同種類的消息會發送(song)到何處。例如,緊急消息可(ke)被送(song)到系(xi)統管理(li)員并在控(kong)制臺上顯示,而警告消息則可(ke)被記(ji)錄到一個(ge)文(wen)(wen)件(jian)中(zhong)。 該機(ji)制提供了3個(ge)syslog相關(guan)函數(shu),分別為openlog()、syslog()和closelog(),下面就分別介紹這3個(ge)函數(shu)。 1)syslog相關函數說明 通常,openlog()函數用(yong)(yong)于(yu)打開(kai)系統日志服務的一個鏈(lian)(lian)接(jie);syslog()函數用(yong)(yong)于(yu)向(xiang)日志文(wen)件中寫入消(xiao)息(xi),在這(zhe)里(li)可以規定(ding)消(xiao)息(xi)的優先級(ji)、消(xiao)息(xi)輸出格(ge)式等;closelog()函數用(yong)(yong)于(yu)關閉系統日志服務的鏈(lian)(lian)接(jie)。 2)syslog相關函數格式   表2列出了(le)openlog()函數(shu)的語法(fa)要點。 表2 openlog()函數(shu)語(yu)法要點 
   表3列(lie)出了(le)syslog()函數(shu)的語法要點。 表3 syslog()函數語法要點 
 表3.4列(lie)出(chu)了(le)closelog()函數的語法要(yao)點。 表3.4 closelog()函數(shu)語法(fa)要點 
 3)使用實例 這里將上(shang)一個(ge)示(shi)(shi)例程序(xu)用syslog服務(wu)進行重寫,其中有區別的地方用加粗(cu)的字體表示(shi)(shi),源(yuan)代(dai)碼如下(xia):     /* syslog_daemon.c利用syslog服務的守護進程實例 */   讀者可以(yi)嘗試用(yong)普通用(yong)戶的身份執行此(ci)程序。由于這里的open()函數必須具有root權限,因此(ci),syslog會將錯誤信息寫入到系統日(ri)志文件(如“/var/log/messages”)中,結果如下: Jan 30 18:20:08 localhost daemon_syslog[612]: open 本文選自華清遠見嵌入式培訓教材《從實踐中學嵌入式Linux應用程序開發》 熱點鏈接(jie):  
         1、Linux下多進程編程之exec函數語法及使用實例 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||