如何创建守护进程?
2022/1/15 7:04:14
本文主要是介绍如何创建守护进程?,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1.守护进程创建步骤
守护进程是没有终端的进程, 运行在后台, 常在系统引导时启动. 那么如何创建守护进程呢?
参照APUE 13.3, 创建守护进程步骤:
- 调用umask设置进程创建文件的权限屏蔽字(umask), 便于守护进程创建文件
umask通常设为0, 如果调用库函数创建文件, 可设置为007 - 调用fork, 父进程exit
因为要调用setsid创建会话, 需要确保调用进程(子进程)不是进程组组长, fork子进程可以确保这点.
PS:不确定程序以何种方式启动,有可能是进程组组长,也有可能不是。 - 调用setsid创建新会话
子进程调用setsid, 成为新会话首进程, 新进程组组长, 断开终端连接。
PS:对进程组组长调用setsid,会调用失败,返回-1,errno被设置。因此,必须先通过fork+父进程exit,这样子进程会成为孤儿进程,被init收养,从而确保子进程不是进程组长。 - 再次调用fork, 父进程exit(可选)
非必须, 主要是为了确保进程无法通过open /dev/tty 再次获得终端, 因为调用open时, 系统会默认为会话首进程创建控制终端。 - 调用chdir, 将当前工作目录更改为根目录
守护进程一般长期存在, 守护进程存在时, 无法卸载工作目录. 为避免这种情况, 更改当前工作目录为根目录("/"). - 调用close, 关闭所有不需要的文件描述符
open_max, getrlimit, sysconf(_SC_OPEN_MAX) 这3个函数都可以获得文件描述符最高值 - 打开/dev/null文件, 让文件描述符0,1,2指向该文件
可以有效防止产生意外效果
2. 创建守护进程代码
自定义函数daemonize将一个进程转化为守护进程
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <sys/resource.h> #include <sys/time.h> #include <errno.h> #include <unistd.h> #include <syslog.h> #include <signal.h> #include <sys/types.h> #include <sys/stat.h> #include <stdarg.h> #define MAXLINE 200 static void err_doit(int errnoflag, int error, const char *fmt, va_list ap) { char buf[MAXLINE]; // 将格式化串fmt (参数ap) 转换成字符串存放到buf vsnprintf(buf, MAXLINE - 1, fmt, ap); if (errnoflag) { // snprintf最后一个参数是const char*, vsnprintf最后一个参数是va_list, 功能一样 snprintf(buf + strlen(buf), MAXLINE - strlen(buf) - 1, ": %s", strerror(error)); } strcat(buf, "\n"); // buf末尾粘贴 "\n", 并以'\0'结束 fflush(stdout); // 以防stdout, stderr是相同设备, 先冲刷stdout fputs(buf, stderr); } /** * 与系统调用相关的致命错误 * 打印消息和终止程序 */ static void err_quit (const char *fmt, ...) { va_list ap; // va_start, va_end 配对获取可变参数..., 存放到va_list ap中 va_start(ap, fmt); err_doit(0, 0, fmt, ap); va_end(ap); exit(1); } /** * 将一个进程转换为守护进程 * 步骤: * 1. 调用umask设置创建文件权限的umask * 2. 调用fork, 让父进程退出, 子进程成为孤儿进程 * 3. 调用setsid, 创建新会话, 子进程成为新会话首进程以及进进程组组长, 断开控制终端 * 4. 再次调用fork, 并让父进程退出, 子进程不是会话首进程(防止再次获得控制终端) * 5. 将当前工作目录修改为根目录, 防止无法卸载目录 * 6. 关闭不需要的文件描述符 * 7. 打开/dev/null, 使其具有文件描述符0,1,2 */ void daemonize(const char *pname) { int i, fd0, fd1, fd2; pid_t pid; struct rlimit rl; struct sigaction sa; // 1. 调用umask修改创建文件权限的umask umask(0); if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { // <=> sysconf(_SC_OPEN_MAX); err_quit("%s: getrlimit error", pname); } // 2. 调用fork, 父进程退出, 子进程称为孤儿被init进程收养 if ((pid = fork()) < 0) err_quit("%s: fork error", pname); else if (pid > 0) { // 父进程 exit(0); } // 3. 调用setsit, 创建新会话, 子进程成为首进程 setsid(); /* 发送SIGHUP信号情形: 1)终端关闭时, 信号被发送到session首进程, 以及作为job提交的进程(shell 以&方式运行的进程) 2)session首进程退出时, 该信号被发送到同session的所有前台进程 3)若父进程退出, 导致进程组成为孤儿进程组, 且该进程组中有进程处于停止状态(收到SIGSTOP信号或SIGSTP), 该信号会被发送到进>程组每个成员 */ sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGHUP, &sa, NULL) < 0) err_quit("%s: can't ignore SIGHUP", pname); // 4. 再次调用fork if ((pid = fork()) < 0) err_quit("%s: fork error", pname); else if (pid > 0) exit(0); // 5. 改变工作目录为 "/"(根目录) if (chdir("/") < 0) err_quit("%s: can't change directory to /", pname); // 6. 关闭不需要的文件描述符 if (rl.rlim_max == RLIM_INFINITY) rl.rlim_max = 1024; for (i = 0; i < rl.rlim_max; ++i) close(i); #if 1 // 7. 附加文件描述符0,1,2到 /dev/null // 因为前面已经关闭了所有文件描述符, 因此重新open, dup得到的文件描述符是递增的 fd0 = open("/dev/null", O_RDWR); fd1 = dup(0); fd2 = dup(0); #else open("/dev/null", O_RDONLY); open("/dev/null", O_RDWR); open("/dev/null", O_RDWR); #endif /* 建立到syslog的连接 LOG_CONS: 若无法发送到syslogd守护进程则登记到控制台 LOG_DAEMON: 标识消息发送进程的类型为 系统守护进程 */ openlog(pname, LOG_CONS, LOG_DAEMON); /* 文件描述符异常检查: 正常情况fd0, fd1, fd2应该分别是0,1,2 */ if (fd0 != 0 || fd1 != 1 || fd2 != 2) { syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0, fd1, fd2); exit(1); } }
main函数
#include <stdio.h> #include <unistd.h> #include "daemonize.h" int main() { char *s = "mydaemonize"; printf("ready to convert a normal process into a daemonize process\n"); daemonize(s); while(1) sleep(1); return 0; }
检查守护进程
编译, 运行新建的守护进程a.out
$ ./a.out $ ps -efj UID PID PPID PGID SID C STIME TTY TIME CMD martin 12596 1614 12595 12595 0 23:27 ? 00:00:00 ./a.out $ ps -efj | grep 12595 martin 12596 1614 12595 12595 0 23:27 ? 00:00:00 ./a.out
终结守护进程
用kill -9命令
$ kill -9 12596
这篇关于如何创建守护进程?的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南