【C语言】守护进程(daemon)的输出到一个文本文件

news2024/9/21 3:39:48

一、常用的守护进程函数

void daemonize ()
{
    //deamonize
    pid_t pid = fork();
    if( pid > 0 )
    {       
        //parent exit
        exit(0);
    }

    //child continue
    setsid();
    chdir("/");
    close(0);
    open("/dev/null", O_RDWR);
    //no env debug
    if(!getenv("debug"))
    {
        close(1);
        close(2);
        dup(0);
        dup(0);
    }
}

这段代码的目的是让一个程序在后台以守护进程(daemon)的形式运行。让我们逐步了解每一行代码的作用:

pid_t pid = fork();

这行代码创建了一个新的进程,这是通过`fork()`系统调用实现的。`fork()`会创建一个和当前进程几乎完全相同的子进程。`fork()`调用会在父进程中返回新创建的子进程的进程ID,在子进程中则返回0。如果返回值大于0,那么代码运行于父进程;如果是0,则表示在子进程中。

if( pid > 0 )
{       
    //parent exit
    exit(0);
}

如果`fork()`的返回值大于0,表示当前代码段在父进程中运行。因为我们的目的是让程序在后台运行,我们不希望保留父进程。所以父进程会调用`exit(0)`正常退出。

//child continue
setsid();

接下来的部分是在子进程中继续执行的。`setsid()`会创建一个新的会话,并设置子进程为这个新会话的领头进程。一个会话可以包含一个或多个进程组;由`setsid`创建的新会话有一个新的进程组,且子进程是这个进程组的领头进程,并且没有控制终端。

chdir("/");

通过`chdir("/")`将当前工作目录更改为根目录("/")。这是因为守护进程通常应该不与任何特定目录关联,尤其是不应该继续驻留在启动它们的目录中,可能会妨碍卸载文件系统等操作。

close(0);
open("/dev/null", O_RDWR);

这两行代码关闭了标准输入(文件描述符0),然后打开`/dev/null`设备用于读写。所有写入到`/dev/null`的数据都会被丢弃,读取`/dev/null`会立即返回文件结束。

if(!getenv("debug"))
{
    close(1);
    close(2);
    dup(0);
    dup(0);
}

最后这部分首先检查是否存在名为"debug"的环境变量。如果没有(`getenv("debug")`返回NULL),则进行以下操作:
- 关闭标准输出(文件描述符1)。
- 关闭标准错误(文件描述符2)。
接下来,调用`dup(0)`复制文件描述符0(也就是之前打开的`/dev/null`),因为在文件描述符1和2被关闭之后,`dup`调用会使用最低的、未被使用的文件描述符号,也就是先是1然后是2,因此这一步相当于重新定向了进程的标准输出到`/dev/null`,然后又将标准错误也重定向到了`/dev/null`。
所以,这段代码的整体作用是生成一个子进程,让它脱离终端和工作目录,且默认情况下把所有的输入、输出重定向到`/dev/null`,使之成为一个后台运行的守护进程。如果设置了“debug”环境变量,则标准输出和错误不会被重定向。

二、守护进程(daemon)的输出到一个文本文件

将守护进程的输出重定向到一个文本文件,需要在代码中用`open`系统调用打开希望输出到的文件,并且用`dup2`或`dup`系统调用将标准输出(文件描述符1)和/或标准错误(文件描述符2)重定向到这个文件描述符上。
以下是将输出重定向到指定的日志文件函数:

#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>

