DAEMON(守护进程)
1.特征
DAEMON是一种具备以下特征的进程
- 它的生命周期很长,通常,一个DAEMON会在系统启动时被创建,运行至系统终止
- 它在后台运行并且不具有控制终端,这确保了内核不会为其生成任何的作业控制信号(SIGTTIN、SIGTOUT等)以及终端相关信号(SIGINT, SIGSTOP等)
一些常见的daemon:
- cron:一个在规定时间执行命令的daemon
- sshd:安全shell daemon,允许在远程主机上使用一个安全的通信协议来登录系统
- httpd:HTTP服务器daemon,用于服务Web页面
- inetd:Internet超级服务器daemon,监听从指定TCP/IP端口进入的网络连接并启动相应的网络应用程序来处理这些连接
2.创建一个daemon
创建一个daemon,一般需要完成以下步骤
-
执行一个fork(),然后父进程退出,子进程继续执行(此时子进程会被init进程收养)
有以下两个原因:
- 如果daemon是从命令行启动的,那么当父进程终止时,shell会接收到SIGCHLD信号,接着shell成为前台进程,刚刚创建出的子进程会自动的在后台运行
- 子进程继承了父进程的进程组ID,然而它的父进程终止了,子进程的进程ID与进程组ID并不相同,即它并非进程组首进程,这样做为后面的步骤提供了保障
-
子进程调用
setsid()
开启一个新的会话,它与之前的控制终端的关联将会被完全断开 -
如果daemon在运行过程中不会打开终端设备文件,那么就无须担心其会请求一个控制终端
如果daemon在运行过程中可能会打开一个终端设备,那么就需要采取措施使得该终端设备不会成为控制终端,方法有二:
-
在使用open()打开终端文件时指定
O_NOCTTY
标志 -
在子进程使用setsid()之后再次使用fork(),然后舍弃子进程,保留孙子进程,这样做确保了孙子进程不会成为会话首进程
(在Linux中,只有会话首进程在打开终端时才会让该终端成为控制终端)
-
-
清除进程的umask以确保daemon创建文件和目录式具有所需要的权限
-
修改进程的工作目录,通常是’/'
这样做主要是为了确保在系统关闭时能够正常的卸载文件系统,如果daemon在某一个别的目录,而该目录下由于守护进程当前正在运行,就会处于busy状态,导致该目录的文件系统无法被卸载。也可以将工作目录修改为任意一个永远不会被卸载的目录,如完成任务时所在的目录或者是配置文件中定义的一个目录
-
关闭daemon从其父进程继承而来的所有打开着的文件描述符(可选)
除非daemon可能需要保持文件描述的打开状态,否则一般都会关闭,原因有二:
- daemon失去了终端并且在后台运行,因此让它保持文件描述符0,1,2毫无意义,因为它们即执行控制终端
- 无法卸载长时间打开着的daemon打开的文件的文件系统
-
打开/dev/null设备文件并且使用dup2()使所有的这些描述符指向这个设备
原因也有二:
- 确保了daemon在调用在这些文件描述符上执行I/O操作时不会出现失败的情况
- 防止daemon使用文件描述符1,2打开文件进行读写导致原有的标准输出和标准错误的数据被破坏(不是很懂…)
3.编写daemon指南
- 信号处理
通常的daemon在在系统关闭时都会执行一些特定脚本文件来停止,可如果不以这种方式终止,那么daemon就会收到一个SIGTERM信 号,因为init进程在系统关闭时会向它的所有子进程发送该信号,如果自己编写的话,需要编写一个在5秒内完成工作的信号处理程序, 因为在5秒后,Init会再次发送一个SIGKILL信号
-
内存泄漏问题
由于daemon在系统中长时间运行,所以需要注意内存的泄漏问题
-
多个实例
一般需要确保系统中统一时刻只有一个daemon实例处于活跃状态
4.使用syslog记录消息和错误处理
syslog是一个集中式日志工具,系统中的所有应用程序都可以使用该工具来记录日志消息
syslog工具有两个基本组件:syslogd daemon和 syslogAPI
-
syslogd daemon
如图(如果没有的话记得
sudo apt-get install inetutils-syslogd
)
syslogAPI
-
建立一个到系统日志的连接
openlog()函数的调用是可选的,它建立一个到系统日志工具的连接之后并且为后续的syslog()调用设置默认设置
#include<syslog.h> void openlog(const char *ident, int log_opentions, int facility);
参数解释:
-
ident
ident是一个指向字符串的指针,syslog()输出的每条消息都会包含这个字符创,这个参数的取值通常为程序名
-
log_options
一个位掩码,包含LOG_NDELAY, LOG_NOWAIT等(查手册去吧你!),默认是LOG_ODELAY
-
facility
用于标识消息的类型
-
-
记录一条日志消息
#includeK<syslog.h> void syslog(int priority, const char *format, ...);
参数解释:
-
priority
priority是facility和level的OR值,省略的话则取默认值(openlog())
levle可选值如下
-
-
format
格式控制字符,比如可以这么写
syslog(priority, "%s", string);
-
关闭日志
当完成日志记录之后可以使用closelog()来释放分配给/dev/log socket的文件描述符,基本不用,因为daemon一般都运行至系统终止
#include<syslog.h> void closelog(void);
-
过滤日志消息
#include<syslog.h> int setlongmask(int mask_priority); //成功:返回之前的日志掩码
所有不在mask_priority中的掩码设置都会被丢弃
-
有用的LOG_MASK()与LSG_UPO()宏
LOG_MASK()会将level值转换为掩码,LOG_UPTO()创建一个能过滤特定级别以及以上的所有消息的位掩码
-
日志消息写在哪?
看/etc/syslog.conf文件吧,具体语法不说了,上网查,记得下载syslogd