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

一、常用的守护进程函数

void daemonize ()
{//deamonizepid_t pid = fork();if( pid > 0 ){       //parent exitexit(0);}//child continuesetsid();chdir("/");close(0);open("/dev/null", O_RDWR);//no env debugif(!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 exitexit(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 ()
{// Daemonizepid_t pid = fork();if (pid > 0) {       // Parent exitsexit(0);}// Child (daemon) continuessetsid();chdir("/");// Redirect standard file descriptors to /dev/null or a log fileclose(STDIN_FILENO);open("/dev/null", O_RDWR); // STDIN// Redirect STDOUT and STDERR to a log fileconst 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 outputclose(STDERR_FILENO); // Close the standard errordup2(logFile, STDOUT_FILENO); // Redirect standard output to the log filedup2(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 descriptorsclose(log_fd);
}

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

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

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

相关文章

AI预测福彩3D第13弹【2024年3月20日预测--第3套算法重新开始计算第3次测试】

今天咱们继续对第3套算法进行第3次测试&#xff0c;第3套算法加入了012路的权重。废话不多说了&#xff0c;直接上结果吧~ 最终&#xff0c;经过研判分析&#xff0c;2024年3月20日福彩3D的七码预测结果如下&#xff1a; 百位&#xff1a;7 4 8 3 6 9 0&#xff08;5换0&#x…

锂电池寿命预测 | Matlab基于ALO-SVR蚁狮优化支持向量回归的锂离子电池剩余寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 锂电池寿命预测 | Matlab基于ALO-SVR蚁狮优化支持向量回归的锂离子电池剩余寿命预测 基于蚁狮优化和支持向量回归的锂离子电池剩余寿命预测: 1、提取NASA数据集的电池容量&#xff0c;以历史容量作为输入&#xff0c;…

Java项目:66 ssm实验室耗材管理系统设计与实现+jsp

作者主页&#xff1a;源码空间codegym 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 管理员管理实验材料&#xff0c;审核教师与学生对实验材料的申请信息&#xff0c;统计每学年实验材料的使用总数信息。 教师申请使用实验材料…

【探索Linux】—— 强大的命令行工具 P.28(网络编程套接字 —— 简单的UDP网络程序模拟实现)

阅读导航 引言一、UDP协议二、UDP网络程序模拟实现1. 预备代码⭕makefile文件⭕打印日志文件⭕打开指定的终端设备文件&#xff0c;并将其作为标准错误输出的目标文件描述符 2. UDP 服务器端实现&#xff08;UdpServer.hpp&#xff09;3. UDP 客户端实现&#xff08;main函数&a…

day04vue学习

day04 一、学习目标 1.组件的三大组成部分&#xff08;结构/样式/逻辑&#xff09; ​ scoped解决样式冲突/data是一个函数 2.组件通信 组件通信语法父传子子传父非父子通信&#xff08;扩展&#xff09; 3.综合案例&#xff1a;小黑记事本&#xff08;组件版&#xff09…

DeprecationWarning: isDaemon() is deprecated, get the daemon attribute instead

报错处理 # t.setDaemon(True) # 阙辉注释 t.daemonTrue # 阙辉新增

已有TensorFlow安装包新建相应python版本的虚拟环境

已有TensorFlow安装包新建虚拟环境 新建conda虚拟环境 新建的虚拟环境默认在Anaconda安装目录D:\Anaconda3\envs&#xff08;根据自己的安装目录看&#xff09; 切换到新建的虚拟环境 在这里可以直接安装下载好的TensorFlow安装包 检查是否安装好 输入python进入python环境…

YOLOv7 | 添加GSConv,VoVGSCSP等多种卷积,有效提升目标检测效果,代码改进(超详细)

⭐欢迎大家订阅我的专栏一起学习⭐ &#x1f680;&#x1f680;&#x1f680;订阅专栏&#xff0c;更新及时查看不迷路&#x1f680;&#x1f680;&#x1f680; YOLOv5涨点专栏&#xff1a;http://t.csdnimg.cn/QdCj6 YOLOv7专栏&#xff1a; http://t.csdnimg.cn/dy…

爬虫逆向sm3和sm4 加密 案例

注意&#xff01;&#xff01;&#xff01;&#xff01;某XX网站逆向实例仅作为学习案例&#xff0c;禁止其他个人以及团体做谋利用途&#xff01;&#xff01;&#xff01; 案例--aHR0cDovLzExMS41Ni4xNDIuMTM6MTgwODgvc3Vic2lkeU9wZW4 第一步&#xff1a;分析页面和请求方式 …

spark基本原理UI界面解读

这里是引用 1 八股文 1.1 基本原理 driver节点是整个应用程序的指挥所 指挥官是sparkcontext 环境&#xff1a;构建一个集群 应用程序提交 确定主节点&#xff0c;确定指挥所driver&#xff0c;确定指挥官sparkcontext sparkcontext会向资源管理器申请资源 会将作业分…

基于Java中的SSM框架实现快餐店线上点餐系统项目【项目源码+论文说明】

基于Java中的SSM框架实现快餐店线上点餐系统演示 摘要 随着计算机互联网的高速发展。餐饮业的发展也加入了电子商务团队。各种网上点餐系统纷纷涌现&#xff0c;不仅增加了商户的销售量和营业额&#xff0c;而且为买家提供了极大的方便&#xff0c;足不出户&#xff0c;就能订…

软件测评中心:进行科技成果鉴定测试的注意事项和好处简析

软件产品科技成果鉴定是有效评价科技成果质量和水平的方法之一&#xff0c;也是鼓励科技成果通过市场竞争等方式得到有效的评价和认可&#xff0c;可以推动科技成果的进步和转化。 一、进行科技成果鉴定测试时的注意事项&#xff1a;   1、应由具备一定资质和能力的专业机构…

Android Studio实现内容丰富的安卓外卖平台

获取源码请点击文章末尾QQ名片联系&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动 项目编号122 1.开发环境android stuido jdk1.8 eclipse mysql tomcat 2.功能介绍 安卓端&#xff1a; 1.注册登录 2.查看公告 3.查看外卖分类 4.购物车&#xff0c; 5.个人中…

深度学习_微调_7

目标 微调的原理利用微调模型来完成图像的分类任务 微调的原理 微调&#xff08;Fine-tuning&#xff09;是一种在深度学习中广泛应用的技术&#xff0c;特别是在预训练模型&#xff08;Pretrained-Models&#xff09;的基础上进行定制化训练的过程。微调的基本原理和步骤如下…

【项目】YOLOv5+PaddleOCR实现艺术字验证码识别

YOLOv5PaddleOCR实现艺术字类验证码识别 一、引言1.1 实现目标1.2 人手动点选验证码逻辑1.3 计算机点选逻辑 二、计算机验证方法2.1 PaddleOCR下方文字识别方法2.2 YOLOv5目标检测方法2.3 艺术字分类方法2.4 返回结果 三、代码获取 一、引言 1.1 实现目标 要识别的验证码类型…

c语言综合练习题

1.编写程序实现键盘输入一个学生的学分绩点 score&#xff08;合法的范围为:1.0—5.0&#xff09;&#xff0c;根据学生的学分绩点判定该学 生的奖学金的等级&#xff0c;判定规则如下表所示。 #include <stdio.h>int main() {float score;printf("请输入学生的学分…

Harbor-私有镜像仓库

目录 一、Harbor 原理说明 1.软件资源介绍 2.Harbor 特性 3.Harbor 认证过程 4.Harbor 认证流程 二、私有镜像仓库实验 1.环境准备 2.安装docker 3.配置镜像加速和私有仓库地址 4.搭建harbor仓库 5.本地windows浏览器访问配置 一、Harbor 原理说明 1.软件资源介绍 …

面试算法-62-盛最多水的容器

题目 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xff1a;你不能倾斜容器。…

CycleGAN训练及测试过程细节记录

CycleGAN训练及测试过程细节记录 文章目录 关于训练关于测试 关于训练 1、训练前将数据配置好&#xff0c;并在Pycharm中写好配置信息 2、关于训练过程的参数配置在 options/train_options.py options/base_options.py batch_size&#xff1a;批大小 crop_size&#xff1a;…

Android分区存储到底该怎么做

文章目录 一、Android存储结构二、什么是分区存储&#xff1f;三、私有目录和公有目录三、存储权限和分区存储有什么关系&#xff1f;四、我们应该该怎么做适配&#xff1f;4.1、利用File进行操作4.2、使用MediaStore操作数据库 一、Android存储结构 Android存储分为内部存储和…