void daemonize ()
{
    // Daemonize
    pid_t pid = fork();
    if (pid > 0) 
    {       
        // Parent exits
        exit(0);
    }
    
    // Child (daemon) continues
    setsid();
    chdir("/");
    
    // Redirect standard file descriptors to /dev/null or a log file
    close(STDIN_FILENO);
    open("/dev/null", O_RDWR); // STDIN

    // Redirect STDOUT and STDERR to a log file
    const char* logFilePath = "/var/log/daemon.log";
    int logFile = open(logFilePath, O_RDWR | O_CREAT | O_APPEND, 0600);

    if (logFile == -1) {
        // Handle error, e.g., exit or print an error message
    } else {
        if (!getenv("debug")) {
            close(STDOUT_FILENO); // Close the standard output
            close(STDERR_FILENO); // Close the standard error
            dup2(logFile, STDOUT_FILENO); // Redirect standard output to the log file
            dup2(logFile, STDERR_FILENO); // Redirect standard error to the log file
        }
        // At this point, whether debugging or not, STDOUT and STDERR go to the log file
    }
    
    if (logFile != STDOUT_FILENO && logFile != STDERR_FILENO) {
        close(logFile); // We don't need this anymore
    }
}

这里我们使用函数`open`创建或打开日志文件。文件被设置为可读写(`O_RDWR`),如果未存在则创建它(`O_CREAT`),并以追加模式打开(`O_APPEND`)。文件权限被设置为0600,即只有拥有者可以读写。
此外,我们会先关闭已打开的标准输出和错误文件描述符,然后使用`dup2`,把 logFile 的文件描述符复制到标准输出和错误的文件描述符上。如果文件的打开操作失败,可能需要适当地处理这个错误,比如打印一个错误信息或者退出程序。
现在,守护进程的标准输出和错误都会被写入到`/var/log/daemon.log`日志文件中。确保程序具有创建和写入该日志文件的权限,尤其是当程序以特权用户(如`root`)运行时。

使用`dup`或`dup2`系统调用只能复制一个现有的文件描述符到另一个文件描述符,不能直接打开新的文件。 如果想重定向守护进程的输出到 /var/log/daemon.log ,需要使用`open`系统调用首先打开这个文件,然后才能用`dup`或`dup2`复制相应的文件描述符。
以下是如何修改守护进程的代码以重定向输出到 /var/log/daemon.log 的示例:

if(!getenv("debug"))
{
    close(1);
    close(2);

    // 打开或创建一个文件用来写入日志
    int log_file = open("/var/log/daemon.log", O_RDWR | O_CREAT | O_APPEND, 0600);

    if (log_file < 0) {
        // 无法打开日志文件,可能是因为权限问题或其他问题
        exit(1);
    }

    // 重定向标准输出到日志文件
    if (dup2(log_file, 1) < 0) {
        // 无法重定向标准输出
        exit(1);
    }

    // 重定向标准错误到日志文件
    if (dup2(log_file, 2) < 0) {
        // 无法重定向标准错误
        exit(1);
    }

    // 现在我们已经复制了日志文件到标准输出和标准错误,关闭原始的日志文件描述符
    if (log_file > 2) {
        close(log_file);
    }
}

在上面的代码中,`open`系统调用用于打开(或创建,如果还不存在的话) /var/log/daemon.log 文件。`O_RDWR` 表示文件是以读写模式打开的,`O_CREAT` 表示如果文件不存在,就创建它。`O_APPEND` 保证所有写操作都是追加到文件末尾,而`0600`表示创建的文件权限(只有拥有者有读写权限)。
dup2系统调用用于把日志文件的文件描述符复制到标准输出(1)和标准错误(2)。如果`dup2`调用成功,它会关闭旧的文件描述符,并将它替换为新的文件描述符。注意,如果发生错误(例如,文件打开失败或`dup2`调用失败),则程序会退出。
最后,检查一下原始的日志文件描述符是否大于2,因为0、1和2是标凑输入、输出和错误。如果原始的描述符大于这些,我们就关闭它,因为标准输出和错误已经被重定向到我们的日志文件。
使用这种方式,守护进程的标准输出和错误都会被记录到 /var/log/daemon.log 文件中。 注意,在生产环境中,直接把日志输出到文件可能是不够的,可能还需使用日志旋转等措施管理日志文件的增长。 

三、dup和dup2

dup 和 dup2 是 Unix 和类 Unix 系统 (如 Linux) 的系统调用,用于复制(duplicate)文件描述符。

