linux 守护进程的实现

文章目录

  • 1. 守护进程及实现步骤
  • 2. 使用fork 方式创建守护进程
  • 3. 使用daemon 函数创建

1. 守护进程及实现步骤

特点:

  • 长期运行:守护进程是一种生存期很长的一种进程,它们一般在系统启动时开始运行,除非强行终止,否则直到系统关机都会保持运行, 不受用户登录注销的影响。
  • 与控制终端脱离:Linux 中,系统与用户交互的界面称为终端,每一个从终端开始运行的进程都会依附于这个终端这个终端也就是会话的控制终端。当控制终端关闭的时候,该会话就会退出,由此终端运行的所有进程都会被终止,所以普通进程都是和运行该进程的终端相绑定的;但守护进程能突破这种限制,它脱离终端并且在后台运行,脱离终端的目的是为了避免进程在运行的过程中的所产生的信息在终端显示,并且进程也不会被任何终端所产生的信息所打断。
    实现:
  • 1、 创建子进程、终止父进程
    父进程调用 fork()创建子进程,然后使用 exit()退出父进程,这样做实现了下面几点。
    (1)如果该守护进程是作为一条简单地 shell 命令启动,那么父进程终止会让 shell 认为这条命令已经执行完毕。
    (2)虽然子进程继承了父进程的进程组ID,但它有自己独立的进程ID,这保证了子进程不是一个进程组的组长进程,这是下面将要调用 setsid 函数的先决条件!
  • 2、子进程调用 setsid 创建会话
    这步是关键,由于之前子进程并不是进程组的组长进程,所以调用 setsid()会使得子进程创建一个新的会话,子进程成为新会话的首领进程,同时也创建了新的进程组、子进程成为组长进程,此时创建的会话将没有控制终端。所以这里调用 setsid 有三个作用:
    (1)让子进程摆脱原会话的控制
    (2)让子进程摆脱原进程组的控制
    (3)让子进程摆脱原控制终端的控制。
    在调用 fork 函数时,子进程继承了父进程的会话、进程组、控制终端等,虽然父进程退出了,但原先的会话期、进程组、控制终端等并没有改变,因此,那还不是真正意义上使两者独立开来。setsid 函数能够使子进程完全独立出来,从而脱离所有其他进程的控制。
  • 3、 将工作目录更改为根目录
    子进程是继承了父进程的当前工作目录,由于在进程运行中,当前目录所在的文件系统是不能卸载的,这对以后使用会造成很多的麻烦。因此通常的做法是让“/”作为守护进程的当前目录,当然也可以指定其它目录来作为守护进程的工作目录。
  • 4、重设文件权限掩码 umask
    文件权限掩码 umask 用于对新建文件的权限位进行屏蔽,由于使用 fork 函数新
    建的子进程继承了父进程的文件权限掩码,这就给子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为 0,确保子进程有最大操作权限、这样可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是 umask,通常的使用方法为 umask(0)。
  • 5、关闭不再需要的文件描述符
    子进程继承了父进程的所有文件描述符,这些被打开的文件可能永远不会被守护进程(此时守护进程指的就是子进程,父进程退出、子进程成为守护进程)读或写,但它们一样消耗系统资源,可能导致所在的文件系统无法卸载,所以必须关闭这些文件,这使得守护进程不再持有从其父进程继承过来的任何文件描述符。
  • 6、 将文件描述符号为 0、1、2 定位到/dev/null
    将守护进程的标准输入、标准输出以及标准错误重定向到/dev/null,这使得守护进程的输出无处显示、也无处从交互式用户那里接收输入。
  • 7、 其它:忽略 SIGCHLD 信号
    处理 SIGCHLD 信号不是必须的,但对于某些进程,特别是并发服务器进程往往是特别重要的,服务器进程在接收到客户端请求时会创建子进程去处理该请求,如果子进程结束之后,父进程没有去 wait 回收子进程,则子进程将成为僵尸进程;如果父进程 wait 等待子进程退出,将又会增加父进程的负担、也就是增加服务器的负担,影响服务器进程的并发性能,在 Linux 下,可以将 SIGCHLD 信号的处理方式设置为SIG_IGN,也就是忽略该信号,可让内核将僵尸进程转交给 init 进程去处理,这样既不会产生僵尸进程、又省去了服务器进程回收子进程所占用的时间。

