C++/Linux 创建daemon进程

如何使用 C++ 在 Linux 环境下创建一个守护进程

在 Linux 环境下,守护进程是一种在后台运行,不与任何终端关联,独立于控制终端并且周期性执行任务或等待处理某些事件的进程。以下是使用 C++ 编写将一个进程转换为守护进程的示例代码,并对关键步骤进行解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstdlib>
#include <cerrno>

// 函数用于将当前进程转换为守护进程
bool daemonize() {
// 第一步:创建子进程,父进程退出
pid_t pid = fork();
if (pid < 0) {
std::cerr << "fork error: " << errno << std::endl;
return false;
} else if (pid > 0) {
// 父进程直接退出,让子进程继续执行
exit(EXIT_SUCCESS);
}

// 此时子进程成为新的会话组长和进程组长
// 第二步:创建新会话
pid = setsid();
if (pid < 0) {
std::cerr << "setsid error: " << errno << std::endl;
return false;
}

// 第三步:改变工作目录
if (chdir("/") < 0) {
std::cerr << "chdir error: " << errno << std::endl;
return false;
}

// 第四步:设置文件权限掩码
umask(0);

// 第五步:关闭标准输入、输出和错误输出,并重新定向到/dev/null
close(STDIN_FILENO);
int fd = open("/dev/null", O_RDWR);
if (fd != STDIN_FILENO) {
return false;
}
if (dup2(fd, STDOUT_FILENO) != STDOUT_FILENO) {
return false;
}
if (dup2(fd, STDERR_FILENO) != STDERR_FILENO) {
return false;
}
if (fd > STDERR_FILENO) {
close(fd);
}

return true;
}

int main() {
if (daemonize()) {
std::cout << "Process has been daemonized successfully." << std::endl;
// 这里可以编写守护进程的具体任务逻辑,比如定时执行某些操作
while (true) {
// 模拟守护进程执行任务,这里简单睡眠10秒
sleep(10);
std::cout << "Daemon is working..." << std::endl;
}
} else {
std::cerr << "Failed to daemonize the process." << std::endl;
}
return 0;
}

代码解释

  1. 创建子进程并退出父进程:使用 fork() 函数创建一个子进程。父进程调用 exit(EXIT_SUCCESS) 退出,这样子进程就不再是控制终端的前台进程,并且可以避免在父进程退出时,子进程成为孤儿进程而被 init 进程收养并受到不必要的终端控制。

  2. 创建新会话:调用 setsid() 函数,使子进程成为新会话的组长,同时也是新进程组的组长。这样做可以让守护进程脱离原会话的控制,并且不再有控制终端,从而实现后台独立运行。

  3. 改变工作目录:使用 chdir("/") 将当前工作目录更改为根目录 / 。这是为了防止守护进程长时间运行时,其工作目录所在的文件系统被卸载而导致进程出现异常。也可以根据实际需求将工作目录设置为其他合适的目录。

  4. 设置文件权限掩码:通过 umask(0) 将文件权限掩码设置为 0,这样守护进程在创建文件或目录时,可以完全按照指定的权限来创建,而不会受到默认掩码的影响。

  5. 关闭并重新定向标准文件描述符:守护进程不应该与终端有交互,所以关闭标准输入(STDIN_FILENO)、标准输出(STDOUT_FILENO)和标准错误输出(STDERR_FILENO),并将它们重新定向到 /dev/null/dev/null 是一个特殊的设备文件,写入它的数据会被丢弃,读取它会立即返回文件结束标志。

int daemon(int nochdir,int noclose)

nochdir: 用于指定是否改变当前的工作目录, 如果 为0 则将工作目录设置为根目录(”/“), 否则依旧使用当前目录作为工作目录

noclose: 参数为0,则将标准输入输出和标准错误输出重定向至”/dev/null“文件