守护进程(Daemon)是 Linux 系统中一种长期运行的后台进程,通常用于执行系统级别的任务或服务。理解守护进程涉及进程组、会话及其与其他进程的关系。本文将详细介绍这些概念及其在 Linux 中的应用。
一、进程组(Process Group)
进程组是一个或多个进程的集合,用于信号传递和终端输入输出控制。进程组中的每个进程都有一个相同的进程组 ID(PGID),PGID 等于进程组组长的进程 ID(PID)。
1. 创建进程组
当一个进程创建新进程时,默认情况下新进程继承其父进程的 PGID。可以通过调用 setpgid
函数来改变进程的 PGID。
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
// 子进程
setpgid(0, 0); // 创建新的进程组
printf("Child process PGID: %d\n", getpgid(0));
} else {
// 父进程
printf("Parent process PGID: %d\n", getpgid(0));
}
return 0;
}
2. 查看进程组
可以使用 ps
命令查看进程的 PGID:
ps -o pid,pgid,cmd
二、会话(Session)
会话是一个或多个进程组的集合。会话由一个会话首领进程创建,该进程成为会话的领导者,并且它的 PID 就是会话 ID(SID)。会话首领进程可以通过调用 setsid
函数创建一个新的会话。
1. 创建会话
创建一个新的会话,将当前进程变为会话领导者:
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t sid = setsid(); // 创建新的会话
if (sid == -1) {
perror("setsid");
return 1;
}
printf("Session ID: %d\n", sid);
return 0;
}
2. 查看会话
可以使用 ps
命令查看进程的 SID:
ps -o pid,sid,cmd
三、守护进程(Daemon)
守护进程是一种在后台运行的进程,通常由系统启动脚本或其他服务管理工具启动。守护进程脱离控制终端,独立于用户的会话。
1. 创建守护进程
创建守护进程的一般步骤包括:
- 创建子进程,终止父进程:确保守护进程不是会话领导者,防止它重新打开控制终端。
- 创建新的会话:使守护进程成为新会话的领导者。
- 改变工作目录:通常将工作目录更改为根目录,以防止锁定任何挂载的文件系统。
- 重设文件权限掩码:防止继承父进程的文件权限。
- 关闭文件描述符:关闭继承的文件描述符。
下面是一个创建守护进程的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
void daemonize() {
pid_t pid;
// 创建子进程
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
if (pid > 0) {
exit(EXIT_SUCCESS); // 父进程退出
}
// 创建新的会话
if (setsid() < 0) {
exit(EXIT_FAILURE);
}
// 捕获、忽略 SIGHUP 信号
signal(SIGHUP, SIG_IGN);
// 创建新子进程,终止父进程,防止重新获得控制终端
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
if (pid > 0) {
exit(EXIT_SUCCESS);
}
// 改变工作目录
if (chdir("/") < 0) {
exit(EXIT_FAILURE);
}
// 重设文件权限掩码
umask(0);
// 关闭文件描述符
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
int main() {
daemonize();
// 守护进程的主循环
while (1) {
// 执行后台任务
sleep(30); // 示例:每隔30秒执行一次任务
}
return EXIT_SUCCESS;
}
四、管理守护进程
1. 使用 systemd
现代 Linux 系统通常使用 systemd
管理守护进程。可以创建一个 systemd
服务文件来管理守护进程。
创建一个示例服务文件 /etc/systemd/system/mydaemon.service
:
[Unit]
Description=My Daemon Service
[Service]
ExecStart=/usr/local/bin/mydaemon
Restart=always
User=nobody
Group=nobody
[Install]
WantedBy=multi-user.target
然后启用并启动服务:
sudo systemctl enable mydaemon
sudo systemctl start mydaemon
2. 使用 init.d
在旧版 Linux 系统中,可以使用 init.d
脚本管理守护进程。创建一个示例脚本 /etc/init.d/mydaemon
:
#!/bin/sh
### BEGIN INIT INFO
# Provides: mydaemon
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: My Daemon Service
### END INIT INFO
case "$1" in
start)
echo "Starting mydaemon"
/usr/local/bin/mydaemon &
;;
stop)
echo "Stopping mydaemon"
pkill mydaemon
;;
restart)
$0 stop
$0 start
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit 0
然后启用并启动服务:
sudo chmod +x /etc/init.d/mydaemon
sudo update-rc.d mydaemon defaults
sudo service mydaemon start