- 浏览: 144919 次
-
文章分类
最新评论
UNIX 系统中,守护进程遵循下列通用惯例。
(1)若守护进程使用锁文件,则该文件通常存储在 /var/run 目录中。不过守护进程可能需要具有超级用户权限才能在此目录下创建文件。锁文件的名字通常是 name.pid,其中,name 是该守护进程或服务的名字。例如,cron 守护进程锁文件的名字是 /var/run/crond.pid。
(2)若守护进程支持配置选项,则配置文件通常存放在 /etc 目录中,名字一般为 name.conf,其中,name 是该守护进程或服务的名字。例如,syslogd 守护进程的配置文件通常是 /etc/syslog.conf。
(3)守护进程可用命令行启动,但通常它们是由系统初始化脚本之一(/etc/rc* 或 /etc/init.d/*)启动的。如果要在守护进程终止时自动地重新启动它,可以在 /etc/inittab 中为该守护进程包括 respawn 记录项,这样 init 就将重新启动该守护进程。
(4)守护进程的配置文件一般会在守护进程启动时读取(如果有的话),之后就不会再查看。若更改了配置文件,则该守护进程可能需要重启以使更改生效。为避免这种麻烦,某些守护进程会捕捉 SIGHUP 信号。当它们接收到该信号时,就重新读取配置文件。因为守护进程并不与终端相结合,它们或者是无控制终端的会话首进程,或者是孤儿进程组的成员,所以没有理由接收 SIGHUP,因而可以安全地重复使用 SIGHUP。
此外,为了正常运作,某些守护进程在任一时刻只允许运行该守护进程的一个副本。例如,该守护进程可能需要排他地访问一个设备。这种情况可以采取文件和记录锁机制。这种方法保证一个守护进程只有一个副本在运行。如果每一个守护进程创建一个有固定名字的文件,并对该文件加一把写锁,那么只允许创建一把这样的写锁,之后创建写锁的尝试都会失败。而在该守护进程终止时,这把锁将被自动删除。
下面这个函数演示了如何使用文件和记录锁来保证只允许一个守护进程的一个副本。其中的 lockfile 函数的实现见后面的fcntl 记录锁一节。
这里,如果操作的文件已经加了锁,那么 lockfile 函数就会失败,errno 会被设置为 EACCES 或 EAGAIN,表明该守护进程已在运行。否则将文件长度截断为 0,并写入进程 ID(将文件长度截断为 0 的原因是之前的守护进程的进程 ID 字符串可能长于当前的进程 ID 字符串。比如以前为 12345,现在的为 9999,那么写入后,文件中留下的将是 99995 而非 9999)。
接下来的这个程序演示了守护进程在多线程中重读其配置文件的一种方法。其中用到了上面定义的这个 already_running 函数,而另一个函数 daemonize 的实现见守护进程编写规则与出错记录 。
这里要注意的是,在 daemonize 函数中修改了 SIGHUP 的默认处理行为,因此应该在调用之后恢复默认处理动作,否则调用 sigwait 的线程决不会见到该信号。如同对多线程程序所推荐的那样,这里阻塞了所有信号,并创建了单独的一个线程来负责处理 SIGHUP 和 SIGTERM 信号。因为 SIGHUP 和 SIGTERM 的默认动作是终止进程,所以在阻塞了这些信号后,当它们中的其中一个被发送到守护进程时,守护进程不会消亡。
运行结果如下(省略了 grep 自身所在的行和一些不重要的列):
当然,若要使用单线程守护进程来重读配置,可以在初始化守护进程后,分别为 SIGHUP 和 SIGTERM 配置信号处理程序,然后将重读逻辑放在信号处理程序中,也可以在信号处理程序中设置一个标志,并由守护进程的主线程来完成所有的工作。比如上面这个程序可以这样修改。
(1)若守护进程使用锁文件,则该文件通常存储在 /var/run 目录中。不过守护进程可能需要具有超级用户权限才能在此目录下创建文件。锁文件的名字通常是 name.pid,其中,name 是该守护进程或服务的名字。例如,cron 守护进程锁文件的名字是 /var/run/crond.pid。
(2)若守护进程支持配置选项,则配置文件通常存放在 /etc 目录中,名字一般为 name.conf,其中,name 是该守护进程或服务的名字。例如,syslogd 守护进程的配置文件通常是 /etc/syslog.conf。
(3)守护进程可用命令行启动,但通常它们是由系统初始化脚本之一(/etc/rc* 或 /etc/init.d/*)启动的。如果要在守护进程终止时自动地重新启动它,可以在 /etc/inittab 中为该守护进程包括 respawn 记录项,这样 init 就将重新启动该守护进程。
(4)守护进程的配置文件一般会在守护进程启动时读取(如果有的话),之后就不会再查看。若更改了配置文件,则该守护进程可能需要重启以使更改生效。为避免这种麻烦,某些守护进程会捕捉 SIGHUP 信号。当它们接收到该信号时,就重新读取配置文件。因为守护进程并不与终端相结合,它们或者是无控制终端的会话首进程,或者是孤儿进程组的成员,所以没有理由接收 SIGHUP,因而可以安全地重复使用 SIGHUP。
此外,为了正常运作,某些守护进程在任一时刻只允许运行该守护进程的一个副本。例如,该守护进程可能需要排他地访问一个设备。这种情况可以采取文件和记录锁机制。这种方法保证一个守护进程只有一个副本在运行。如果每一个守护进程创建一个有固定名字的文件,并对该文件加一把写锁,那么只允许创建一把这样的写锁,之后创建写锁的尝试都会失败。而在该守护进程终止时,这把锁将被自动删除。
下面这个函数演示了如何使用文件和记录锁来保证只允许一个守护进程的一个副本。其中的 lockfile 函数的实现见后面的fcntl 记录锁一节。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <syslog.h> #include <sys/stat.h> #define LOCKFILE "/var/run/singleDaemon.pid" #define LOCKMODE (S_IRUSR |S_IWUSR |S_IRGRP |S_IROTH) extern int lockfile(int); int already_running(void){ int fd = open(LOCKFILE, O_RDWR|O_CREAT, LOCKMODE); if(fd < 0){ syslog(LOG_ERR, "can't open %s: %s", LOCKFILE, strerror(errno)); exit(1); } if(lockfile(fd) < 0){ if(errno == EACCES || errno == EAGAIN){ close(fd); return 1; } syslog(LOG_ERR, "can't lock %s: %s", LOCKFILE, strerror(errno)); exit(1); } ftruncate(fd, 0); char buf[16]; sprintf(buf, "%ld", (long)getpid()); write(fd, buf, strlen(buf)+1); return 0; }
这里,如果操作的文件已经加了锁,那么 lockfile 函数就会失败,errno 会被设置为 EACCES 或 EAGAIN,表明该守护进程已在运行。否则将文件长度截断为 0,并写入进程 ID(将文件长度截断为 0 的原因是之前的守护进程的进程 ID 字符串可能长于当前的进程 ID 字符串。比如以前为 12345,现在的为 9999,那么写入后,文件中留下的将是 99995 而非 9999)。
接下来的这个程序演示了守护进程在多线程中重读其配置文件的一种方法。其中用到了上面定义的这个 already_running 函数,而另一个函数 daemonize 的实现见守护进程编写规则与出错记录 。
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <string.h> #include <syslog.h> #include <pthread.h> extern void daemonize(const char *); extern int already_running(void); sigset_t mask; void reread(void){ /* read configuration... */ } void *thr_fn(void *arg){ int signo; for(;;){ if(sigwait(&mask, &signo) != 0){ syslog(LOG_ERR, "sigwait error"); exit(1); } switch(signo){ case SIGHUP: syslog(LOG_INFO, "reread configuration"); reread(); break; case SIGTERM: syslog(LOG_INFO, "got SIGTEM; thread exit"); exit(0); default: syslog(LOG_INFO, "unexpected signal %d\n", signo); } } return 0; } int main(int argc, char *argv[]){ char *cmd = NULL; if((cmd = strrchr(argv[0], '/')) == NULL) cmd = argv[0]; else cmd++; daemonize(cmd); // become a daemon if(already_running()){ // ensure only one copy of the daemon is running. syslog(LOG_ERR, "daemon already running"); exit(1); } struct sigaction act; act.sa_handler = SIG_DFL; // restore SIGHUP default sigemptyset(&act.sa_mask); act.sa_flags = 0; if(sigaction(SIGHUP, &act, NULL) != 0){ syslog(LOG_ERR, "can't restore SIGHUP default"); exit(1); } sigfillset(&mask); // and block all signals if(pthread_sigmask(SIG_BLOCK, &mask, NULL) != 0){ syslog(LOG_ERR, "pthread_sigmask error"); exit(1); } pthread_t tid; // create a thread to handle SIGHUP and SIGTERM if(pthread_create(&tid, NULL, thr_fn, 0) != 0){ syslog(LOG_ERR, "pthread_create error\n"); exit(1); } /* preceed with the rest of the daemon */ for(;;){ pause(); } exit(0); }
这里要注意的是,在 daemonize 函数中修改了 SIGHUP 的默认处理行为,因此应该在调用之后恢复默认处理动作,否则调用 sigwait 的线程决不会见到该信号。如同对多线程程序所推荐的那样,这里阻塞了所有信号,并创建了单独的一个线程来负责处理 SIGHUP 和 SIGTERM 信号。因为 SIGHUP 和 SIGTERM 的默认动作是终止进程,所以在阻塞了这些信号后,当它们中的其中一个被发送到守护进程时,守护进程不会消亡。
运行结果如下(省略了 grep 自身所在的行和一些不重要的列):
$ sudo ./rereadConfig.out # 需要 root 权限来运行 $ ps -efj | grep rereadConfig.out # 查看守护进程 UID PID PPID PGID SID TTY CMD root 109758 1 109757 109757 ? ./rereadConfig.out $ $ sudo kill -s SIGHUP 109758 # 发送 SIGHUP 信号来重读配置 $ ps -efj | grep rereadConfig.out # SIGHUP 信号不会终止守护进程 UID PID PPID PGID SID TTY CMD root 109758 1 109757 109757 ? ./rereadConfig.out $ $ sudo kill -s SIGTERM 109758 # 发送 SIGTERM 信号来重读配置 $ ps -efj | grep rereadConfig.out # SIGTERM 信号终止了守护进程 $
当然,若要使用单线程守护进程来重读配置,可以在初始化守护进程后,分别为 SIGHUP 和 SIGTERM 配置信号处理程序,然后将重读逻辑放在信号处理程序中,也可以在信号处理程序中设置一个标志,并由守护进程的主线程来完成所有的工作。比如上面这个程序可以这样修改。
int main(){ ... sa.sa_handler = sigterm; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGHUP); sa.sa_flags = 0; sigaction(SIGTERM, &sa, NULL); sa.sa_handler = sighup; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGTERM); sa.sa_flags = 0; sigaction(SIGHUP, &sa, NULL); ... }
发表评论
-
打开伪终端设备
2018-07-09 20:50 1269在伪终端概述一节中已对 PTY进行了初步的介绍。尽管 ... -
伪终端概述
2018-06-02 11:05 1580伪终端就是指,一个应用程序看上去像一个终端,但事实上它 ... -
终端窗口大小和 termcap
2018-05-29 22:39 812多数 UNIX 系统都提供了一种跟踪当前终端窗口大小的 ... -
终端规范模式和非规范模式
2018-05-29 00:25 978终端规范模式很简单:发一个读请求,当一行已经输入后,终 ... -
终端标识
2018-05-23 11:18 580尽管控制终端的名字在多数 UNIX 系统上都是 /de ... -
波特率和行控制函数
2018-05-22 07:53 957虽然大多数终端设 ... -
终端属性和选项标志
2018-05-20 07:40 718tcgetattr 和 tcsetattr ... -
终端特殊输入字符
2018-05-17 06:33 829终端支持下表所示的特殊输入字符。 为了更改 ... -
终端 I/O 综述
2018-05-10 07:56 453终端设备可认为是由内核中的终端驱动程序控制的。每个终端 ... -
POSIX 信号量
2018-05-09 00:03 590在XSI IPC通信之信 ... -
XSI IPC 通信之共享存储
2018-04-25 07:18 958在XSI IPC通信之消息队列和XSI IPC通信之信 ... -
XSI IPC通信之信号量
2018-04-17 23:38 629在XSI IPC通信之消 ... -
XSI IPC通信之消息队列
2018-04-15 10:54 514消息队列是消息的链接表,存储在内核中,由消息队列标识符 ... -
XSI IPC 相似特征介绍
2018-02-08 23:48 494有 3 种称作 XSI IPC ... -
IPC 通信之 FIFO
2018-02-06 22:55 437FIFO 也被称为命名管道,未命名的管道只能在两个相关 ... -
IPC 通信之管道
2018-01-30 22:22 404管道是 UNIX 系统 IPC 的最古老但也是最常用的 ... -
readv/writev 函数及存储映射 I/O
2018-01-19 00:57 915readv 和 writev 函数可用于在一次函数调用 ... -
POSIX 异步 I/O
2018-01-16 21:33 470POSIX 异步 I/O 接口为对不同类型的文件进行异 ... -
fcntl 记录锁
2018-01-06 23:48 649记录锁的功能是:当有进程正在读或修改文件的某个部分时, ... -
守护进程编写规则与出错记录
2017-12-26 01:53 466在编写守护进程程 ...
相关推荐
数学建模-公平席位分配问题(比例+惯例法)
### GNU编码标准与Makefile惯例解析 #### 引言 GNU编码标准是自由软件基金会(FSF)制定的一套指导原则,旨在确保GNU项目下的软件不仅功能强大,而且易于维护、扩展和理解。这套标准涵盖了从编码规范、文档编写到...
UCP600 跟单信用证统一惯例中文版 UCP600 是由国际商会(ICC)发布的跟单信用证统一惯例中文版,旨在规范跟单信用证的操作和解释。该惯例适用于任何在正文中明确表明按本惯例办理的跟单信用证(包括在其适用范围内...
文章认为,惯例复制不仅是一个跨组织间的知识转移过程,它还涉及组织内部的知识创造过程,是一种内生性政治进程。在此过程中,正向知识流动和逆向知识流动成为识别组织惯例复制的两个基本过程。正向知识流动强调组织...
这篇实验报告主要涉及了两种公平的席位分配方法:参照惯例的席位分配方法和Q值方法,这两种方法常用于解决资源分配问题,例如在选举、会议代表分配等场景。实验目的是让学生理解这两种方法的区别,并能熟练运用...
程序实现了 用惯例Q值和dhondt 方法分配席位问题,可适用于所有情况
在软件开发过程中,编程规则、惯例和约定是确保代码质量、可读性和团队协作效率的重要基石。这些规范不仅有助于减少错误,提高代码的可维护性,还能促进代码风格的一致性,使得团队成员能更快地理解和修改他人编写的...
外贸人必看的有关日期的国际惯例
打招呼 很多兄弟不知道怎么和女生聊天总是纠结与第一话怎么说,怎么显现出自己的与众不同。这进入了一个误区。所以一开始就要强调:不在于你说了什么,而在于“你是谁”。 推荐新手的打招呼方法 新手们如果想不到好...
在2002年4月的ICC银行委员会会议上,各国代表对何时、如何修订UCP500未达成一致意见(我国赞成立即开始修订),但一致同意先对产生最多争议的七个条款进行评议。因为ICC提出的专家意见中超过58%集中在UCP500这七个...
本篇文章将详细解读价格条款及其相关的国际惯例。 首先,价格术语在国内外贸易中的表现形式有所不同。在国内贸易中,通常会明确标出货物的价格,包括各种税费、运输费用等。而在国际贸易中,为了简化交易过程和明确...
### 命名惯例与规范在C#编程中的应用 #### 概述 在软件开发领域,特别是使用C#语言进行编程时,遵循一套统一且规范的命名惯例至关重要。这不仅能提升代码的可读性和可维护性,还能促进团队协作,减少因命名不一致...
但是,根据标题“商事仲裁国际惯例.pdf”,我们可以围绕“商事仲裁”和“国际惯例”这两个关键主题展开讨论,梳理出一些相关知识点。 ### 商事仲裁 商事仲裁是解决商人或商业实体之间争议的一种非诉讼解决方式。它...
[精选]会计惯例和财务报表的国际比较.pptx
【国际商事惯例的内涵】 国际商事惯例是国际贸易中的一种重要规则,它源于实际的商业活动,并在长时间的实践中逐渐形成。这些惯例通常适用于特定的地域、行业或类型的贸易,是商人们在长期的交往中自发遵守并普遍...
国际贸易术语与国际贸易惯例是全球商业活动中至关重要的概念,它们规定了买卖双方在交易中的权利、义务和责任。国际贸易术语,如FOB、CIF、CFR等,是用于表明商品价格构成、交货地点以及各方承担的风险、费用的特殊...
《会计惯例与财务报表管理知识分析比较》 会计惯例与财务报表管理是财务管理领域的核心内容,它们在全球...理解和掌握这些知识点,对于跨国企业的财务管理和审计工作至关重要,也有助于推动全球会计标准的统一进程。
Python编程惯例.md
这里会介绍守护进程的设计规则、系统日志机制、daemon函数以及守护进程设计中的一些常见惯例。 14. 高级I/O:包括记录锁、SysVSTREAMS机制、非阻塞I/O、I/O多路转接、异步I/O、readv和writev函数、存储映射I/O等。 ...
文档标题提到的是“收藏的55个话术惯例”,这些惯例主要是用于人际沟通,特别是与异性交往中的对话技巧。描述中并未提供具体信息,但从标签“文档”来看,这是一个包含文字资料的文件,可能是一个Word文档或PDF文档...