2. 使用fork 方式创建守护进程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <string.h>/*** 守护进程,使用子进程实现, 进行日志记录测试
*/int main(void) {pid_t pid;int fd;int fdLog;time_t t;int i;// 创建子进程pid = fork();if (pid < 0) {perror("fork error");exit(-1);} else if (0 < pid) {// 父进程exit(0);            // 直接退出父进程}//子进程 // 1. 创建新的会话,脱离终端控制if (0 > setsid()) {perror("setid error");exit(-1);}// 2. 设置当前工作目录为根目录if (0 > chdir("/")) {perror("chdir error");exit(-1);}// 3. 重新设置文件权限掩码umask(0);// 4. 关闭所有文件描述符for (i = 0; i < sysconf(_SC_OPEN_MAX); i++) {close(i);}// 5. 将文件描述符0,1,2(标准输入,输出,错误)重定向到/dev/nullpid = open("/dev/null", O_RDWR);dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);/* 6.忽略 SIGCHLD 信号 */signal(SIGCHLD, SIG_IGN);// 进入到守护进程for (;;) {fdLog = open("/home/orangepi/workespace-linux-code/daemon/daemon.log", O_WRONLY | O_CREAT | O_APPEND, 0644);if (fdLog == -1) {printf("open log file error");}t = time(NULL);char *buf = asctime(localtime(&t));write(fdLog, buf, strlen(buf));    // 在日志中记录时间close(fdLog);sleep(10);}exit(0);
}

测试结果:
在这里插入图片描述
在这里插入图片描述

3. 使用daemon 函数创建

#include <unistd.h>
int daemon(int nochdir, int noclose);
参数:- nochdir:为0时表示将当前目录更改至“/- noclose:为0时表示将标准输入、标准输出、标准错误重定向至“/dev/null”
返回值:- 成功: 返回0- 失败: 返回-1
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>/*** 守护进程,使用daemon创建, 定时写文件测试
*/
static int flag = 1;// 使用SIGQUIT 信号控制守护进程的退出,由于守护进程在后台运行,打印信息是看不到的
void handler(int sig) {printf("quit signal %d\n", sig);flag = 0;
}int main(void) {time_t t;int fd;// 创建守护进程if (-1 == daemon(0, 0)) {printf("daemon error\n");exit(-1);}// 设置信号处理函数struct sigaction act;act.sa_handler = handler;sigemptyset(&act.sa_mask);   // 初始化信号掩码为空act.sa_flags = 0;if (sigaction(SIGQUIT, &act, NULL)) {printf("sigaction error\n");exit(-1);}// 进入守护进程while (flag) {fd = open("/home/orangepi/workespace-linux-code/daemon/daemon.log", O_WRONLY | O_CREAT | O_APPEND, 0644);if (fd == -1) {printf("open log file error");}t = time(NULL);char *buf = asctime(localtime(&t));write(fd, buf, strlen(buf));    // 在日志中记录时间close(fd);sleep(10);}return 0;
}

测试:
在这里插入图片描述
在这里插入图片描述

注: 部分参考自正点原子的linux C应用开发指南

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/641320.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Day 31 贪心算法理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和

贪心算法理论基础 ​ 贪心算法的本质&#xff1a;选择每一个阶段的局部最优&#xff0c;从而达到系统的整体最优&#xff1b; ​ 贪心的套路就是没有套路&#xff0c;最好的策略就是举反例&#xff0c;因为大多数时候并不要求严格证明&#xff0c;只需要得到普遍性结论即可&a…

echart-better基于最新的echarts5.5标题旋转功能

使用教程以及相关的echarts-better最新的包在这里&#xff1a;https://edu.csdn.net/course/detail/24569 echarts在侧边竖向展示标题&#xff0c;以及次标题 主标题和次标题进行旋转&#xff0c;适用于移动端或其他场景。

