學習進程就不得不提到一個運行在後臺的特殊進程——守護進程。(也稱精靈進程)。Linux系統啓動時會啓動很多系統服務進程,這些系統服務進程沒有控制終端,不能直接和用戶交互。其它進程都是在用戶登錄或運行程序時創建,在運行結束或用戶註銷時終止,但系統服務進程不受用戶登錄註銷的影響,它們一直在運行着而我們就將這種進程稱爲守護進程。
守護進程獨立於控制終端並週期性的執行某種任務或者等待處理某些發生的事件。守護進程是一種很有用的進程。Linux的大多數服務器就是用1守護進程實現的,比如,Internet服務器inetd、Web服務器httpd等。此外,守護進程還可以完成許多系統任務,比如作業規劃進程crond等。
可以使用ps axj命令查看系統中的進程。
(由於太多隻截取了一部分)
還可以使用相關參數來有選擇性的查看。參數a表示不僅列當前用戶的進程,也列出所有其他用戶的進程,參數x表示不僅列有控制終端的進程,也列出所有無控制終端的進程,參數j表示列出與作業控制相關的信息。
使用參數x來進行查看。
瞭解了守護進程的概念之後,自己也可以來寫一個守護進程。
創建守護進程最關鍵的一步是調用setsid函數創建一個新的Session,併成爲Session Leader。
在調用這個函數之前,當前進程不允許是進程組的Leader,否則該函數返回-1。要保證當前進程不是進程組的Leader只要先fork再調用setsid就行了。fork創建的子進程和父進程在同一個進程組中,進程組Leader必然是該組的第一個進程,所以子進程不可能是該組的第一個進程,在子 進程中調用setsid就不會有問題了。
創建守護進程可以按照如下步驟進行:
1、 調用umask函數將文件模式創建屏蔽字設置爲0;
2、調用fork,父進程退出(exit);
3、調用setsid創建一個新會話;
4.、將當前工作目錄更改爲根目錄;
5、 關閉不在需要的文件描述符;
6.、其他:忽略SIGCHLD信號。
#include <stdio.h> #include <signal.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h> void creat_daemon() { int i; int fd0; pid_t pid; struct sigaction sa; umask(0); if( (pid = fork()) < 0 ) { } else if (pid != 0) { exit(0); //終止父進程 } setsid(); //設置新會話 sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if( sigaction(SIGCHLD, &sa, NULL ) < 0 ) { return; } if( (pid = fork())<0) {printf("fork error!\n"); return; } else if( pid != 0) { exit(0); } if( chdir("/") < 0 ) {printf("child dir error\n"); return; } close(0); fd0 = open("/dev/null", O_RDWR); dup2(fd0, 1); dup2(fd0, 2); } int main() { creat_daemon(); while(1) { sleep(1); } }
可以看到有的人創建時調用了一次fork(),而有的人則調用了兩次fork()。這是什麼原因呢?
第一次fork的作用是讓shell認爲本條命令已經終止,不用掛在終端輸入上。還有一個作用是爲後面setsid服務。setsid的調用者不能是進程組組長(group leader),此時父進程是進程組組長。
第二次的作用是:防止進程再次打開一個控制終端。因爲打開一個控制終端的前臺條件是該進程必須是會話組長。再fork一次,子進程ID != sid(sid是進程父進程的sid)。所以也無法打開新的控制終端。因此,第二次fork不是必須的。但是爲了安全起見,調用兩次是比較保險的。