dup 系统调用会复制一个旧的文件描述符,返回一个新的文件描述符。新文件描述符指向旧文件描述符所指向的文件。新描述符会是当前未使用的最小值的文件描述符。比如:

int new_fd = dup(old_fd);

在这段代码中,`old_fd` 是旧的文件描述符,`new_fd` 是新创建的文件描述符。新的 new_fd 指向和 old_fd 相同的文件,拥有相同的文件偏移量和访问模式(比如读、写)。

dup2 函数和 dup 类似,也是用来复制文件描述符。不过,`dup2` 允许指定新文件描述符的值。如果指定的值已经是一个打开的文件描述符,`dup2` 会先关闭它,然后再复制。这个操作是原子性的,即系统保证这两步(关闭和复制)是连续执行的,没有其他的调用会插入其中。例如:

dup2(old_fd, TARGET_FD);

这将复制 old_fd,并确保新的文件描述符的值是 TARGET_FD。

在一个守护进程中重定向输出到 /dev/null 使用 dup 是因为它会自动复制到当前未使用的最低的文件描述符上。因为在之前的代码中,标准输入、输出和错误(文件描述符0、1、2)已经关闭,所以调用 dup(0) 将会分别把 /dev/null 分配给标准输出和标准错误 (文件描述符1和2)。
重定向输出到一个具体的文件,如 /var/log/daemon.log,使用 dup2 是因为需要更精确地控制使用哪个文件描述符。比如,想确保标准输出和错误分别被送到文件描述符1和2:

int fd = open("/var/log/daemon.log", O_RDWR | O_CREAT | O_APPEND, 0600);
if (fd < 0) {
    // Error handling
}
dup2(fd, STDOUT_FILENO); // STDOUT_FILENO typically is 1
dup2(fd, STDERR_FILENO); // STDERR_FILENO typically is 2

在这段代码中,`open` 调用创建或打开日志文件,然后 dup2 函数重定向标准输出和错误到这个文件(文件描述符1和2),保证所有守护进程的输出都被写入到指定日志文件中。使用 dup2 而不是 dup 使得能精确指定要被复制的新文件描述符的数值。

对于将守护进程的输出重定向到`/var/log/daemon.log`而言,简单地使用`dup`可能是不够的。
想要将守护进程的标准输出和标准错误重定向到`/var/log/daemon.log`,可以采用如下方法:
首先打开日志文件:

int log_fd = open("/var/log/daemon.log", O_WRONLY|O_CREAT|O_APPEND, 0600);
if (log_fd < 0) {
    // Handle error if file opening fails
}

在这里,`O_WRONLY`表示以只写方式打开文件,`O_CREAT`表示如果文件不存在就创建它,`O_APPEND`表示写入时总是追加到文件的末尾。`0600`是文件的权限位,表示文件所有者可以读写,而其他用户没有任何权限。
之后将标准输出和标准错误重定向到这个文件:

dup2(log_fd, STDOUT_FILENO); // STDOUT_FILENO is 1
dup2(log_fd, STDERR_FILENO); // STDERR_FILENO is 2

在这里,`dup2`首先会关闭第二个参数指定的文件描述符(如果它已经打开),然后将第二个参数的文件描述符复制为第一个参数的文件描述符。在上述代码中,我们先将`log_fd`复制到标准输出(`STDOUT_FILENO`),然后再复制到标准错误(`STDERR_FILENO`)。
最后,既然标准输出和标准错误已被重定向,`log_fd`就不再需要了,因此应该关闭它:

if (log_fd > STDERR_FILENO) { // Check if it's not one of the standard descriptors
    close(log_fd);
}

对于标准输入,通常会继续将其重定向到`/dev/null`。
通过上述步骤,守护进程的标准输出和错误就会被写入到`/var/log/daemon.log`文件中去。记得要考虑`open`和`dup2`可能失败的情况,并进行适当的错误处理。

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

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

相关文章

