學習筆記之守護進程

Linux C編程一站式學習 學習筆記

一,基本概念

Linux 系統啓動時會啓動很多系統服務進程,如inetd,init等,這些系統服務進程沒有控制終端,不能直接和用戶交互。其它進程都是在用戶登錄或運行程序時創建,在運行結束或用戶註銷時終止,但系統服務進程不受用戶登錄註銷的影響,它們一直在運行着。這種進程有一個名稱叫守護進程(Daemon)。

守護進程的三個特點:後臺運行,獨立於終端,完成一定的任務。
首先所謂的後臺運行過程是一般是在圖形界面或是終端不可見的;而獨立於終端是說它不和終端聯繫,運行之後一般不接受終端的輸入也不向終端輸出;而完成一點的任務是每一個守護進程的運行都是爲了完成一定的任務而運行的,這些任務一般都是系統相關的任務。也就是控制檯除開這些特殊性以外,守護進程與普通進程基本上沒有什麼區別。因此,守護進程可以由一個普通進程按照上述的守護進程的特性而改造成爲守護進程。

我們用ps axj命令查看系統中的進程。參數a表示不僅列當前用戶的進程,也列出所有其他用戶的進程,參數x表示不僅列有控制終端的進程,也列出所有無控制終端的進程,參數j表示列出與作業控制相關的信息。

凡是TPGID一欄寫着-1的都是沒有控制終端的進程,也就是守護進程。在COMMAND一列用[]括起來的名字表示內核線程,這些線程在內核裏創建,沒有用戶空間代碼,因此沒有程序文件名和命令行,通常採用以k開頭的名字,表示Kernel。syslogd負責維護/var/log下的日誌文件,可以看出,守護進程通常採用以d結尾的名字,表示Daemon。

二,創建守護進程

先介紹一下Linux中的進程與控制終端,登錄會話和進程組之間的關係:進程屬於一個進程組,進程組號(GID)就是進程Leader的進程號(PID)。登錄會話可以包含多個進程組。這些進程組共享一個控制終端。這個控制終端通常是創建進程的登錄終端。

控制終端,登錄會話和進程組通常是從父進程繼承下來的。我們的目的就是要擺脫它們,使之不受它們的影響。方法是在fork的基礎上,調用setsid()使子進程成爲會話組長:

#include <unistd.h>
pid_t setsid(void);

該函數調用成功時返回新創建的會話的id(其實也就是當前進程的id),出錯返回-1。注意,調用這個函數之前,當前進程不允許是進程組的Leader,否則該函數返回-1。要保證當前進程不是進程組的Leader也很容易,只要先fork再調用setsid就行了。fork創建的子進程和父進程在同一個進程組中,進程組的Leader必然是該組的第一個進程,所以子進程不可能是該組的第一個進程,在子進程中調用setsid就不會有問題了。setsid()調用成功後,進程成爲新的會話組長和新的進程組長,並與原來的登錄會話和進程組脫離。由於會話過程對控制終端的獨佔性,進程同時與控制終端脫離。

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>

void daemonize(void)
{
	pid_t  pid;

	/*
	 * Become a session leader to lose controlling TTY.
	 */
	if ((pid = fork()) < 0) {
		perror("fork");
		exit(1);
	} else if (pid != 0) /* parent */
		exit(0);
	setsid();

	/*
	 * Change the current working directory to the root.
	 */
	if (chdir("/") < 0) {
		perror("chdir");
		exit(1);
	} 

	/*
	 * Attach file descriptors 0, 1, and 2 to /dev/null.
	 */
	close(0);
	open("/dev/null", O_RDWR);
	dup2(0, 1);
	dup2(0, 2);
}

int main(void)
{
	daemonize();
	while(1);
}
按照守護進程的慣例,通常將當前工作目錄切換到根目錄,將文件描述符0、1、2重定向到/dev/null。Linux也提供了一個庫函數daemon(3)實現我們的daemonize函數的功能,它帶兩個參數指示要不要切換工作目錄到根目錄,以及要不要把文件描述符0、1、2重定向到/dev/null

測試如下:

xgx@ubuntu:~/Clan/thread$ ./a.out 
xgx@ubuntu:~/Clan/thread$ ps
  PID TTY          TIME CMD
 7168 pts/2    00:00:02 bash
13463 pts/2    00:00:00 ps
xgx@ubuntu:~/Clan/thread$ ps xj |grep a.out
    1 13462 13462 13462 ?           -1 Rs    1000   0:07 ./a.out
 7168 13467 13466  7168 pts/2    13466 S+    1000   0:00 grep --color=auto a.out




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章