Promise.all 的方法还没执行完就执行了.then

碰见一个问题&#xff0c;接盘了一个有问题的页面修改。 改变日期后 查询很多数据再去重新加载页面上的数据显示相关的组件。 问题就来了。 加载异常捏…… 最后我一通查&#xff1a; 重点来了 是因为这个Promise.all(数组)&#xff0c;里边这个数组的问题。现在是在数据中…

O2O电商接口解决方案||主流电商|跨境电商API接口应用场景及接入

01 涉及主流电商平台API数据采集接口 电商接口&#xff1a;淘宝&#xff0c;天猫&#xff0c;京东&#xff0c;拼多多&#xff0c;1688&#xff0c;抖音&#xff0c;微店&#xff0c;快手 跨境电商&#xff1a;LAZADA&#xff0c;速卖通&#xff0c;亚马逊&#xff0c;阿里巴巴…

二叉树中的最长交错路径

题目链接 二叉树中的最长交错路径 题目描述 注意点 每个节点的值在 [1, 100] 之间 解答思路 深度优先遍历整棵树&#xff0c;遍历的同时需要将到达根节点是向左交叉还是向右交叉以及路径长度传递到子树。当根节点是向左交叉遍历而来&#xff0c;子树想和根节点组成路径就只…

带RS485通讯,开关量输入输出,谐波分析等家户工商业储能智能计量电表ADL3000-E-B/KC可出口欧美UL认证

安科瑞薛瑶瑶18701709087 ◉概述 ADL3000-E-B 导轨式多功能电能表&#xff0c;是主要针对电力系统&#xff0c;工矿企业&#xff0c;公用设施的电能统计、管理需求而设计的一款智能仪表&#xff0c;产品具有精度高、体积小、安装方便等优点。集成常见电力参数测量及电能计量及…

剑指offer--调整数字顺序使奇数位于偶数前面

题目描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有的偶数位于数组的后半部分. 算法分析 算法:利用快速排序的一次划分思想&#xff0c;后面的奇数往前移&#xff0c;前面的偶数往后移 时间复杂度 &#xff1a;O(n) 空间…

分布式监控平台---Zabbix

一、Zabbix概述 作为一个运维&#xff0c;需要会使用监控系统查看服务器状态以及网站流量指标&#xff0c;利用监控系统的数据去了解上线发布的结果&#xff0c;和网站的健康状态。 利用一个优秀的监控软件&#xff0c;我们可以&#xff1a; 通过一个友好的界面进行浏览整个…

Appian发布最新版本:通过AI流程自动化推动业务发展

Appian公司于2024年4月16日在弗吉尼亚州麦克莱恩宣布推出Appian平台的最新版本。此版本引入了Process HQ&#xff0c;这是一个集流程挖掘和企业AI于一体的系统&#xff0c;结合了Appian的数据平台。Process HQ为企业运营提供前所未有的可见性&#xff0c;支持数据驱动的决策和流…

使用ERNIE SDK和Comate开发AI“划拳”游戏!

作者晓飞好&#xff0c;飞桨星河社区开发者&#xff0c;金融行业软件开发工程师&#xff0c;对大模型应用开发比较感兴趣。个人主页链接&#xff1a;https://aistudio.baidu.com/personalcenter/thirdview/48323 技术的迅速发展为我们带来了无限的创新可能&#xff0c;大模型技…

Keil中编译无error(有warning),但程序无法运行的一种情况

问题 void Run_Led(void) {HAL_GPIO_TogglePin(RUN_LED_GPIO_Port, RUN_LED_Pin);Delay_ms(500); }void StartDefaultTask(void *argument) {/* USER CODE BEGIN StartDefaultTask */char c;/* Infinite loop */for(;;){while(1) { Run_Led;}...}非常简单的一个程序&#xf…

vue2 顶象 安全 验证码的使用

顶象-业务安全引领者&#xff0c;让数字世界无风险 类似与这样的登录之前的验证 滑动一个盒子了 或者是 顺序点击文字了 等 <template><a-modal:closable"false":visible"show"cancel"handleCancel":maskClosable"true":wid…