温故——守护进程

本文介绍了守护进程的基本概念,包括其启动方式、编程规则及如何与系统交互。探讨了单实例守护进程的设计,并详细讲解了如何通过文件和记录锁机制确保守护进程的唯一运行。此外,还介绍了守护进程如何读取配置文件以及重启自身的方法。
摘要由CSDN通过智能技术生成

基本概念

父进程ID为0的各进程通常是内核进程,他们作为系统引导装入过程的一部分而启动。

init是一个由内核载引导装入时启动的用户层次的命令。

常见守护进程:
- kthreadd,特殊的内核进程,来创建其它内核进程
- kswapd,内存换页守护进程
- rpcbind,提供RPC程序号映射为网络端口号的服务
- inetd,侦听网络接口,取得来自网络对各种网络服务器的请求
- cron,在定期安排的时期和时间执行命令

内核守护进程以无控制终端方式启动。用户层守护进程缺少控制终端可能是调用了setsid的结果。

大多数用户层守护进程都是进程组的组长进程和会话的受进程,而且是这些进程组和会话中的唯一进程。

用户层守护进程的父进程市init进程。

编程规则

  • 首先要调用umask将文件模式创建屏蔽字设置为一个已知值(通常是0)
  • 调用fork,然后使父进程exit
  • 调用setsid创建一个新会话
  • 将当前工作目录改为根目录
  • 关闭不再需要的文件描述符。
  • 某些守护进程打开/dev/null使其具有文件描述符0,1,2

例子程序daemonize中有两次fork:

//...
// Become a session leader to lose controlling TTY.
if ((pid = fork()) < 0)
    err_quit("%s: con't fork", cmd);
else if (pid != 0)
    exit(0);
setsid();

// Ensure future opens won't allocate controlling TTYS.
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) < 0)
    printf("%s: can't ignore SIGHUP", cmd);
if ((pid = fork()) < 0)
    printf("%s: can't fork", cmd);
else if (pid != 0)
    exit(0);

为什么是SIGHUB信号?

大多数用户进程(守护进程)调用syslog(3)函数来产生日志消息。日志级别,DEBUG->INFO->NOTICE->WARNING->ERR->CRIT->ALERT->EMERG

单实例守护进程

使用文件和记录锁机制,搞一把写锁。

#define LOCKFILE "/var/run/daemon.pid"
#define LOCKMODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

//...
fd = open(LOCKFILE, O_RDWR | O_CREAT, LOCKMODE);
//...

守护进程读取配置文件

守护进程如何让init重新启动该进程?/etc/inittab中为该进程包括respawn记录项。

某些守护进程将捕捉SIGHUP信号,当接受到此信号时reload配置文件。守护进程并不与终端想结合,它们是无控制终端的会话首进程或者孤儿进程组的成员,所以守护进程没有理由期望接收SIGHUP。(正是基于这样的理由,书中利用重新恢复SIGHUP为默认处理方式SIG_DFL,然后当一个线程sigwait到一个SIGHUP的时候,进程reload配置)【疑问:对信号的屏蔽,是意味着不会给进程/线程发送相应的信号,还是进程可以收到这些信号但是不作任何处理?“屏蔽”和“忽略”各自的意思?】

//...
// Restore SIGHUP default and block all signals.
sa.sa_handler = SIG_DFL;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) < 0)
    err_quit("can't restore SIGHUP default\n");
sigfillset(&mask);
if ((err = pthread_sigmask(SIG_BLOCK, &mask, NULL)) != 0)
{
    err_exit("SIG_BLOCK error\n");
    exit(1);
}
//...

这段代码注意,首先要理解调用sigaction对SIGHUP的处理(实际做了什么事?留到复习信号的时候再理解),然后是调用pthread_sigmask对所有的信号进行了屏蔽?(主线程屏蔽了之后,之后创建的子线程也继承了这种屏蔽)

SIGHUP和SIGTERM的默认动作是终止进程。阻塞了这些信号之后,这两个信号被发送到守护进程的时候,守护进程不会消亡。

单线程读取配置文件的处理方式,仍然是理解对信号的处理:

//...
// Handle signals of interest.
sa.sa_handler = sigterm;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGHUP);
sa.sa_flags = 0;
if (sigaction(SIGTERM, &sa, NULL) < 0)
{
    //...syslog and exit
}

sa.sa_handler = sighup;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGTERM);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) < 0)
{
    // ...syslog and exit
}
//...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值