守護進程
守護進程也稱精靈進程,是運行在後臺的一種特殊進程,它獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。
Linux系統啓動時會啓動很多系統服務進程,這些系統服務進程沒有控制終端,不能直接和用戶交互。其它進程都是在用戶登錄或運行程序時創建,在運行結束或用戶註銷時終止,但系統服務進程(守護進程)不受用戶登錄註銷的影響,它們一直在運行着。這種進程有一個名稱叫守護進程(Daemon)。
使用命令查看守護進程
ps axj | more
- 凡是TPGID一欄寫着-1的都是沒有控制終端的進程,也就是守護進程。
- 在COMMAND一列用[]括起來的名字表示內核線程,這些線程在內核裏創建,沒有用戶空間代碼,因此沒有程序文件名和命令行, 通常採用以k開頭的名字,表示Kernel。
- 可以看出,守護進程通常採用以d結尾的名字,表示Daemon。
創建守護進程
創建守護進程最關鍵的一步是調用setsid函數創建一個新的Session,併成爲Session Leader。
#include <unistd.h>
pid_t setsid(void);
該函數調用成功時返回新創建的Session的id(其實也就是當前進程的id),出錯返回-1。
注意,調用這個函數之前,當前進程不允許是進程組的Leader,否則該函數返回-1。要保證當前進程不是進 程組的Leader也很容易,只要先fork再調用setsid就行了。fork創建的子進程和父進程在同一個進程組中,進程組的Leader必然是該組的第一個進程,所以子進程不可能是該組的第一個進程,在子進程中調用setsid就不會有問題了。
成功調用該函數的結果是:
- 創建一個新的Session,當前進程成爲Session Leader,當前進程的id就是Session的id。
- 創建一個新的進程組,當前進程成爲進程組的Leader,當前進程的id就是進程組的id。
- 如果當前進程原本有一個控制終端,則它失去這個控制終端,成爲一個沒有控制終端的進程。所謂失去控制終端是指,原來的控制終端仍然是打開的。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
void mydaemon()
{
int fd0;
pid_t pid;
struct sigaction sa;
umask(0); //設置文件的默認權限
if((pid = fork()) < 0)
{
perror("fork");
exit(1);
}
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) //註冊SIGCHLD信號,確保不會干擾父進程。
{
perror("sigaction");
exit(2);
}
//再次fork 終止父進程,保持子進程不是話首進程,從保證後
//續不會在和其他終端關聯!
//這部分不是必須的
// if((pid = fork()) < 0)
//{
// perror("fork2");
// exit(3);
// }
// else if(pid > 0)
// {
// exit(0);
// }
if(chdir("/") < 0) //更改工作目錄
{
perror("chdir");
exit(3);
}
close(0); //關閉不必要的文件描述符
fd0 = open("/dev/null", O_RDWR);
dup2(fd0, 1); //重定向
dup2(fd0, 2);
while(1)
{}
}
int main()
{
mydaemon();
return 0;
}
daemon函數
#include <unistd.h>
int main()
{
daemon(0, 0);
while(1)
{}
return 0;
}