Flask中的Blueprints:模块化和组织大型Web应用【第142篇—Web应用】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Flask中的Blueprints&#xff1a;模块化和组织大型Web应用 在构建大型Web应用时&#xff0…

面试经典150题(114-118)

leetcode 150道题 计划花两个月时候刷完之未完成后转&#xff0c;今天完成了5道(114-118)150 gap 了一周&#xff0c;以后就不记录时间了。。 114.(70. 爬楼梯) 题目描述&#xff1a; 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不…

【Qt图形界面引擎(一)】:第一个Qt程序

跨平台图形界面引擎&#xff0c;接口简单&#xff0c;易上手&#xff0c;一定程度简化内存。 Qt发展史 1991年由Qt Company开发的跨平台C图形用户界面应用程序开发框架2008年&#xff0c;Qt Company科技被诺基亚公司收购&#xff0c;Qt也因此成为诺基亚旗下的编程语言工具2012…

【神经网络 基本知识整理】(激活函数) (梯度+梯度下降+梯度消失+梯度爆炸)

神经网络 基本知识整理 激活函数sigmoidtanhsoftmaxRelu 梯度梯度的物理含义梯度下降梯度消失and梯度爆炸 激活函数 我们知道神经网络中前一层与后面一层的连接可以用y wx b表示&#xff0c;这其实就是一个线性表达&#xff0c;即便模型有无数的隐藏层&#xff0c;简化后依旧…

十、MySQL主从架构配置

一、资源配置 主库&#xff1a;192.168.134.132 从库&#xff1a;192.168.134.133 从库&#xff1a;192.168.134.134 二、主从同步基本原理&#xff1a; master用户写入数据&#xff0c;会生成event记录到binary log中&#xff0c;slave会从master读取binlog来进行数据同步…

如何将大华dav视频转mp4?一键无损清晰转换~

Digital Audio Video&#xff08;DAV&#xff09;文件格式源于数字监控领域&#xff0c;旨在解决视频监控数据的存储和回放问题。随着数字监控技术的发展&#xff0c;DAV格式逐渐成为监控设备记录视频的标准格式&#xff0c;广泛应用于安防系统、摄像头监控等场景。 MP4文件格式…

