一、什么是守护进程?
守护进程,也就是通常说的 Daemon 进程,是后台运行的、没有控制终端与之相连的进程。它是一个生存期较长的进程,周期性地执行某种任务或等待处理某些发生的事件。
系统服务一般也是守护进程,由systemd管理和启动的,通常会配成开机启动。
二、守护进程的特点
- 后台运行,没有控制终端( TTY 为 ?),终端进程组ID(TPGID)为 -1。
- 退出session(终端窗口)之后,守护进程会继续运行(即不会接收SIGHUP),这是判定是否为“守护进程”的依据。
- 系统服务进程甚至不受用户注销的影响。
三、如何开发守护进程
3.1 纯编码实现
int init_daemon(void) {
int pid;
int i;
// 1)屏蔽一些控制终端操作的信号:为了防止守护进行在没有运行起来前,控制终端受到干扰退出或挂起
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP ,SIG_IGN);
// 2)在后台运行
if( pid=fork() ){ // 父进程
exit(0); //结束父进程,子进程继续
}else if(pid< 0){ // 出错
perror("fork");
exit(EXIT_FAILURE);
}
// 3)使子进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离
setsid();
// 4)禁止子进程重新打开控制终端
if( pid=fork() ){ // 父进程
exit(0); // 结束第一子进程,第二子进程继续(第二子进程不再是会话组长)
}else if(pid< 0){ // 出错
perror("fork");
exit(EXIT_FAILURE);
}
// 5)关闭打开的文件描述符,NOFILE 为文件描述符最大个数,不同系统有不同限制
for(i=0; i< NOFILE; ++i){
close(i);
}
// 6)改变当前工作目录
chdir("/tmp");
// 7)重设文件创建掩模
umask(0);
// 8)处理 SIGCHLD 信号,防止子进程将成为僵尸进程
signal(SIGCHLD,SIG_IGN);
return 0;
}
int main(int argc, char *argv[])
{
init_daemon();
while(1) ;
return 0;
}
3.2 调用 daemon(0, 0)
int daemon(int nochdir,int noclose)
nochdir参数用于指定是否改变工作目录,如果给它传递0,则工作目录将被设置为“/”(根目录),否则继续使用当前工作目录。
noclose参数为0时,标准输入、标准输出和标准错误输出都被重定向到/dev/null文件,否则依然使用原来的设备。
该函数成功时返回0,失败返回-1,并设置errno。
3.3 使用 nohup 启动
nohup命令对进程做了三件事:
- 阻止SIGHUP信号发到这个进程。
- 关闭标准输入。该进程不再能够接收任何输入,即使运行在前台。
- 重定向标准输出和标准错误到文件nohup.out。
nohup命令实际上将子进程与它所在的session分离了。但不会自动把进程变成“后台任务”,所以命令必须加上 & 。
四、如何配置系统服务
1、在 /etc/systemd/system/ 下添加服务脚本文件(如 mytest.service)
[Unit]
Description=服务描述
After=udev.service(设置启动顺序,应该在哪些服务之后启动)
Before=(设置启动顺序,应该在哪些服务之前启动)
Requires= (设置强依赖关系,如果强依赖服务启动失败或异常退出,那么当前服务也必须退出)
Wants= (设置弱依赖关系)
[Service]
Type=forking(启动类型,包括simple,exec,forking,oneshot,dbus,notify,idle)
EnvironmentFile=所需环境变量文件或参数文件
ExecStart=启动命令(需指定全路径)
ExecStop=停止命令(需指定全路径)
KillMode=control-group
User=以什么用户执行命令
Restart=always()
RestartSec=3s
[Install]
WantedBy=multi-user.target(表示该服务所在的Target(服务组),常用的有:1)multi-user.target,表示多用户命令行状态;2)graphical.target,表示图形用户状态,它依赖于 multi-user.target)
2、设置自启动
# 添加执行权限
chmod +x /etc/systemd/system/mytest.service
# 添加或修改配置文件后,需要重新加载
systemctl daemon-reload
# 设置自启动,实质就是在 /etc/systemd/system/multi-user.target.wants/ 添加服务文件的链接
systemctl enable mytest.service