linux 守护进程的实现

news2024/11/17 13:41:16

文章目录

  • 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/null
    pid = 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.coloradmin.cn/o/1619795.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《QT实用小工具·三十八》QT炫酷的菜单控件

1、概述 源码放在文章末尾 非常飘逸的 Qt 菜单控件&#xff0c;带有各种动画效果&#xff0c;用起来也十分方便。 无限层级&#xff0c;响应键盘、鼠标单独操作&#xff0c;支持单快捷键。 允许添加自定义 widget、layout&#xff0c;当做特殊的 QDialog 使用。 项目demo演示…

Python写个二维码

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、进入官网下载二、下载一下三.输入代码 前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、进入官网下载 官网 pip insta…

Stable Diffusion WebUI 使用 LoRA 调整风格——详细教程

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里&#xff0c;订阅后可阅读专栏内所有文章。 大家好&#xff0c;我是水滴~~ 本教程旨在深入探讨 LoRA 模型的奥秘&#xff0c;涵盖其基本概念、独特作用以及实操指南。我们将从下载和使用LoRA的步…

配线架与交换机:了解差异和最佳用途

什么是配线架&#xff0c;它与交换机有何不同&#xff1f; 配线架是一种具有多个插孔的设备或单元&#xff0c;用于方便管理电缆连接。 它充当静态交换机&#xff0c;允许轻松连接或断开网络设备&#xff0c;并为所有电缆连接提供集中位置。 另一方面&#xff0c;Switch 是一种…

基于深度学习的车牌识别

如果你认为车牌只是车子的‘名字’&#xff0c;那么是时候让你见识一下&#xff0c;当科技赋予它‘超能力’时会发生什么&#xff1f; 上效果图&#xff1b; 这就是车牌识别的力量&#xff0c;下面是主函数代码&#xff1a; # -*- coding: UTF-8 -*- import argparse import …

为底图发愁? 这里有一份清爽又百搭的底图绘制方法!

图纸不够清爽美观&#xff1f; 图纸表达混乱&#xff0c;重点不够醒目&#xff1f; 图纸的颜色太难调了&#xff0c;怎么调都不满意&#xff1f; ...... 俗话说&#xff0c;好的底图是图纸成功的关键&#xff01; 绝大部分的图纸问题&#xff0c;都和底图有关&#xff01; …

【C语言__指针01__复习篇11】

目录 前言 一、什么是指针 二、计算机中常见的单位 三、CPU是怎样找到一块内存空间的 四、如何得到变量的地址 五、指针变量 六、解引用指针变量的作用 七、指针变量的大小 八、指针变量类型的意义 8.1 指针的解引用 8.2 指针-整数 九、void*指针 十、const修饰变…

自己写的爬虫小案例

网址&#xff1a;aHR0cDovL2pzc2NqZ3B0Lmp4d3JkLmdvdi5jbi8/dXJsPS92aWV3L3dvcmtpbmdVbml0L3dvcmtpbmdVbml0Lmh0bWw 这串代码能够爬取勘察单位企业的详细信息。 import requests import time import csv f open(勘察单位公司信息.csv,w,encodingutf-8,newline) csv_writer …

文件上传漏洞-白名单检测

如何确认是否是白名单检测 上传一张图片与上传一个自己构造的后缀&#xff0c;如果只能上传图片不能上传其它后缀文件&#xff0c;说明是白名单检测。 绕过技巧 可以利用 00 截断的方式进行绕过&#xff0c;包括 %00 截断与 0x00 截断。除此之外如果网站存在文件包含漏洞&…

自动备份的小软件

自动备份的小软件 前几天有个小姐姐和我说&#xff0c;他的硬盘坏了&#xff0c;但是他有没有备份&#xff0c;所以我决定做一个自动备份的软件。 软件整体是使用pythonpyqt5做到。 github链接 软件截图 使用效果 使用方法 教程 流程图 优势 可以很大程度上解决数据丢失…

基于__torch_dispatch__机制的dump方法

基于__torch_dispatch__机制的dump方法 1.参考链接2.原理3.代码4.效果 之前拦截torch和torch.Tensor的办法,在处理backward时,不能看到aten算子的细节.以下基于__torch_dispatch__机制的方案更节约代码,且能看到调用栈 1.参考链接 [原理] (https://dev-discuss.pytorch.org/t…

【vue2】实现微信截图(复制图片)在项目内可粘贴

需求 后台管理在上传图片地方需要将复制的图片粘贴上传 一、添加事件 在原有上传组件的基础上添加 paste事件 二、方法 onPaste(e) {const items (e.clipboardData || window.clipboardData).items;let blob null;for (let i 0; i < items.length; i) {if (items[i].ty…

related_name和related_query_name属性

在Django模型继承中&#xff0c;假如在外键或多对多字段中使用了related_name属性或related_query_name属性&#xff0c;则必须为该字段提供一个独一无二的反向名字和查询名字。但是&#xff0c;这样在抽象基类中一般会引发问题&#xff0c;因为基类中的字段都被子类继承并且保…

WEB攻防-IIS中间件PUT漏洞

IIS6.0 server在web服务扩展中开启了WebDAV&#xff08;Web-based Distributed Authoring and Versioning&#xff09;。WebDAV是一种HTTP1.1的扩展协议。它扩展了HTTP 1.1&#xff0c;在GET、POST、HEAD等几个HTTP标准方法以外添加了一些新的方法&#xff0c;如PUT&#xff0c…

【js】解决自动生成颜色时相邻颜色视觉相似问题的技术方案

解决自动生成颜色时相邻颜色视觉相似问题的技术方案 在进行大规模颜色生成时&#xff0c;特别是在数据可视化、用户界面设计等应用领域&#xff0c;一个常见的挑战是确保相邻颜色在视觉上具有足够的区分度。本文介绍的方法通过结合黄金分割比与饱和度、亮度的周期性变化&#…

Axure实现tab页面切换功能

1. 实现效果 2. 实现原理 创建两个标签&#xff0c;并实现点击时选中状态点击时&#xff0c;设置面板状态 3. 实现步骤 3.1 实现可切换的标签 在页面上拖拽两个矩形作为两个tab标签&#xff0c;并命名 tab1 和 tab2 设置每个矩形的边框显示&#xff0c;只显示下边框即可 …

Oracle使用内部包自定义创建表空间和用户

如果之前有类似的表空间,可以使用dbms自动生成对应的表空间和数据文件 select dbms_metadata.get_ddl(TABLESPACE,ts.tablespace_name) from dba_tablespaces ts; 可以使用类似的 SQL> set echo off SQL> spool /data/logs/create_tablespace.log SQL> select dbms…

【安卓13】解决带GMS编译报super分区空间不足错误

1、错误信息 2、解决方案 不同供应商修改分区大小的文件路径不一样&#xff0c;但是万变不离其宗&#xff0c;根据报错信息全局搜索关键词BOARD_SUPER_PARTITION_SIZE 这里以RK供应商和AML供应商修改为例&#xff1a; &#xff08;1&#xff09;RK改法&#xff1a; 根目录下…

uniapp配置了pages.json 的 tabbar 国际化,小程序切换语言没有实时切换

如上图&#xff0c;按照uniapp官方文档配置了tabbar的国际化 但是微信小程序实时切换语言没有实时刷新 解决方案&#xff1a; 在App.vue中加入以下代码&#xff1a; 在onLaunch中执行方法即可

Springboot+Vue项目-基于Java+MySQL的图书馆管理系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…