守护进程(linux)

目录

1.守护进程

一、核心目标

二、代码逐段解析

[1. 头文件与常量定义](#1. 头文件与常量定义)

[2. Daemon 函数核心逻辑](#2. Daemon 函数核心逻辑)

[步骤 1:忽略可能导致异常退出的信号](#步骤 1:忽略可能导致异常退出的信号)

[步骤 2:让父进程退出,子进程成为 "孤儿进程"](#步骤 2:让父进程退出,子进程成为 "孤儿进程")

[步骤 3:创建新会话,脱离控制终端](#步骤 3:创建新会话,脱离控制终端)

[步骤 4:切换工作目录(可选)](#步骤 4:切换工作目录(可选))

[步骤 5:处理标准 I/O 描述符(关闭或重定向)](#步骤 5:处理标准 I/O 描述符(关闭或重定向))

三、使用场景

四、总结


1.守护进程

Daemon.hpp

cpp 复制代码
C++
#pragma once
#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
const char *root = "/";
const char *dev_null = "/dev/null";
void Daemon(bool ischdir, bool isclose)
{
// 1. 忽略可能引起程序异常退出的信号
signal(SIGCHLD, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
// 2. 让自己不要成为组长
if (fork() > 0)
exit(0);
// 3. 设置让自己成为一个新的会话, 后面的代码其实是子进程在走
setsid();
// 4. 每一个进程都有自己的 CWD,是否将当前进程的 CWD 更改成为 /根目录
if (ischdir)
chdir(root);
// 5. 已经变成守护进程啦,不需要和用户的输入输出,错误进行关联了
if (isclose)
{
close(0);
close(1);
close(2);
}
else
{
// 这里一般建议就用这种
int fd = open(dev_null, O_RDWR);
if (fd > 0)
{
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
}
}
}

这段代码是一个典型的守护进程(Daemon)初始化函数,用于将一个普通进程转换为守护进程。守护进程是在后台长期运行、脱离控制终端、独立于用户交互的进程(如系统服务、后台任务等)。下面逐部分解析其实现逻辑和作用:

一、核心目标

将调用该函数的进程转换为守护进程,使其具备以下特性:

  • 脱离控制终端(不接收终端输入 / 输出,不受终端关闭影响);
  • 在后台独立运行,不阻塞用户交互;
  • 避免成为 "僵尸进程",长期稳定运行。

二、代码逐段解析

1. 头文件与常量定义

cpp

运行

复制代码
#pragma once  // 防止头文件重复包含(类似#ifndef的简化写法)
#include <iostream>
#include <cstdlib>   // 包含exit()等函数
#include <signal.h>  // 信号处理(signal())
#include <unistd.h>  // 包含fork()、chdir()、setsid()等系统调用
#include <fcntl.h>   // 文件操作(open()、dup2()等)
#include <sys/types.h>
#include <sys/stat.h>

const char *root = "/";         // 根目录路径(用于切换工作目录)
const char *dev_null = "/dev/null";  // 空设备路径(用于重定向I/O)

这些头文件提供了进程控制、信号处理、文件操作等核心系统调用的声明,常量用于后续路径操作。

2. Daemon 函数核心逻辑

函数参数 ischdir 控制是否切换工作目录,isclose 控制是否直接关闭标准 I/O 描述符(或重定向到空设备)。

步骤 1:忽略可能导致异常退出的信号

cpp

运行

复制代码
signal(SIGCHLD, SIG_IGN);  // 忽略子进程结束信号(防止僵尸进程)
signal(SIGPIPE, SIG_IGN);  // 忽略管道破裂信号(避免向已关闭管道写数据时崩溃)
  • SIGCHLD:子进程退出时会向父进程发送此信号,若忽略,内核会自动回收子进程资源,避免产生僵尸进程。
  • SIGPIPE:当向一个已关闭的管道(或 socket)写入数据时产生,忽略可防止程序因该信号异常退出。
步骤 2:让父进程退出,子进程成为 "孤儿进程"

cpp

运行

复制代码
if (fork() > 0)  // 父进程分支
    exit(0);     // 父进程退出,仅保留子进程
  • fork() 创建子进程后,父进程立即退出,子进程会被操作系统的 init 进程(或 systemd 等进程管理器)收养,避免受原父进程退出的影响。
  • 更重要的是:此时子进程不是进程组组长 (进程组组长是原父进程,已退出),为下一步 setsid() 做准备(setsid() 要求调用进程不能是进程组组长,否则失败)。
步骤 3:创建新会话,脱离控制终端

cpp

运行

复制代码
setsid();  // 子进程创建新会话,成为新会话的首进程

setsid() 的作用是:

  • 子进程脱离原有的进程组和会话;
  • 成为新会话的首进程和新进程组的组长;
  • 最重要:脱离控制终端 (不再与原终端关联,不会收到终端的信号如 Ctrl+C 产生的 SIGINT)。
步骤 4:切换工作目录(可选)

cpp

运行

复制代码
if (ischdir)
    chdir(root);  // 若需要,将工作目录切换到根目录 "/"
  • 守护进程通常长期运行,若原工作目录被卸载(如挂载的外部设备),会导致进程异常。切换到根目录(/,几乎不会被卸载)可避免此问题。
步骤 5:处理标准 I/O 描述符(关闭或重定向)

cpp

运行

复制代码
if (isclose) {
    // 直接关闭标准输入(0)、输出(1)、错误(2)
    close(0);
    close(1);
    close(2);
} else {
    // 更常用:重定向到/dev/null(空设备,写入的数据会被丢弃)
    int fd = open(dev_null, O_RDWR);  // 打开空设备(可读可写)
    if (fd > 0) {
        dup2(fd, 0);  // 标准输入重定向到/dev/null(读不到数据)
        dup2(fd, 1);  // 标准输出重定向到/dev/null(输出被丢弃)
        dup2(fd, 2);  // 标准错误重定向到/dev/null
        close(fd);    // 关闭原始fd,避免资源泄漏
    }
}
  • 守护进程无需与用户交互,保留标准 I/O(关联原终端)没有意义,甚至可能因终端关闭导致 I/O 错误。
  • 直接关闭描述符(isclose=true)或重定向到 /dev/nullisclose=false)是标准做法。/dev/null 被称为 "黑洞",所有写入的数据会被丢弃,读取时直接返回 EOF,避免 I/O 阻塞。

三、使用场景

调用 Daemon(true, false) 后,当前进程就会变成一个标准的守护进程,例如:

cpp

运行

复制代码
int main() {
    Daemon(true, false);  // 转换为守护进程
    // 后续逻辑:如循环处理任务、监听端口等(长期运行)
    while (true) {
        sleep(1);  // 模拟工作
    }
    return 0;
}

四、总结

这段代码通过信号忽略、进程分叉、创建新会话、切换工作目录、重定向 I/O五个核心步骤,将普通进程转换为守护进程,使其具备后台运行、脱离终端、稳定持久的特性,是 Linux 后台服务开发的基础工具函数。

相关推荐
小宇的天下2 天前
Calibre eqDRC(方程化 DRC)核心技术解析与实战指南(14-1)
数据库·windows·microsoft
墨辰JC2 天前
STM32架构基于调度器的非阻塞按键状态机设计
stm32·microsoft·架构·状态机·调度器
素素.陈2 天前
调用大模型解析图片中的文字
linux·windows·microsoft
程序员龙语2 天前
HTML浮动布局与表格应用核心要点总结
microsoft
小北方城市网3 天前
第 8 课:Python 面向对象进阶 —— 类方法、静态方法与高级特性
网络·python·microsoft·数据库架构
心疼你的一切3 天前
【技术创作的璀璨盛宴——2025年CSDN博客之星总评选深度总结】
microsoft·unity·游戏引擎·游戏程序·csdn·博客之星
liukuang1103 天前
IPO视角| 卧安机器人赴港IPO曲线救国:先卖窗帘、再造人
microsoft·机器人
安得权4 天前
.NET 把文件上传到Sharepoint - Microsoft Graph API方式
microsoft·.net·sharepoint
持梦远方4 天前
持梦行文本编辑器(cmyfEdit):架构设计与十大核心功能实现详解
开发语言·数据结构·c++·算法·microsoft·visual studio
QT 小鲜肉4 天前
【Linux命令大全】001.文件管理之paste命令(实操篇)
linux·运维·服务器·笔记·microsoft