Transformer的前世今生 day03(Word2Vec

前情回顾 由上一节&#xff0c;我们可以得到&#xff1a; 任何一个独热编码的词都可以通过Q矩阵得到一个词向量&#xff0c;而词向量有两个优点&#xff1a; 可以改变输入的维度&#xff08;原来是很大的独热编码&#xff0c;但是我们经过一个Q矩阵后&#xff0c;维度就可以控…

【ARM】DSTREAM上面的各个指示灯代表什么意思?

【更多软件使用问题请点击亿道电子官方网站查询】 1、 文档目标 对于DStream仿真器上面的指示灯亮灭代表的意义进行分析。 2、 问题场景 主要对于DStream仿真器的使用过程中&#xff0c;不同的情况下面仿真器的指示灯会进行相应的亮灭。了解一下不同指示灯的亮灭所提示的信息…

大模型算命,对比下kimi和ChatGPT,AI离道破天机就差一个你信不信

我不懂算命&#xff0c;但很好奇。 先随便问问kimi&#xff0c;它先通过网络找到了参考&#xff0c;直接就给出了结论&#xff0c;看着有点儿草率&#xff0c;像极了街头算命先生。 给点儿专业书籍让他学习学习&#xff0c;它就开始认真了&#xff0c;看上去有些专业。 试试…

51-32 CVPR’24 | 3DSFLabelling,通过伪自动标注增强 3D 场景流估计

24 年 2 月&#xff0c;鉴智机器人、剑桥大学和上海交通大学联合发布CVPR24工作&#xff0c;3DSFLabelling: Boosting 3D Scene Flow Estimation by Pseudo Auto-labelling。 提出 3D 场景自动标注新框架&#xff0c;将 3D 点云打包成具有不同运动属性的 Boxes&#xff0c;通过…

File文件对象

在计算机系统中&#xff0c;文件是非常重要的存储方式。Files&#xff08;java.nio.file.Files&#xff09;提供了多种方法来处理文件系统中的文件。比直接使用File文件要方便。 Files工具类:读取指定文件中的所有文本 package study1;import java.io.IOException; import ja…

(一)Neo4j下载安装以及初次使用

&#xff08;一&#xff09;下载 官网地址&#xff1a;Neo4j Graph Database & AnamConnect data as its stored with Neo4j. Perform powerful, complex queries at scale and speed with our graph data platform.https://neo4j.com/ &#xff08;二&#xff09;安装并配…

C++Qt学习——QFile、QPainter、QChart

目录 1、QFile&#xff08;文本读写&#xff09;——概念 1.1、拖入三个控件&#xff0c;对pushButton进行水平布局&#xff0c;之后整体做垂直布局 1.2、按住控件&#xff0c;转到槽&#xff0c;写函数 1.3、打开文件控件 A、首先引入以下两个头文件 B、设置点击打开文件控…

搭建 es 集群

一、VMware准备机器 首先准备三台机器 这里我直接使用 VMware 构建三个虚拟机 都是基于 CentOS7 然后创建新用户 部署 es 需要单独创建一个用户&#xff0c;我这里在构建虚拟机的时候直接创建好了 然后将安装包上传 可以使用 rz 命令上传&#xff0c;也可以使用工具上传 工…

Apache Doris 如何基于自增列满足高效字典编码等典型场景需求

自增列&#xff08;auto_increment&#xff09;是数据库中常见的一项功能&#xff0c;它提供一种方便高效的方式为行分配唯一标识符&#xff0c;极大简化数据管理的复杂性。当新行插入到表中时&#xff0c;数据库系统会自动选取自增序列中的下一个可用值&#xff0c;并将其分配…

【路径规划】全局路径规划算法——A*算法(含python实现 | c++实现)

文章目录 参考资料1. 算法简介2. 算法精讲2.1 预处理2.2 开始搜索2.3 继续搜索2.4 确定实际路径 3. 算法总结3.1 算法步骤3.2 伪代码 4. python实现5. c实现 参考资料 Introduction to the A* Algorithm路径规划与轨迹跟踪系列算法Robotic Motion Planning Lectures A星算法详…

【附下载】3Ds Max从安装、配置到入门提高和高级用法

#3Ds Max 一、安装 1.1 安装说明 地址&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1lwKMbgbE32wCL6PpMv706A?pwddll8 提取码&#xff1a;dll8 –来自百度网盘超级会员V2的分享 安装说明&#xff1a;文件夹里有安装说明 安装解压即可 关键就是将crack文件放到自己…

高架学习笔记之信息安全基础

目录 一、信息安全基础 1.1. 概念 1.2. 信息存储安全 1.3. 网络安全 二、信息安全系统的组成框架 三、信息加解密技术和数字签名 四、访问控制技术 五、信息安全的保障体系与评估方法 5.1. 计算机信息系统安全保护等级 5.2. 安全风险管理 一、信息安全基础 1.1. 概念 …

DEYOv2: Rank Feature with Greedy Matchingfor End-to-End Object Detection

摘要 与前代类似&#xff0c; DEYOv2 采用渐进式推理方法 来加速模型训练并提高性能。该研究深入探讨了一对一匹配在优化器中的局限性&#xff0c;并提出了有效解决该问题的解决方案&#xff0c;如Rank 特征和贪婪匹配 。这种方法使DEYOv2的第三阶段能够最大限度地从第一和第二…

工控MCGS触摸屏Hacking勒索部署

https://github.com/MartinxMax/Mo0n_V1.2 !!不关注点赞收藏,以后没好东西了奥!! 端口扫描-获取信息 $python Mo0n.py -scan x.x.x.0/24 or $nmap -sS -Pn -T4 x.x.x.0/24 -p 127 MCGS编程软件 发现触摸屏受到密码保护 Oops&#xff01;&#xff01;&#xff01; echo /\_…