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环境高级编程是一本很经典的书,之前我粗略的看了一遍,感觉理解得不够深入. 听说写博客可以提高自己的水平,因此趁着这个机会我想把它重新看一遍,并把每一章的笔记写在博客里面. 我学习的时 ...
随机推荐
- 经典SQL语句大全、50个常用的sql语句
50个常用的sql语句 Student(S#,Sname,Sage,Ssex) 学生表 Course(C#,Cname,T#) 课程表 SC(S#,C#,score) 成绩表 Teacher(T#,T ...
- CSA Round 84 Growing Trees
题目 题目大意 给定一棵有 $n$ 个节点的树,边的权值每天变化.对于第 $i$ 条边,在第 $0$ 天,其权值为 $c_i$,每天权值变化 $a_i$(即,在第 $k$ 天,其权值为 $c_i + ...
- powerdesign设置字体大小
http://www.2cto.com/database/201406/308923.html
- 关于在redux当中 reducer是如何知道传入的state是初始化state下面的哪一条数据
首先初始化redux的数据 reducer 那么问题来了,todos这个reducer是如何知道传入的是初始化state下面的todos这条数据呢? 合并reducer 合并之后是这样的 他们之间的关 ...
- 问题:viewController不会调用dealloc()不会销毁
问题 在调试程序时,我从ViewController A push进 ViewController B,在从B back时发现程序不会执行B里面的dealloc(),很诡异的问题,因为按理说此时点击b ...
- 嵌入式 Linux线程锁详解pthread_mutexattr_t【转】
转自:http://blog.sina.com.cn/s/blog_8795b0970101il6g.html 在Posix Thread中定义有一套专门用于线程同步的mutex函数. . 创建和销毁 ...
- 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---23
以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下:
- mac使用matplotlib
mac系统,安装了matplotlib之后, 导入matplotlib.pyplot的时候报错 RuntimeError: Python is not installed as a framework ...
- php --图片加图片水印
最近在做一个视频网站需要视频有一个封面图片,但是不能是普通的图片,能让别人一眼看出来是 视频,所以我就在图片上面加了视频播放器的那种水印,具体代码如下: <?php/** * 图片加水印(适用于 ...
- 关于apache 虚拟主机配置访问403的问题
<Directory /> Options FollowSymLinks Order allow,deny Allow from all</Directory&g ...