【Linux系统编程】特殊进程之守护进程
00. 目錄
文章目錄
- 00. 目錄
- 01. 守護(hù)進(jìn)程概述
- 02. 守護(hù)進(jìn)程查看方法
- 03. 編寫(xiě)守護(hù)進(jìn)程的步驟
- 04. 守護(hù)進(jìn)程代碼
- 05. 附錄
01. 守護(hù)進(jìn)程概述
守護(hù)進(jìn)程(Daemon Process),也就是通常說(shuō)的 Daemon 進(jìn)程(精靈進(jìn)程),是 Linux 中的后臺(tái)服務(wù)進(jìn)程。它是一個(gè)生存期較長(zhǎng)的進(jìn)程,通常獨(dú)立于控制終端并且周期性地執(zhí)行某種任務(wù)或等待處理某些發(fā)生的事件。
守護(hù)進(jìn)程是個(gè)特殊的孤兒進(jìn)程,這種進(jìn)程脫離終端,為什么要脫離終端呢?之所以脫離于終端是為了避免進(jìn)程被任何終端所產(chǎn)生的信息所打斷,其在執(zhí)行過(guò)程中的信息也不在任何終端上顯示。由于在 Linux 中,每一個(gè)系統(tǒng)與用戶進(jìn)行交流的界面稱為終端,每一個(gè)從此終端開(kāi)始運(yùn)行的進(jìn)程都會(huì)依附于這個(gè)終端,這個(gè)終端就稱為這些進(jìn)程的控制終端,當(dāng)控制終端被關(guān)閉時(shí),相應(yīng)的進(jìn)程都會(huì)自動(dòng)關(guān)閉。
Linux 的大多數(shù)服務(wù)器就是用守護(hù)進(jìn)程實(shí)現(xiàn)的。比如,Internet 服務(wù)器 inetd,Web 服務(wù)器 httpd 等。
02. 守護(hù)進(jìn)程查看方法
在終端輸入ps axj
- a 表示不僅列當(dāng)前用戶的進(jìn)程,也列出所有其他用戶的進(jìn)程
- x 表示不僅列有控制終端的進(jìn)程,也列出所有無(wú)控制終端的進(jìn)程
- j 表示列出與作業(yè)控制相關(guān)的信息
從上面的結(jié)果可以看出守護(hù)進(jìn)程的一些特點(diǎn):
- 守護(hù)進(jìn)程基本上都是以超級(jí)用戶啟動(dòng)( UID 為 0 )
- 沒(méi)有控制終端( TTY 為 ?)
- 終端進(jìn)程組 ID 為 -1 ( TPGID 表示終端進(jìn)程組 ID)
一般情況下,守護(hù)進(jìn)程可以通過(guò)以下方式啟動(dòng):
- 在系統(tǒng)啟動(dòng)時(shí)由啟動(dòng)腳本啟動(dòng),這些啟動(dòng)腳本通常放在 /etc/rc.d 目錄下;
- 利用 inetd 超級(jí)服務(wù)器啟動(dòng),如 telnet 等;
- 由 cron 定時(shí)啟動(dòng)以及在終端用 nohup 啟動(dòng)的進(jìn)程也是守護(hù)進(jìn)程。
03. 編寫(xiě)守護(hù)進(jìn)程的步驟
3.1 屏蔽一些控制終端操作的信號(hào)
這是為了防止守護(hù)進(jìn)行在沒(méi)有運(yùn)行起來(lái)前,控制終端受到干擾退出或掛起。
signal(SIGTTOU,SIG_IGN); signal(SIGTTIN,SIG_IGN); signal(SIGTSTP,SIG_IGN); signal(SIGHUP ,SIG_IGN);3.2 父進(jìn)程退出
守護(hù)進(jìn)程不可以是組長(zhǎng)進(jìn)程。方法是在進(jìn)程中調(diào)用 fork() 使父進(jìn)程終止, 讓守護(hù)進(jìn)行在子進(jìn)程中后臺(tái)執(zhí)行。形式上脫離了控制終端。
if( pid = fork() ){ // 父進(jìn)程exit(0); //結(jié)束父進(jìn)程,子進(jìn)程繼續(xù) }3.3 創(chuàng)建會(huì)話,完全脫離控制終端
有必要先介紹一下 Linux 中的進(jìn)程與控制終端,登錄會(huì)話和進(jìn)程組之間的關(guān)系:進(jìn)程屬于一個(gè)進(jìn)程組,進(jìn)程組號(hào)(GID)就是進(jìn)程組長(zhǎng)的進(jìn)程號(hào)(PID)。登錄會(huì)話可以包含多個(gè)進(jìn)程組。這些進(jìn)程組共享一個(gè)控制終端。這個(gè)控制終端通常是創(chuàng)建進(jìn)程的 shell 登錄終端。 控制終端、登錄會(huì)話和進(jìn)程組通常是從父進(jìn)程繼承下來(lái)的。我們的目的就是要擺脫它們 ,使之不受它們的影響。因此需要調(diào)用 setsid() 使子進(jìn)程成為新的會(huì)話組長(zhǎng),示例代碼如下:
setsid();setsid() 調(diào)用成功后,進(jìn)程成為新的會(huì)話組長(zhǎng)和新的進(jìn)程組長(zhǎng),并與原來(lái)的登錄會(huì)話和進(jìn)程組脫離。由于會(huì)話過(guò)程對(duì)控制終端的獨(dú)占性,進(jìn)程同時(shí)與控制終端脫離。
3.4 關(guān)閉不需要文件描述符
進(jìn)程從創(chuàng)建它的父進(jìn)程那里繼承了打開(kāi)的文件描述符。如不關(guān)閉,將會(huì)浪費(fèi)系統(tǒng)資源,造成進(jìn)程所在的文件系統(tǒng)無(wú)法卸下以及引起無(wú)法預(yù)料的錯(cuò)誤。按如下方法關(guān)閉它們:
3.5 改變當(dāng)前工作目錄
進(jìn)程活動(dòng)時(shí),其工作目錄所在的文件系統(tǒng)不能卸下。一般需要將工作目錄改變到根目錄。對(duì)于需要轉(zhuǎn)儲(chǔ)核心,寫(xiě)運(yùn)行日志的進(jìn)程將工作目錄改變到特定目錄如 /tmp。示例代碼如下:
chdir("/");3.6 設(shè)置權(quán)限掩碼
進(jìn)程從創(chuàng)建它的父進(jìn)程那里繼承了文件創(chuàng)建掩碼。它可能修改守護(hù)進(jìn)程所創(chuàng)建的文件的存取權(quán)限。為防止這一點(diǎn),將文件創(chuàng)建掩碼清除:
umask(0);3.7 處理 SIGCHLD 信號(hào)
但對(duì)于某些進(jìn)程,特別是服務(wù)器進(jìn)程往往在請(qǐng)求到來(lái)時(shí)生成子進(jìn)程處理請(qǐng)求。如果父進(jìn)程不等待子進(jìn)程結(jié)束,子進(jìn)程將成為僵尸進(jìn)程(zombie)從而占用系統(tǒng)資源(關(guān)于僵尸進(jìn)程的更多詳情,請(qǐng)看《特殊進(jìn)程之僵尸進(jìn)程》)。如果父進(jìn)程等待子進(jìn)程結(jié)束,將增加父進(jìn)程的負(fù)擔(dān),影響服務(wù)器進(jìn)程的并發(fā)性能。在 Linux 下可以簡(jiǎn)單地將 SIGCHLD 信號(hào)的操作設(shè)為 SIG_IGN 。
signal(SIGCHLD, SIG_IGN);這樣,內(nèi)核在子進(jìn)程結(jié)束時(shí)不會(huì)產(chǎn)生僵尸進(jìn)程。
04. 守護(hù)進(jìn)程代碼
守護(hù)進(jìn)程參考代碼:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h>int main(void) {int i = 0;pid_t pid = -1;//a. 設(shè)置掩碼umask(0); //b. 創(chuàng)建子進(jìn)程 父進(jìn)程退出pid = fork();if (-1 == pid){perror("fork"); goto err0;}else if (0 != pid){//父進(jìn)程exit(0); }//子進(jìn)程//c. 創(chuàng)建一個(gè)新的會(huì)話setsid();//d. 更改當(dāng)前工作目錄chdir("/"); //e. 關(guān)系不需要的描述符close(0); close(1);close(2);//做事情while(1){system("echo hello world >> /tmp/a"); sleep(1); }return 0; err0:return 1; }測(cè)試結(jié)果:
05. 附錄
5.1 參考博客:【Linux系統(tǒng)編程】特殊進(jìn)程之守護(hù)進(jìn)程
總結(jié)
以上是生活随笔為你收集整理的【Linux系统编程】特殊进程之守护进程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【Linux系统编程】特殊进程之孤儿进程
- 下一篇: 【Linux系统编程】进程替换:exec