简 述: 前面几篇,刚写过了父子进程的实例。这里写一个守护进程 的例子,从了解到运用 setsid()
;守护进程也就是脱离于终端,不需要和用户交流的,不受注销影响的后台程序(可理解为 win 中的服务 )。
[TOC]
本文初发于 “偕臧的小站“,同步转载于此。
编程环境:
💻: uos20
📎 gcc/g++ 8.3
📎 gdb8.0
💻: MacOS 10.14
📎 gcc/g++ 9.2
📎 gdb8.3
守护进程的特点:
- 后台服务程序
- 独立(是脱离于)控制终端的,用户不需要和终端交互
- 周期性的执行某任务
- 不受用户登录注销的影响
- 一般采用 d 结尾的名字(服务)
进程组 - 多个进程:
- 进程组的组长?
- 组长是是组里的第一个进程
- 进程组的 ID == 进程组的组长的 ID
会话 - 多个进程组:
进程组是由多个进程组成,而会话是由多个进程组组成成。
- 创建一个会话注意的事项:
- 不能是组长进程
- 创建会话的进程成为新进程组的组长
- 有些 Linux 版本需要 root 权限执行此操作(Ubuntu 不需要)
- 创建出的新会话会丢弃原有的控制终端
- 一般步骤:先 fork(),父亲死,儿子创建绘画操作(setsid())
- 获取进程所属的会话 ID:
pid_t getsid(pid_t pid);
- 创建一个会话:
pid_t setsid(void);
创建守护进程模型:
下面列出创建一个标准的进程守护模型操作流程:
- fork() 进程,父进程退出 (必须)
- 子进程创建新的会话 (必须)
- 使用
setsid()
- 使用
- 改变当前工作目录 chdir (可选)
- 比如,U 盘插在笔记本,运行 U 盘文件夹里面的可执行程序,然后拔掉,会有一些影响
- 重设文件掩码 (可选)
- 子进程会继承父进程的掩码
- 增加子进程程序操作的灵活性
- umask(0)
- 关闭文件描述符 (可选)
- 节约资源,关闭此进程的 PCB 的文件描述符表中 0、1、2 三个,因为预警不需要和终端交互,故可关闭
- 执行核心工作 (必须)
- 你想让该守护进程干的事情
写一个例子:
对上面的函数使用,写一个例子:写一个守护进程,每隔 2s 获取一次系统时间,写入到文本文件中。
代码示例
#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <sys/time.h> #include <sys/types.h> #include <fcntl.h> #include <signal.h> #include <sys/stat.h> #include <time.h> void func(int no); //用作回调函数,给 signal() 调用 int main(int argc, char *argv[]) { pid_t pid = fork(); if (pid > 0) { _exit(1); //父进程退出 } else if (pid == 0) { setsid(); //子进程创建为会话 chdir("/Users/muli/Desktop/"); //改变进程的工作目录 umask(0); //重设置文件掩码 close(STDIN_FILENO); //关闭和终端的联系,文件描述符 close(STDOUT_FILENO); close(STDERR_FILENO); __sigaction_u sigactu; //设置信号捕捉 sigactu.__sa_handler = func; struct sigaction act; act.sa_flags = 0; act.__sigaction_u = sigactu; sigaddset(&act.sa_mask, SIGQUIT); itimerval time; //设置周期性的定时器 time.it_value.tv_sec = 2; time.it_value.tv_usec = 0; time.it_interval.tv_sec = 1; time.it_interval.tv_usec = 0; sigaction(SIGVTALRM, &act, NULL); //捕捉(下面一行发射的)信号,在 fun() 里面实现 setitimer(ITIMER_VIRTUAL, &time, NULL); //使用定时器,发射信号 while (true); //保持该守护进程不死亡 } return 0; } void func(int no) { time_t currtime; //获取系统当前时间,传出参数 time(&currtime); char* ptr = ctime(&currtime); int fd = open("/Users/muli/Desktop/setsid.txt", O_CREAT | O_WRONLY | O_APPEND, 0777); //写入磁盘文件 write(fd, ptr, sizeof(ptr) + 1); close(fd); }
代码分析:
这里
sigaction(SIGVTALRM, &act, NULL);
和代码setitimer(ITIMER_VIRTUAL, &time, NULL);
这一行,成为互相对应的关系。,使用了周期定时器 setitimer,第一个参数填了三个枚举之一,但是对应的 sigaction 信号捕捉中,就要填写对应的参数(见 man 手册);注意,要先有捕捉函数,后实现信号发射函数,避免出现信号已经发射了,但是捕捉函数还没有实现或执行。
运行效果:
文件掩码是什么?
- linux中的 umask 函数主要用于:在创建新文件或目录时 屏蔽掉新文件或目录不应有的访问允许权限。
- 文件的访问允许权限共有9种,分别是:rwxrwxrwx
- 它们分别代表:用户读 用户写 用户执行 组读 组写 组执行 其它读 其它写 其它执行
- 更多是使用 umask 命令或者 umask() 函数老表示,比如: 777;
下载地址:
欢迎 star 和 fork 这个系列的 linux 学习,附学习由浅入深的目录。