1 守護進程中涉及到的基本概念
1.1進程組
1.1.1 進程組基本概念
進程組是一個或多個進程的集合,可以接收來自同一個終端的各種訊號。每運行一個程式或是命令都將產生一個進程組。
每個進程屬於一個進程組,而每個進程組都存在一個領頭進程(或是叫組長進程),一般進程組的第一個進程是領頭進程。領頭進程可以建立一個進程組、建立該組中的進程。領頭進程fork出的子進程也將在該進程組中,一旦子進程執行exec等退出函數就不再屬於該進程組。
進程組的生命週期:從建立開始到最後一個進程離開為止成為進程組的生命週期。組中最後一個進程的離開可以是終止,或加入其他進程。進程組的生命週期與組長進程是否終止無關,只要有一個進程存在,進程組的生命週期就未結束。
1.1.2 相關命令
1、查看所有進程組pid(即領頭進程的pid):ps –A –o pgrp= | sort |uniq
2、查看某個進程的進程組pid:ps –C 進程名 –o pgrp=
3、根據進程名獲得進程pid:pidof 進程名
4、根據進程pid獲得進程名:ps –aux |grep 進程pid
5、擷取某個進程的具體資訊:ps –ef |grep 進程名或進程pid
1.1.3 相關函數
#include <unistd.h>
pid_t getpgrp(); //擷取進程組pid,即進組中的領頭進程的pid,相當於調用getpgid(0)
pid_t getpid(); //擷取當前進程pid
pid_t getppid(); //擷取當前進程的父pid
pid_t getpgid(); //返回指定進程的進程組pid
int setpgid(pid_t pid, pid_t pgid); /*將pid進程的進程組id設定為pgid,如果兩個參數相等,
*則pid指定的進程變成進程組組長。如果pid是0,則進程
*組id為pgid,如果pgid是0,則進程組id為pid。*/
int setpgrp(); //將當前進程所屬的進程組的pid設定為當前進程的pid,相當於setpgid(0, 0)
1.2會話
1.2.1 會話基本概念
一次登入形成一個會話,一個會話可包括多個進程組(www.linuxidc.com一個前台進程組和多個後台進程組),但只能有一個前台進程組。
1.2.2 相關函數
#include <unistd.h>
pid_t setsid();
當調用該函數的進程不是進程組的領頭進程時,該函數才能建立新的會話。函數調用成功後,調用進程成為新會話的領頭進程和新進程的領頭進程,同時進程失去控制終端。
1.3控制終端
進程組中有領頭進程,類似地,會話中也對應有領頭進程。會話的領頭進程開啟一個終端之後,該終端就成為會話的控制終端,與控制終端建立串連的會話的領頭進程成為控制進程(session leader)。一個會話只能有一個控制終端,同時一個控制終端只能一個會話。 產生在控制端上的輸入和訊號(比如按下ctrl+c就會產生SIGINT訊號)將發送給會話的前台進程組中的所有進程。
2守護進程及其特性
1、守護進程最重要的特性是後台運行;
2、守護進程必須與其運行前的環境脫離開來。這些環境包括未關閉的檔案描述符,控制終端,會話和進程組,工作目錄等,這些環境通常是從執行它的父進程(特別是Shell)中繼承下來的。
3、守護進程可以在Linux系統啟動時從指令碼/etc/rc.d中啟動,可以由作業規划進程crond啟動,還可以由使用者終端(通常是Shell)執行。
3 守護進程編程
針對守護進程的特性可以將普通進程改造為守護進程。
3.1後台運行特性
讓daemon在子進程中運行。
if( pid=fork() )
{
exit(0); //父進程結束,子進程繼續
}
3.2脫離環境
3.2.1脫離進程組、登入工作階段和控制終端
進程組、登入工作階段和控制終端通常都是從父進程繼承下來的,為了不受它們的影響,控制終端必須將其擺脫。具體方法是調用setsid函數。
通過if( pid=fork() ){exit(0);}將產生了子進程,能保證調用setsid的進程不是進程組中的領頭進程。當setsid調用成功後,該子進程成為新會話中的領頭進程和進程組的領頭進程,並脫離了原來的會話、進程組和控制終端。
3.2.2禁止進程開啟控制終端
if( pid=fork() )
{
exit(0); //該子進程結束,又產生子進程
}
結束該子進程,產生新的子進程,此時的新子進程不是會話領頭進程,所以不能開啟控制終端。通過這種方式就能禁止進程開啟控制終端。
3.2.3脫離開啟的檔案描述符
進程從其父進程繼承了開啟的檔案描述符。如不關閉,將會造成系統資源的浪費,同時,造成進程所在的檔案系統無法卸載以及引起其他無法預料的錯誤。
max_fd = sysconf(_SC_OPEN_MAX);
for(i = 0; i < max_fd; i++)
{
close(i);
}
3.2.4脫離當前工作目錄
進程活動時,其工作目錄所在的檔案系統不能卸載,一般需要將工作目錄切換到根目錄。對於需要作業記錄的進程將工作目錄改變到特定的目錄。
chdir(“/”);
3.2.5重設檔案的許可權掩碼
進程從其父進程繼承的檔案的許可權掩碼,可能會修改守護進程所建立的檔案許可權,需要將掩碼清楚。
umask(0);
3.2.6處理SIGCHLD訊號
處理SIGCHLD訊號並不是必須的。但對於某些進程,www.linuxidc.com 特別是伺服器處理序往往在請求到來時產生子進程處理請求。如果父進程沒有等待(沒有調用wait或waitpid)子進程的結束,子進程將成為殭屍進程(Zombie Process)從而佔用系統資源。而如果父進程等待子進程結束,又將增加父進程的負擔,影響伺服器處理序的並發效能。在Linux可以將SIGCHLD設定為SIG_IGN,這樣核心在子進程結束時不會產生殭屍進程。
signal(SIGCHLD, SIG_IGN); //屏蔽SIGCHLD訊號
這是常用於並發伺服器提升效能的一個技巧。當伺服器處理序沒有調用wait去清理子進程而產生殭屍子進程時,如果屏蔽了SIGCHLD訊號,核心會把殭屍子進程轉交給init進程處理。
4 守護進程樣本
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
void init_daemon()
{
int pid;
int i;
int max_fd;
if(pid = fork())
{
exit(0); //父進程結束,子進程繼續
}
else if(pid < 0)
{
exit(1); //fork失敗
}
setsid();
if(pid = fork())
{
exit(0); //該子進程結束,又產生新子進程
}
else if(pid < 0)
{
exit(1); //fork錯誤,退出
}
/* 關閉開啟的檔案描述符*/
max_fd = sysconf(_SC_OPEN_MAX);
for(i = 0; i < max_fd; i++)
{
close(i);
}
/* 切換工作目錄 */
chdir("/tmp");
/* 重設檔案建立掩碼 */
umask(0);
return;
}
int main()
{
FILE *fp = NULL;
signal(SIGCHLD, SIG_IGN);
init_daemon();
while(1)
{
sleep(1);
if((fp = fopen("/mnt/hgfs/Share/unix/test.log", "a")) != NULL)
{
fprintf(fp, "%s\n", "test message");
fclose(fp);
}
}
return 0;
}
編譯:gcc –o daemon daemon.c
運行:./daemon
查看守護進程:ps –ef |grep daemon
查看該精靈產生的檔案:
作者“Orion的部落格”