詳解守護(hu)進程的(de)創建(jian)與(yu)fork兩次分析
時間:2018-09-27 來源:未知(zhi)
相信大部分程序員都知道(dao)(dao)如何去創建一個(ge)守(shou)護(deamon)進程,但是另(ling)一方面(mian),有許多(duo)人不知道(dao)(dao)為什(shen)么要這么做(zuo),具(ju)體為什(shen)么這么實現(xian)。這里(li)我們(men)就來詳細分析一下(xia)創建deamon進程每(mei)一步(bu)的意義。
本文引用地址://fsbing.cn/emb/Column/7509.html
首(shou)先(xian)說一(yi)下deamon進(jin)(jin)(jin)(jin)(jin)程(cheng)(cheng)的(de)(de)(de)(de)(de)(de)(de)概念,deamon是一(yi)種(zhong)運(yun)行(xing)(xing)在(zai)后臺(tai)的(de)(de)(de)(de)(de)(de)(de)一(yi)種(zhong)特殊的(de)(de)(de)(de)(de)(de)(de)進(jin)(jin)(jin)(jin)(jin)程(cheng)(cheng),它(ta)獨立于(yu)控制終(zhong)(zhong)端(duan)并(bing)且周期性的(de)(de)(de)(de)(de)(de)(de)執行(xing)(xing)某(mou)種(zhong)任務(wu)或(huo)(huo)(huo)等待處理某(mou)些發生(sheng)(sheng)的(de)(de)(de)(de)(de)(de)(de)事件(jian)。由于(yu)在(zai)Linux中(zhong),每(mei)個(ge)(ge)(ge)系統與用(yong)戶進(jin)(jin)(jin)(jin)(jin)行(xing)(xing)交流的(de)(de)(de)(de)(de)(de)(de)界面成為(wei)終(zhong)(zhong)端(duan),每(mei)一(yi)個(ge)(ge)(ge)從此終(zhong)(zhong)端(duan)開始運(yun)行(xing)(xing)的(de)(de)(de)(de)(de)(de)(de)進(jin)(jin)(jin)(jin)(jin)程(cheng)(cheng)都(dou)會(hui)(hui)依附(fu)于(yu)這個(ge)(ge)(ge)終(zhong)(zhong)端(duan),這個(ge)(ge)(ge)終(zhong)(zhong)端(duan)被(bei)稱為(wei)這些進(jin)(jin)(jin)(jin)(jin)程(cheng)(cheng)的(de)(de)(de)(de)(de)(de)(de)控制終(zhong)(zhong)端(duan),當控制終(zhong)(zhong)端(duan)被(bei)關閉的(de)(de)(de)(de)(de)(de)(de)時(shi)候(hou),相(xiang)應的(de)(de)(de)(de)(de)(de)(de)進(jin)(jin)(jin)(jin)(jin)程(cheng)(cheng)都(dou)會(hui)(hui)自動關閉。但是守護(hu)(hu)進(jin)(jin)(jin)(jin)(jin)程(cheng)(cheng)卻能突破這種(zhong)限制,它(ta)脫離于(yu)終(zhong)(zhong)端(duan)并(bing)且在(zai)后臺(tai)運(yun)行(xing)(xing),并(bing)且它(ta)脫離終(zhong)(zhong)端(duan)的(de)(de)(de)(de)(de)(de)(de)目的(de)(de)(de)(de)(de)(de)(de)是為(wei)了避免(mian)進(jin)(jin)(jin)(jin)(jin)程(cheng)(cheng)在(zai)運(yun)行(xing)(xing)的(de)(de)(de)(de)(de)(de)(de)過程(cheng)(cheng)中(zhong)的(de)(de)(de)(de)(de)(de)(de)信(xin)息(xi)在(zai)任何(he)終(zhong)(zhong)端(duan)中(zhong)顯示并(bing)且進(jin)(jin)(jin)(jin)(jin)程(cheng)(cheng)也不會(hui)(hui)被(bei)任何(he)終(zhong)(zhong)端(duan)所產生(sheng)(sheng)的(de)(de)(de)(de)(de)(de)(de)終(zhong)(zhong)端(duan)信(xin)息(xi)所打斷。它(ta)從被(bei)執行(xing)(xing)的(de)(de)(de)(de)(de)(de)(de)時(shi)候(hou)開始運(yun)轉,直到(dao)整個(ge)(ge)(ge)系統關閉才退出(當然可以人(ren)為(wei)的(de)(de)(de)(de)(de)(de)(de)殺死相(xiang)應的(de)(de)(de)(de)(de)(de)(de)守護(hu)(hu)進(jin)(jin)(jin)(jin)(jin)程(cheng)(cheng))。如果(guo)想(xiang)讓某(mou)個(ge)(ge)(ge)進(jin)(jin)(jin)(jin)(jin)程(cheng)(cheng)不因為(wei)用(yong)戶或(huo)(huo)(huo)中(zhong)斷或(huo)(huo)(huo)其他變化而(er)影響,那么就必須把(ba)這個(ge)(ge)(ge)進(jin)(jin)(jin)(jin)(jin)程(cheng)(cheng)變成一(yi)個(ge)(ge)(ge)守護(hu)(hu)進(jin)(jin)(jin)(jin)(jin)程(cheng)(cheng)。
守護進程的創建步驟:
1、創建子進程,父進程退出(chu)。由(you)于(yu)守護(hu)進(jin)程(cheng)是(shi)脫(tuo)離終端(duan)(duan)的(de),因此完(wan)成(cheng)第一(yi)步后就會在shell終端(duan)(duan)里(li)(li)造成(cheng)一(yi)個(ge)程(cheng)序已經運(yun)行完(wan)畢(bi)的(de)假象。之后的(de)所有工(gong)作在子(zi)進(jin)程(cheng)中完(wan)成(cheng),而用(yong)戶在shell終端(duan)(duan)里(li)(li)則可以(yi)執行其他命令,從而在形(xing)式上做(zuo)到了與控制終端(duan)(duan)脫(tuo)離。實(shi)現的(de)語句如下(xia):if(pid=fork()){exit(0);}是(shi)父(fu)進(jin)程(cheng)就結束(shu),然后子(zi)進(jin)程(cheng)繼(ji)續(xu)執行。
2、在子進程(cheng)中創建(jian)新(xin)的會話(hua)(脫離(li)控制終端)。在(zai)這里(li)使用(yong)的是系統函(han)數setsid()來創建一個新的會(hui)話(hua),并且擔任該會(hui)話(hua)組的組長(chang),擺(bai)脫原會(hui)話(hua)的控制(zhi)(zhi)==》擺(bai)脫原進程的控制(zhi)(zhi)==》擺(bai)脫原控制(zhi)(zhi)終(zhong)端(duan)的控制(zhi)(zhi)。
3、改(gai)變當(dang)前(qian)目(mu)錄為根目(mu)錄。使(shi)用fork()創建的(de)(de)(de)(de)(de)(de)子進程是繼承了(le)父進程的(de)(de)(de)(de)(de)(de)當(dang)(dang)前(qian)工(gong)作目(mu)錄,由于在進程運行中,當(dang)(dang)前(qian)目(mu)錄所在的(de)(de)(de)(de)(de)(de)文件系統是不能卸載的(de)(de)(de)(de)(de)(de),這(zhe)對以(yi)后使(shi)用會(hui)造成很多的(de)(de)(de)(de)(de)(de)麻煩。因此(ci)通常的(de)(de)(de)(de)(de)(de)做(zuo)法是讓“/”作為(wei)守護進程的(de)(de)(de)(de)(de)(de)當(dang)(dang)前(qian)目(mu)錄,當(dang)(dang)然也可以(yi)指(zhi)定(ding)其他的(de)(de)(de)(de)(de)(de)別的(de)(de)(de)(de)(de)(de)目(mu)錄來作為(wei)守護進程的(de)(de)(de)(de)(de)(de)工(gong)作目(mu)錄。
4、重設文(wen)件權限掩碼。文(wen)(wen)件(jian)(jian)權(quan)(quan)限掩碼(ma)是(shi)屏(ping)蔽掉(diao)文(wen)(wen)件(jian)(jian)權(quan)(quan)限中(zhong)的對應位。由于使用(yong)fork()函(han)數(shu)新(xin)創(chuang)建(jian)的子進(jin)(jin)程(cheng)(cheng)(cheng)繼承(cheng)了父(fu)進(jin)(jin)程(cheng)(cheng)(cheng)的文(wen)(wen)件(jian)(jian)權(quan)(quan)限掩碼(ma),這(zhe)就給該子進(jin)(jin)程(cheng)(cheng)(cheng)使用(yong)文(wen)(wen)件(jian)(jian)帶了很多的麻煩(比如(ru)父(fu)進(jin)(jin)程(cheng)(cheng)(cheng)中(zhong)的文(wen)(wen)件(jian)(jian)沒有(you)執(zhi)行文(wen)(wen)件(jian)(jian)的權(quan)(quan)限,然而在(zai)子進(jin)(jin)程(cheng)(cheng)(cheng)中(zhong)希望(wang)執(zhi)行相(xiang)應的文(wen)(wen)件(jian)(jian)這(zhe)個時(shi)候就會出問題)。因(yin)此在(zai)子進(jin)(jin)程(cheng)(cheng)(cheng)中(zhong)要(yao)把(ba)文(wen)(wen)件(jian)(jian)的權(quan)(quan)限掩碼(ma)設(she)置成(cheng)為0,即在(zai)此時(shi)有(you)大(da)(da)的權(quan)(quan)限,這(zhe)樣(yang)可以大(da)(da)大(da)(da)增強(qiang)該守護(hu)進(jin)(jin)程(cheng)(cheng)(cheng)的靈(ling)活性(xing)。設(she)置的方法(fa)是(shi):umask(0)。
5、關閉文件描述符。同文(wen)(wen)件權(quan)限碼一樣,用(yong)fork()函數(shu)新建(jian)的(de)(de)子進程(cheng)(cheng)會(hui)從父進程(cheng)(cheng)那里(li)繼承一些已經打(da)開了的(de)(de)文(wen)(wen)件。這些文(wen)(wen)件被打(da)開的(de)(de)文(wen)(wen)件可能永遠不會(hui)被守護進程(cheng)(cheng)讀寫,如(ru)果不進行(xing)關閉(bi)的(de)(de)話將(jiang)會(hui)浪費系(xi)統的(de)(de)資源,造成(cheng)進程(cheng)(cheng)所(suo)在的(de)(de)文(wen)(wen)件系(xi)統無法卸(xie)下(xia)(xia)以及(ji)引(yin)起預料的(de)(de)錯誤。按照(zhao)如(ru)下(xia)(xia)方法關閉(bi)它們:
fdtablesize = getdtablesize();
for (fd = 0; fd < fdtablesize; fd++)
close(fd);
我們來看一下代碼:
int main(int argc, const char *argv[])
{
pid_t pid;
pid = fork();
if(pid < 0)
{
perror("fail to fork");
exit(0);
}else if(pid > 0)
{
exit(0);
}else{
setsid();
umask(0);
pid = fork();
if(pid != 0)
{
exit(0);
}
chdir("/");
int maxfd = getdtablesize();
while(maxfd--)
{
close(maxfd);
}
while(1)
{
syslog(LOG_INFO,"im deamon\n");
sleep(1);
}
}
return 0;
}
可以看(kan)到(dao)上面的代碼里我fork了兩次,雖然(ran)說這并(bing)不是必須(xu)的,但是這的確是對(dui)守(shou)護進程做出了一(yi)些更優化(hua)的操作。
首先第一次fork:這(zhe)里(li)第一次fork的作用(yong)(yong)(yong)就(jiu)是(shi)(shi)讓shell認(ren)為(wei)這(zhe)條(tiao)命令已(yi)經終止,不(bu)用(yong)(yong)(yong)掛在終端(duan)輸(shu)入上;再一個(ge)是(shi)(shi)為(wei)了(le)后面的setsid服(fu)務,因(yin)為(wei)調(diao)用(yong)(yong)(yong)setsid函數的進程(cheng)(cheng)不(bu)能(neng)是(shi)(shi)進程(cheng)(cheng)組(zu)組(zu)長(會(hui)報(bao)錯Operation not permitted),如果不(bu)fork子(zi)進程(cheng)(cheng),那么此時的父進程(cheng)(cheng)是(shi)(shi)進程(cheng)(cheng)組(zu)組(zu)長,無(wu)法調(diao)用(yong)(yong)(yong)setsid。所(suo)以到(dao)這(zhe)里(li)子(zi)進程(cheng)(cheng)便成為(wei)了(le)一個(ge)新(xin)會(hui)話(hua)組(zu)的組(zu)長。
第二次fork:第(di)二次(ci)fork是為了(le)避免后期進(jin)(jin)程誤操作(zuo)而(er)(er)再(zai)次(ci)打開(kai)終(zhong)(zhong)端(duan)。因為打開(kai)一個控制終(zhong)(zhong)端(duan)的前(qian)提條件是該進(jin)(jin)程必(bi)須為會(hui)話(hua)組(zu)組(zu)長,而(er)(er)我(wo)們(men)通過第(di)二次(ci)fork,確保了(le)第(di)二次(ci)fork出來的子進(jin)(jin)程不會(hui)是會(hui)話(hua)組(zu)組(zu)長。

