Linux下的进程(六):守护进程 - HeavyYuan/A-CD-Record-Management-System GitHub Wiki
一、成为守护进程
守护进程(Daemon)的生命周期是系统的生命周期,其在后台运行,没有控制终端。
普通进程成为守护进程的过程,称为daemonize
1. 设定umask
一般会设置成0,也可以根据需求自行设定。
2. 调用fork, 然后使父进程exit
1)通常情况下,用户是从shell下通过命令启动了守护进程,我们需要这条命令快速返回,而父进程exit可以满足这一点。 2)fork之后,子子进程不是进程组组长,为后续setsid做铺垫
3. 调用setsid创建新会话
只有非进程组组长,才能调用会话id
上诉子进程调用setsid,成为会话首进程,并且切断与已有的控制终端的连接(表现为tpgrp = -1)
此时,该子进程已经成为后台进程。
4. 再次fork,父进程退出
守护进程都没有控制终端,第3点的子进程是会话首进程,其可以获取到控制终端
为了避免其再次获取控制终端,需要再次fork,此时子进程就不在是会话首进程了。
5. 更改当前工作目录
从父进程继承的当前工作目录可能在一个挂载的文件系统中,所以该文件系统不能被卸载
一般做法是,将当前工作目录改成为根目录,或者是一个指定的目录.
6. 关闭继承父进程的所有文件描述符
因为没有exec,所以close_on_exec标志不能使用
这里方法是,在fork之前获取最高文件描述符值(getrlimit), fork之后全部关闭。
7. 将描述符0,1,2 attach 到/dev/null
后续例程在读写标准输入、输出、错误时,没有任何效果
此时的进程已经没有控制终端,对使用终端的用户不构成影响。所以个人任务此步骤不是很必要。
8. 初始化日志输出(rsyslog)
此时无法输出到终端,需要有日志途径
二、单例模式
通过创建锁文件来实现,如:/var/run/daemon.pid
文件中是daemon的进程ID
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 (flock(fd,LOCK_EX|LOCK_NB) < 0) {
if (errno == EWOULDBLOCK) { /*locked*/
close(fd);
return(1);
}
syslog(LOG_ERR, "can't lock %s: %s", LOCKFILE, strerror(errno));
exit(1);
}
三、守护进程的惯例
- 锁文件用/var/run/procg_name.pid
- 配置文件在/etc/name.conf
- 可以手动命令行启动 or 系统初始化脚本(/etc/rc* 或 /etc/init.d/*)启动
- 通过信号处理来重读配置文件