引用自:
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); }