什么是守护进程?
2024-01-10 11:49:56
守护进程
守护进程(Daemon Process)是在后台运行的一种特殊类型的进程,通常不与任何终端关联。它们独立于用户会话,通常在系统启动时启动,并持续运行以执行一些特定的任务或服务。守护进程是为了提供系统服务、执行定期任务、监听网络请求等目的而设计的。
1. 进程组
多个进程的集合就是进程组, 这个组中必须有一个组长, 组长就是进程组中的第一个进程,组长以外的都是普通的成员,每个进程组都有一个唯一的组ID,进程组的ID和组长的PID是一样的。
相关函数
得到当前进程所在进程组的组ID
pid_t getpgrp(void);
获取指定的进程所在的进程组的组ID,参数 pid 就是指定的进程
pid_t getpgid(pid_t pid);
将某个进程移动到其他进程组中或者创建新的进程组
int setpgid(pid_t pid, pid_t pgid);
参数:
- pid: 某个进程的进程ID
- pgid: 某个进程组的组ID
- 如果pgid对应的进程组存在,pid对应的进程会移动到这个组中, pid != pgid
- 如果pgid对应的进程组不存在,会创建一个新的进程组, 因此要求 pid == pgid, 当前进程就是组长了
返回值:函数调用成功返回0,失败返回-1
2. 会话
会话(session)是由一个或多个进程组组成的,一个会话可以对应一个控制终端, 也可以没有。一个普通的进程可以调用setsid()
函数使自己成为新 session 的领头进程(会长),并且这个 session 领头进程还会被放入到一个新的进程组中。先来看一下setsid()函数的原型:
#include <unistd.h>
// 获取某个进程所属的会话ID
pid_t getsid(pid_t pid);
// 将某个进程变成会话 =>> 得到一个守护进程
// 使用哪个进程调用这个函数, 这个进程就会变成一个会话
pid_t setsid(void);
3. 创建守护进程
创建一个守护进程的过程通常包括以下步骤:
- fork(): 在父进程中调用
fork
创建子进程,并在子进程中执行后续的步骤。 - setsid(): 在子进程中调用
setsid
函数创建一个新的会话,并使子进程成为该会话的领头进程(session leader)。 - fork() again: 为了确保守护进程不会在将来打开终端时重新获得控制,再次在子进程中调用
fork
,并使父进程退出,这样子进程就不再是会话的领头进程。 - chdir(): 切换工作目录到一个合适的目录,防止当前工作目录被卸载。
- umask(): 调用
umask
设置文件创建掩码,以确保守护进程创建的文件具有适当的权限。 - 关闭文件描述符: 关闭不再需要的文件描述符,防止它们在后续的操作中干扰。
- 执行核心工作: 执行守护进程的实际工作。
- 处理信号: 可选步骤,可以设置信号处理函数,以确保守护进程在收到特定信号时能够执行适当的操作。
下面是一个简单的 C 语言守护进程创建示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void daemonize() {
pid_t pid, sid;
// 创建子进程
pid = fork();
// 父进程退出,子进程继续执行
if (pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid > 0) {
exit(EXIT_SUCCESS);
}
// 创建新的会话
sid = setsid();
if (sid < 0) {
perror("setsid");
exit(EXIT_FAILURE);
}
// 再次创建子进程,防止获取控制终端
pid = fork();
if (pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid > 0) {
exit(EXIT_SUCCESS);
}
// 切换工作目录
if (chdir("/") < 0) {
perror("chdir");
exit(EXIT_FAILURE);
}
// 设置文件掩码
umask(0);
// 关闭文件描述符
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// 执行核心工作
// ...
// 处理信号(可选)
// signal(SIGTERM, handler_function);
// signal(SIGINT, handler_function);
// ...
}
int main() {
// 创建守护进程
daemonize();
// 守护进程的核心工作
while (1) {
// ...
}
return 0;
}
这个例子中,daemonize
函数包含了创建守护进程所需的步骤。注意,实际上守护进程的核心工作部分是一个简单的无限循环,我们可以根据实际需求来添加守护进程的具体逻辑。
文章来源:https://blog.csdn.net/f593256/article/details/135498508
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!