守護進程也稱精靈進程(Daemon),是運行在後臺的一種特殊進程。它獨立於控制終端並且週期性地執行任務或等待處理某些發生的事件。守護進程是一種很有用的進程。
Linux的大多數服務器就是守護進程實現的。比如,Internet服務器inetd,Web服務器httpd等。同時,守護進程完成許多系統任務。比如,作業規劃進程crond等。
Linux系統啓動時會啓動許多系統服務器進程,這些系統服務進程沒有控制終端,不能直接和用戶交互。其他進程都是在用戶登錄或運行程序時創建,在運行結束或用戶註銷時終
止,但系統服務進程不受用戶登錄註銷的影響,他們一直運行着。這種進程有一個名稱叫做守護進程(Daemon)。
下面我們用ps axj命令查看系統中的進程。參數a表示不僅列出當前用戶的進程,也列出所有其他用戶的進程,參數x表述不僅列有控制終端的進程,也列出所有無控制終端的
進程,參數j表示列出與作業控制相關的信息。
凡是TCGIP一欄寫着-1的都是沒有控制終端的進程,即守護進程。在COMMAND一列用括號括起來的名字表示內核線程,這些線程在內核裏創建,沒有用戶空間代碼。因
此沒有程序文件名和命令行,通常採用以K開頭的名字,表示Kernel.init進程是第一個運行的進程,udevd負責維護/dev下的設備文件,acpid負責電源管理,syslog負責維
護/var/log下的日誌文件,可以看出,守護進程常採用以d結尾的名字,表示Daemon。
二、創建守護進程:
創建一個守護進程大概分爲六部:
1、調用umask將文件模式創建屏蔽字設置爲0.
2、調用fork,父進程退出(eait)。原因:1)如果該進程作爲一條簡單的shell命令啓動時,那麼父進程終止使得shell認爲該命令已經執行完畢。2)保證子進程不是一個進程組
的組長進程。
3、調用setsid創建一個回話。setsid會導致:1)調用進程成爲新的會話的首進程。2)調用進程稱爲一個進程組的組長進程。3)調用進程沒有控制終端。(再fork一次,保證daemon進程之後不會打開tty設備)
4、將當前工作目錄改爲根目錄。
5、關閉不再需要的文件描述符。
6、其他:忽略SIGCHLD信號。
三、創建守護進程代碼如下:
1 #include<stdio.h>
2 #include<signal.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5 #include<fcntl.h>
6 #include<sys/stat.h>
7 void creat_daemon(void)
8 {
9 int i;
10 int fd0;
11 pid_t pid;
12 struct sigaction sa;
13 umask(0);
14 if((pid=fork())<0)
15 {}
16 else if(pid!=0)
17 {
18 exit(0);
19 }
20 setsid();
21 sigemptyset(&sa.sa_mask);
22 sa.sa_flags=0;
23 if(sigaction(SIGCHLD,&sa,NULL)<0)
24 {
25 return;
26 }
27 if((pid=fork())<0)
28 {
29 printf("fork error!\n");
30 return;
31 }
32 else if(pid!=0)
33 {
34 exit(0);
35 }
36 if(chdir("/")<0)
37 {
38 printf("child dir error\n");
39 return;
40 }
41 close(0);
42 fd0=open("/dev/null",O_RDWR);
43 dup2(fd0,1);
44 dup2(fd0,2);
45 }
46 int main()
47 {
48 creat_daemon();
49 while(1)
50 {
51 sleep(1);
52 }
53 }
四、爲什麼創建守護進程有人要fork兩次創建守護進程最關鍵的一步是調用setsid函數創建一個新的會話(Session),併成爲Session Leader。
#include<unistd.h>
pid_t setsid(void);
該函數調用成功時返回新創建的Session的id(是當前進程的id),出錯返回-1,注意,調用這個函數之前。當前進程不允許是這個進程組的Leader,否則該函數返回-1。要保
證當前進程不是進程組的Leader也很容易,只要先fork再調用setsid就行了。fork創建的子進程和父進程在同一個進程組中,進程組的Leader必然是該組的第一個進程,所以子
進程不可能是該組進程的第一個進程,在子進程中調用setsid就不會有問題了。
成功調用該函數的結果是:
1、創建一個新的Session,當前進程成爲Session Leader,當前進程的id就是Session的id。
2、創建一個新的進程組,當前進程成爲進程組的id,就是進程組的id。
3、如過當前進程原本有一個控制終端,則它失去這個控制終端,成爲一個沒有控制終端的進程。所謂失去控制終端是指,原來的控制終端仍然是打開的,任然可以讀寫,但只是
一個普通打開文件而不是控制終端了。
session中包含了很多東西,如:控制終端、進程組等等。如果我們只fork()一次,將第一個由父進程創建處的子進程分離就來作爲守護進程,一般情況下也是沒有問題
的。但是,如果此時通過什麼方式讓次進程創建出了一個與自己的Session相關的控制終端,那麼則會產生一定的影響。所以,則有了第二次fork使子進程創建出了一個“孫子進
程”,並且我們還是使用第一個父進程創建出來的子進程進行setsid,但他並不是守護進程,該孫子進程進行進程相關的操作。簡言之,因爲子進程創建了一個新的Session,並
作爲該Session的領頭進程,同時這個Session包含了該孫子進程且他並不是領頭進程。那麼該孫子進程就永遠無法創建出一個控制終端,也就沒有任何影響。