怎麼寫Daemon程序

 引用自:

http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html

http://www.qqgb.com/Netware/Linux/Linux7/82888.html

 

作者:Devin Watson
譯者:Li Zhiwei

第一版 2004年5月

本文講述如何用GCC在Linux編寫Daemon. 使用本文檔需要Linux和C方面的知識。原文版權屬於Devin Watson,以BSD許可發佈. (翻譯時略有刪節)

1.介紹:什麼是Daemon
一個Daemon(或服務)是一個被設計成自動運行的後臺進程,它很少或者根本就不需要用戶干預。Apache服務http daemon就是這樣的一個例子。他在後臺等待監聽某個端口,根據請求的類型,爲頁面或者腳本服務。

在Linux下建立一個daemon,需要以一個特定順序使用一系列規則.瞭解它們如何工作,將幫助你理解daemon如何在Linux用戶空間操作,也能調用內核操作。實際上,一些有內核模塊的daemon接口能夠同硬件設備工作,如外部控制板打印機,PDA等.

2.1 它要做什麼
一個Daemon程序應該只做一件事情,並要做好。這件事情可能是像管理在多個域名上的數以百計的郵箱這麼複雜的事情,也可能是 寫一個報告,調用sendmail發送給管理員這麼簡單。

不管怎樣,你應該有一個好的計劃。如果它同其他daemon有互操作,這也是考慮好的事情.

2.2 如何互動
Daemon不應該直接通過終端同用戶通信。實際上,daemon根本就不應該直接同用戶通信。所有的通信都應該傳給某些類型的接口(可能是你寫的,或者你不必寫這樣一個接口),它可能是複雜的GTK+ GUI,也可能是簡單的signal

3.基本的Daemon結構
一個Daemon啓動時,它必須做一些底層的家務,來準備實際的工作。這包括幾個步驟:
1)從父進程分離
2)改變文件模式掩碼(umask)
3)打開log文件,準備寫入
4)建立一個唯一的會話ID(SID)
5)改變當前的工作目錄到一個安全的地方
6)關閉標準的文件描述符
7)進入實際的daemon代碼

3.1從父進程分離
一個daemon可能被系統啓動,也可能被用戶終端啓動,或者被腳本啓動。當他啓動時,跟系統中的其他可執行程序沒什麼分別。爲了讓它自治,實際的代碼必須在一個子進程裏執行。這就是衆所周知的forking

注意調用fork之後的錯誤檢查。寫daemon時,你將必須做儘可能多的防禦。實際上,一個daemon程序有較多的代碼就是做錯誤檢查的。fork()程序返回子進程的ID(pid,不等於0),如果失敗則返回-1. 如果daemon不能開啓新的進程,就要終止。

如果子進程成功創建,父進程也是一言不發就退出了。這似乎很奇怪,其實真正要做的事情,交給子進程去做了。

3.2 改變文件模式掩碼(umask)
爲了寫任何由daemon創建的文件(包括logs), umask必須被改變以確認它們能被合適的寫入或者讀出。這跟在命令行改變它們類似,只不過我們這裏是用程序實現的。

通過設置uamsk爲0,我們將對daemon創建的文件有完全的權限。即使你不打算使用任何文件,設置umask也是一個好注意,因爲這樣你可以訪問文件系統中的任何文件。

3.3 打開一個log文件準備寫入
這部分是可選的。但推薦你打開一個log文件。這可能是你唯一能查看調試信息的地方。

3.4 建立一個唯一的Session ID(SID)
從這裏,子進程必須從內核取得一個唯一的SID。否則,子進程將成爲一個孤兒。

setsid()函數同fork()函數的返回類型一樣。我們用類似的錯誤檢查代碼來檢查是否建立了SID

3.5 改變工作目錄
當前的工作目錄必須被改到一個一定會存在的目錄。因爲許多Linux發行版病不完全遵循Linux文件結構標準,有個目錄是一定存在的,那就是root. 

你再一次看到錯誤防護代碼。chdir失敗返回-1,在改變到root目錄後,必須檢查一次。

3.6 關閉標準的文件描述符號

 

代碼示例:

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>

int main(void) {

/* Our process ID and Session ID */
pid_t pid, sid;

/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}

/* Change the file mode mask */
umask(0);

/* Open any logs here */

/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}



/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}

/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);

/* Daemon-specific initialization goes here */

/* The Big Loop */
while (1) {
/* Do some task here ... */

sleep(30); /* wait 30 seconds */
}
exit(EXIT_SUCCESS);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章