标签:
守护进程是独立在后台执行和终端控制的所有进程。
DAEMON没有控制终端,由于其通常是由系统初始化脚本启动,但是,有可能根据由用户终端 shell 提示符下,键入命令行启动。护进程必须亲自脱离与控制终端的关联,从而避免与作业控制、终端会话管理、终端产生信号等发生不论什么不期望的交互。也能够避免在后台执行的守护进程非预期地输出到终端。有关作业控制、终端控制的内容可參考文章《作业控制、终端控制 和 守护进程》
因为守护进程没有控制终端,当守护进程出错时,必须通过某种输出函数输出错误消息。而不能使用标准输出函数。syslog 函数是输出这些消息的标准方法,它把这些消息发送给 syslogd 守护进程。
Unix 系统中的 syslogd 守护进程通常由某个系统初始化脚本启动,并且在系统工作期间一直执行。其启动过程例如以下:
来自内核中的不论什么出错消息觉得是这个设备的输入;
下面是守护进程产生日志消息的方式:
syslog 函数
因为守护进程没有控制终端。所以不能把错误日志消息 fprintf 到 stderr 上。可是能够使用 syslog 函数进行处理日志消息。
其定义例如以下:
/* 守护进程 */ #include <syslog.h> void syslog(int priority, const char *message, ...); void openlog(const char *ident, int options, int facility); void closelog(void); /* * 说明: * 函数openlog和closelog是可选的。若不调用openlog函数,则在第一次调用syslog函数时。会自己主动调用openlog函数。 * openlog能够在首次调用syslog前调用。closelog能够在应用进程不再须要发送日志消息时调用; * ident參数是一个由syslog冠于每一个日志消息之前的字符串,一般是程序名。 * ****************************************************************** * options參数由下面值一个或多个逻辑“或”组成: * ****************************************************************** * (1)LOG_CONS 若无法发送到syslogd守护进程。则将消息登记到控制台 * (2)LOG_NDELAY 不延迟打开,马上创建套接字 * (3)LOG_PERROR 既发送到syslogd守护进程,又登记到标准错误输出 * (4)LOG_PID 每条日志消息都登记进程ID * (5)LOG_NOWAIT 不等待在消息记入日志过程中可能已创建的子进程 * (6)LOG_ODELAY 在记录第一条消息之前延迟打开至syslogd守护进程的连接 ********************************************************************* * 參数 priority是级别(level)和设施(facility)两者的组合。 * level和facility的目的在于同意在/dev/syslog.conf文件里统一配置来自同一个给定设施的全部消息, * 或统一配置具有同样级别的全部消息。 * ********************************************************* * 当中level取值例如以下:(注:下面按优先级从高到低) * ********************************************************* * LOG_EMERG 系统不可使用 * LOG_ALERT 必须马上採取修复 * LOG_CRIT 临界条件 * LOG_ERR 出错条件 * LOG_WARNING 警告条件 * LOG_NOTICE 正常然而重要的条件(默认值) * LOG_INFO 通告消息 * LOG_DEBUG 调试级消息 *********************************************************** *********************************************************** * facility取值例如以下: * ********************************************************* * LOG_AUTH 安全/授权消息 * LOG_AUTHPRIV 安全/授权消息(私用) * LOG_CRON cron守护进程 * LOG_DAEMON 系统守护进程 * LOG_FTP FTP守护进程 * LOG_KERN 内核产生的消息 * LOG_LOCAL0 保留由本地使用 * LOG_LOCAL1 保留由本地使用 * LOG_LOCAL2 保留由本地使用 * LOG_LOCAL3 保留由本地使用 * LOG_LOCAL4 保留由本地使用 * LOG_LOCAL5 保留由本地使用 * LOG_LOCAL6 保留由本地使用 * LOG_LOCAL7 保留由本地使用 * LOG_LPR 行打印机系统 * LOG_MALL 邮件系统 * LOG_NEWS 网络新闻系统 * LOG_SYSLOG 由syslogd内部产生的消息 * LOG_USER 随意用户级消息(默认) * LOG_UUCP UUCP系统 * ********************************************************* */
这个套接字一直打开,直到进程终止为止。
当 openlog 函数被应用程序调用时。通常并不马上创建 Unix 域套接字,直到首次调用 syslog 函数时套接字才被创建并打开。可是 openlog 函数能够指定选项 LOG_NDELAY 要求调用 openlog 函数时马上创建套接字。
守护进程编程步骤
1. 创建子进程,父进程退出
?全部工作在子进程中进行
?形式上脱离了控制终端
2. 在子进程中创建新会话
?setsid()函数
?使子进程全然独立出来,脱离控制
3. 改变当前文件夹为根文件夹
?chdir()函数
?防止占用可卸载的文件系统
?也能够换成其他路径
4. 重设文件权限掩码
?umask()函数
?防止继承的文件创建屏蔽字拒绝某些权限
?添加守护进程灵活性
5. 关闭文件描写叙述符
?继承的打开文件不会用到,浪费系统资源,无法卸载
?返回所在进程的文件描写叙述符表的项数。即该进程打开的文件数目
守护进程创建的流程图例如以下:
/* 初始化守护进程 */ #include <unistd.h> #include <stdlib.h> #include <sys/stat.h> #include <syslog.h> #include <fcntl.h> #include <signal.h> #include <sys/resource.h> extern void err_quit(const char *,...); void daemonize(const char *pname, int facility) { int i, fd0, fd1, fd2; pid_t pid; struct rlimit rl; struct sigaction sa; umask(0); /* 限制进程资源,參数RLIMIT_NOFILE指定进程不能超过资源最大数 */ if(getrlimit(RLIMIT_NOFILE, &rl) < 0) err_quit("%s: can't get file limit", pname); /* 创建子进程*/ if((pid =fork()) < 0) err_quit("%s: can't fork", pname); else if(pid != 0) exit(0);/* 父进程退出 */ setsid();/* 创建会话 */ sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if(sigaction(SIGHUP, &sa, NULL) < 0) err_quit("%s: can't ignore SIGHUP"); if((pid =fork()) < 0) err_quit("%s: can't fork", pname); else if(pid != 0) exit(0); /* 把工作文件夹改为根文件夹 */ if(chdir("/") < 0) err_quit("%s: can't change directory to /"); if(rl.rlim_max == RLIM_INFINITY) rl.rlim_max = 1024; /* 关闭全部打开的描写叙述符 */ for(i = 0; i < (int)rl.rlim_max; i++) close(i); fd0 = open("/dev/null", O_RDWR); fd1 = dup(0); fd2 = dup(0); /* 使用syslogd处理错误 */ openlog(pname, LOG_CONS, facility); if(fd0 != 0 || fd1 != 1 || fd2 != 2) { syslog(LOG_ERR, "unexpectrd file descriptors %d %d %d", fd0, fd1, fd2); exit(1); } }
若本进程是从前台作为一个 shell 命令启动的。当父进程终止时,shell 就觉得该命令执行完成。这样子进程就自己主动在后台执行。
另外,子进程继承了父进程的进程组 ID,可是子进程也有自己的进程 ID,这保证子进程不是一个进程组的首进程。所以必须调用 setsid 函数使子进程称为进程组的首进程;
再次调用 fork 的目的是确保守护进程将来即使打开一个终端设备时。也不会自己主动获得控制终端。当没有控制终端的一个会话首进程打开一个设备时,该终端自己主动成为这个会话首进程的控制终端。
然而再次调用 fork 之后。确保新的子进程不再是一个会话首进程,从而不能自己主动获得一个控制终端。当会话首进程(即首次 fork 产生的子进程)终止时,其会话中的全部进程(即再次 fork 产生的子进程)都会收到 SIGHUP 信号。所以必须调用 sigaction 函数忽略该信号;
#include "unp.h" #include <syslog.h> extern int daemon_proc; /* defined in error.c */ void daemon_inetd(const char *pname, int facility) { daemon_proc = 1; /* for our err_XXX() functions */ openlog(pname, LOG_PID, facility); }
版权声明:本文博客原创文章,博客,未经同意,不得转载。
标签:
原文地址:http://www.cnblogs.com/bhlsheji/p/4618559.html