如何使用 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);
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) { sleep(10); std::cout << "Daemon is working..." << std::endl; } } else { std::cerr << "Failed to daemonize the process." << std::endl; } return 0; }
|
代码解释
-
创建子进程并退出父进程:使用 fork() 函数创建一个子进程。父进程调用 exit(EXIT_SUCCESS) 退出,这样子进程就不再是控制终端的前台进程,并且可以避免在父进程退出时,子进程成为孤儿进程而被 init 进程收养并受到不必要的终端控制。
-
创建新会话:调用 setsid() 函数,使子进程成为新会话的组长,同时也是新进程组的组长。这样做可以让守护进程脱离原会话的控制,并且不再有控制终端,从而实现后台独立运行。
-
改变工作目录:使用 chdir("/") 将当前工作目录更改为根目录 / 。这是为了防止守护进程长时间运行时,其工作目录所在的文件系统被卸载而导致进程出现异常。也可以根据实际需求将工作目录设置为其他合适的目录。
-
设置文件权限掩码:通过 umask(0) 将文件权限掩码设置为 0,这样守护进程在创建文件或目录时,可以完全按照指定的权限来创建,而不会受到默认掩码的影响。
-
关闭并重新定向标准文件描述符:守护进程不应该与终端有交互,所以关闭标准输入(STDIN_FILENO)、标准输出(STDOUT_FILENO)和标准错误输出(STDERR_FILENO),并将它们重新定向到 /dev/null 。/dev/null 是一个特殊的设备文件,写入它的数据会被丢弃,读取它会立即返回文件结束标志。
int daemon(int nochdir,int noclose)
nochdir: 用于指定是否改变当前的工作目录, 如果 为0 则将工作目录设置为根目录(”/“), 否则依旧使用当前目录作为工作目录
noclose: 参数为0,则将标准输入输出和标准错误输出重定向至”/dev/null“文件