apue学习笔记(第十三章 守护进程)
本章将说明守护进程结构,以及如何编写守护进程程序。
守护进程,也就是通常说的Daemon进程,是Unix中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
编程规则
在编写守护进程程序时需遵循一些基本规则,以防止产生不必要的交互作用。下面将说明这些规则。
1.调用umask将文件模式创建屏蔽字设置为一个已知值(通常是0)
2.调用fork,然后使父进程exit,保证了子进程不是进程组的组长进程,是下面进行setsid调用的先决条件
3.调用setsid创建一个新会话。然后它会执行3个步骤:(a)成为新会话的首进程 (b)成为一个新进程组的组长进程 (c)没有控制终端
4.将当前工作目录更改为根目录。
5.关闭不再需要的文件描述符
6.某些守护进程打开/dev/null使其具有文件描述符0、1、2
下面函数演示了创建守护进程的基本步骤
#include "apue.h"
#include <syslog.h>
#include <fcntl.h>
#include <sys/resource.h> void
daemonize(const char *cmd)
{
int i, fd0, fd1, fd2;
pid_t pid;
struct rlimit rl;
struct sigaction sa; /*
* Clear file creation mask.
*/
umask(); /*
* Get maximum number of file descriptors.
*/
if (getrlimit(RLIMIT_NOFILE, &rl) < )
err_quit("%s: can't get file limit", cmd); /*
* Become a session leader to lose controlling TTY.
*/
if ((pid = fork()) < )
err_quit("%s: can't fork", cmd);
else if (pid != ) /* parent */
exit();
setsid(); /*
* Ensure future opens won't allocate controlling TTYs.
*/
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = ;
if (sigaction(SIGHUP, &sa, NULL) < )
err_quit("%s: can't ignore SIGHUP", cmd);
if ((pid = fork()) < )
err_quit("%s: can't fork", cmd);
else if (pid != ) /* parent */
exit(); /*
* Change the current working directory to the root so
* we won't prevent file systems from being unmounted.
*/
if (chdir("/") < )
err_quit("%s: can't change directory to /", cmd); /*
* Close all open file descriptors.
*/
if (rl.rlim_max == RLIM_INFINITY)
rl.rlim_max = ;
for (i = ; i < rl.rlim_max; i++)
close(i); /*
* Attach file descriptors 0, 1, and 2 to /dev/null.
*/
fd0 = open("/dev/null", O_RDWR);
fd1 = dup();
fd2 = dup(); /*
* Initialize the log file.
*/
openlog(cmd, LOG_CONS, LOG_DAEMON);
if (fd0 != || fd1 != || fd2 != ) {
syslog(LOG_ERR, "unexpected file descriptors %d %d %d",
fd0, fd1, fd2);
exit();
}
}
出错记录
因为守护进程本就不应该有控制终端,所以不能简单地把出错信息写到标准错误上。
syslog设施是一个集中的守护进程出错记录设施,下图演示了syslog设施的详细组织结构:

有以下3种产生日志消息的方法:
1.内核例程可以调用log函数。任何一个用户进程都可以通过打开并读取/dev/klog设备来读取这些信息。
2.大多数用户进程(守护进程)调用syslog函数来产生日志消息。这使消息被发送至UNIX域数据报套接字/dev/log。
3.可将日志消息发向UDP端口514
通常,syslogd守护进程读取所有3种格式的日志消息。此守护进程在启动时读一个配置文件,其文件名一般为/etc/syslog.conf,该文件决定了不同类型的消息应送向何处。
该设施的接口是syslog函数
#include <syslog.h>
void openlog(const char *ident,int option,int facility);
void syslog(int priority,const char *format,...);
void closelog(void);
int setlogmask(int maskpri);
调用openlog是可选的。如果不调用openlog,则在第一次调用syslog时,自动调用openlog。
调用closelog也是可选的。因为它只是关闭增被用于与syslogd守护进程进行通信的描述符。
调用openlog使我们可以指定一个ident(一般是程序的名称),以后,此ident将被加至每则日志消息中。
option参数是指定各种选项的位屏蔽。下图介绍了可用的option选项:

facility参数值选取自下图,用来让配置文件说明来自不同设施的消息将以不同的方式进行处理。

调用syslog产生一个日志消息。其priority参数是facility和level的组合。其中level的值按优先级由最高到最低一次排列如下:

将format参数以及其他所有参数传至vsprintf函数以便进行格式化。其中,format中每个出现的%m字符都先被替换成与errno值相对应的出错消息字符串。
setlogmask函数用于设置进程的记录优先级屏蔽字。各条消息除非已在记录优先级屏蔽字中进行了设置,否则将不被记录。
除了syslog,很多平台还提供它的一种辩题来处理可变参数列表。
#include <syslog.h>
#include <stdarg.h>
void vsyslog(int priority,const char *format,va_list arg);
单实例守护进程
为了正常运作,某些守护进程会实现为在任一时刻只运行该守护进程的一个副本。
文件和记录锁机制(第十四章)为一种方法提供了基础,该方法保证了一个守护进程只有一个副本,下面函数将演示这一点。
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h> #define LOCKFILE "/var/run/daemon.pid"
#define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) extern int lockfile(int); int
already_running(void)
{
int fd;
char buf[]; fd = open(LOCKFILE, O_RDWR|O_CREAT, LOCKMODE);
if (fd < ) {
syslog(LOG_ERR, "can't open %s: %s", LOCKFILE, strerror(errno));
exit();
}
if (lockfile(fd) < ) {
if (errno == EACCES || errno == EAGAIN) {
close(fd);
return();
}
syslog(LOG_ERR, "can't lock %s: %s", LOCKFILE, strerror(errno));
exit();
}
ftruncate(fd, );
sprintf(buf, "%ld", (long)getpid());
write(fd, buf, strlen(buf)+);
return();
}
其中lockfile函数如下
#include <unistd.h>
#include <fcntl.h> int
lockfile(int fd)
{
struct flock fl; fl.l_type = F_WRLCK;
fl.l_start = ;
fl.l_whence = SEEK_SET;
fl.l_len = ;
return(fcntl(fd, F_SETLK, &fl));
}
守护进程实例
下面程序说明了守护进程可以重读其配置文件的一种方法
#include "apue.h"
#include <pthread.h>
#include <syslog.h> sigset_t mask; extern int already_running(void); void
reread(void)
{
/* ... */
} void *
thr_fn(void *arg)
{
int err, signo; for (;;) {
err = sigwait(&mask, &signo);
if (err != ) {
syslog(LOG_ERR, "sigwait failed");
exit();
} switch (signo) {
case SIGHUP:
syslog(LOG_INFO, "Re-reading configuration file");
reread();
break; case SIGTERM:
syslog(LOG_INFO, "got SIGTERM; exiting");
exit(); default:
syslog(LOG_INFO, "unexpected signal %d\n", signo);
}
}
return();
} int
main(int argc, char *argv[])
{
int err;
pthread_t tid;
char *cmd;
struct sigaction sa; if ((cmd = strrchr(argv[], '/')) == NULL)
cmd = argv[];
else
cmd++; /*
* Become a daemon.
*/
daemonize(cmd); /*
* Make sure only one copy of the daemon is running.
*/
if (already_running()) {
syslog(LOG_ERR, "daemon already running");
exit();
} /*
* Restore SIGHUP default and block all signals.
*/
sa.sa_handler = SIG_DFL;
sigemptyset(&sa.sa_mask);
sa.sa_flags = ;
if (sigaction(SIGHUP, &sa, NULL) < )
err_quit("%s: can't restore SIGHUP default");
sigfillset(&mask);
if ((err = pthread_sigmask(SIG_BLOCK, &mask, NULL)) != )
err_exit(err, "SIG_BLOCK error"); /*
* Create a thread to handle SIGHUP and SIGTERM.
*/
err = pthread_create(&tid, NULL, thr_fn, );
if (err != )
err_exit(err, "can't create thread"); /*
* Proceed with the rest of the daemon.
*/
/* ... */
exit();
}
下面程序说明一个单线程守护进程然后捕捉SIGHUP并重读其配置文件
#include "apue.h"
#include <syslog.h>
#include <errno.h> extern int lockfile(int);
extern int already_running(void); void
reread(void)
{
/* ... */
} void
sigterm(int signo)
{
syslog(LOG_INFO, "got SIGTERM; exiting");
exit();
} void
sighup(int signo)
{
syslog(LOG_INFO, "Re-reading configuration file");
reread();
} int
main(int argc, char *argv[])
{
char *cmd;
struct sigaction sa; if ((cmd = strrchr(argv[], '/')) == NULL)
cmd = argv[];
else
cmd++; /*
* Become a daemon.
*/
daemonize(cmd); /*
* Make sure only one copy of the daemon is running.
*/
if (already_running()) {
syslog(LOG_ERR, "daemon already running");
exit();
} /*
* Handle signals of interest.
*/
sa.sa_handler = sigterm;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGHUP);
sa.sa_flags = ;
if (sigaction(SIGTERM, &sa, NULL) < ) {
syslog(LOG_ERR, "can't catch SIGTERM: %s", strerror(errno));
exit();
}
sa.sa_handler = sighup;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGTERM);
sa.sa_flags = ;
if (sigaction(SIGHUP, &sa, NULL) < ) {
syslog(LOG_ERR, "can't catch SIGHUP: %s", strerror(errno));
exit();
} /*
* Proceed with the rest of the daemon.
*/
/* ... */
exit();
}
apue学习笔记(第十三章 守护进程)的更多相关文章
- APUE读书笔记-第13章-守护进程
第13章 守护进程 13.1 引言 *守护进程也称精灵进程(daemon)是生存期较长的一种进程.它们常常在系统自举时启动,仅在系统关闭时才终止.因为它们没有控制终端,所以说它们是在后台运行的.UNI ...
- 《APUE》读书笔记第十三章-守护进程
守护进程 守护进程是生存期较长的一种进程,它们常常在系统自举时启动,仅在系统关闭时才终止.因为它们没有控制终端,所以说它们是在后台运行的.UNIX系统由很多守护进程,它们执行日常事务活动. 本章主要介 ...
- UNP学习笔记(第十三章 守护进程和inetd超级服务器)
关于守护进程可以查看apue的笔记 http://www.cnblogs.com/runnyu/p/4645046.html daemon_init函数 下面给出名为daemon_init函数,通过调 ...
- apue学习笔记(第九章 进程关系)
本章将详细地说明进程组以及POSIX.1引入的会话的概念.还将介绍登录shell和所有从登录shell启动的进程之间的关系 终端登录 BSD终端登录.系统管理者创建通常名为/etc/ttys的文件,其 ...
- 《Unix环境高级编程》读书笔记 第13章-守护进程
1. 引言 守护进程是生存期长的一种进程.它们常常在系统引导装入时启动,仅在系统关闭时才终止.它们没有控制终端,在后台运行. 本章说明守护进程结构.如何编写守护进程程序.守护进程如何报告出错情况. 2 ...
- Linux学习笔记(9)-守护进程
明天学这个!! ---------------------------------------------------------- 守护进程(Daemon)是运行在后台的一种特殊进程.它独立于控制终 ...
- [汇编学习笔记][第十三章int指令]
第十三章int指令 13.1 int指令 格式: int n, n 为中断类型码 可以用int指令调用任何一个中断的中断处理程序(简称中断例程). 13.4 BIOS和DOS 所提供的中断例程 BIO ...
- docker 学习笔记20:docker守护进程的配置与启动
安装好docker后,需要启动docker守护进程.有多种启动方式. 一.服务的方式 因为docker守护进程被安装成服务.所以,可以通过服务的方式启停docker守护进程,包括查看状态. sudo ...
- apue学习笔记(第一章UNIX基础知识)
总所周知,UNIX环境高级编程是一本很经典的书,之前我粗略的看了一遍,感觉理解得不够深入. 听说写博客可以提高自己的水平,因此趁着这个机会我想把它重新看一遍,并把每一章的笔记写在博客里面. 我学习的时 ...
随机推荐
- 【bzoj4966】总统选举 随机化+线段树
题目描述 黑恶势力的反攻计划被小C成功摧毁,黑恶势力只好投降.秋之国的人民解放了,举国欢庆.此时,原秋之国总统因没能守护好国土,申请辞职,并请秋之国人民的大救星小C钦定下一任.作为一名民主人士,小C决 ...
- canvas图片跨域问题
canvas的drawImage使用跨域图片时候,会报错,解决方法如下: 1. 使用base64替换跨域图片 如果图片不大,且只有几张,可以使用base64,来代替跨域引用图片. 2. 设置image ...
- 【VBA】全局数组定义
[说明] 全局数组定义(写在Module的最上面) 'Array Public Arr_approver Public Arr_delegator Public Arr_Role
- css 文字垂直居中问题
CSS 文字垂直居中问题 问题:在 div 中文字居中问题: 当使用 line-height:100%%; 时,文字没有居中,如下: html: <div id="header_log ...
- webpack+babel+ES6+react环境搭建
webpack+babel+ES6+react环境搭建 步骤: 1 创建项目结构 注意: 先创建一个项目目录 react 这个名字自定义,然后进入到这个目录下面 mkdir app //创建app ...
- Js 中 == 与 === 的区别
1.对于string,number等基础类型,==和===是有区别的 1)不同类型间比较,==之比较“转化成同一类型后的值”看“值”是否相等,===如果类型不同,其结果就是不等 2)同类型比较,直接进 ...
- HDU 4328 Cut the cake
Cut the cake Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Tota ...
- 【MFC】Tab Control 控件的使用(转)
原文转自 http://blog.csdn.net/hustspy1990/article/details/5425365 1.先建立一个对话框MFC应用程序,然后在工具箱里面把Tab Control ...
- vim技巧记录底行模式的使用(1)
若正在编辑文件,临时须要查看目录: (1)直接在底行模式下使用ls命令,完整的为:!ls 在我这里就显示如下的结果: functionpointer helloWorld.s tes ...
- 01.mp4v2应用—mp4转h264
1.h264文件基本功能 NAL 头 0x00 0x00 0x00 0x01 sps :nal+0x67开头 pps :nal+0x68开头 I帧 0x65 开头 ...... 2.mp4v2提取26 ...