Linux系统编程之守护进程

概述

在Linux系统中,守护进程是一个在后台运行的特殊类型的进程。它独立于控制终端,并且周期性地执行某种任务,或等待处理某些发生的事件。守护进程通常用于提供服务,比如:Web服务器、数据库管理系统等。

守护进程具有以下四个核心特征。

1、脱离终端。守护进程不与任何终端关联,这意味着它们不会因为用户注销而停止运行。

2、生命周期长。随系统启动,直至系统关闭或终止。

3、工作目录设为根目录。为了避免影响文件系统的卸载操作,守护进程的工作目录通常设置为根目录/。

4、重定向标准输入输出。守护进程通常将标准输入、标准输出和标准错误重定向到/dev/null或者日志文件中。

在Linux系统中,守护进程的父进程通常为init(PID为1)。这是因为,守护进程会通过fork来创建子进程,然后让父进程退出。

守护进程的创建

一般情况下,创建守护进程有如下8个步骤。

1、创建子进程。通过调用fork函数来创建一个新的子进程,并让父进程退出。这样做的目的是让shell认为命令已经执行完毕,同时保证子进程不是会话首进程,从而避免获取控制终端。

2、创新新会话。在子进程中,调用setsid来创建一个新的会话。这一步将当前进程设置为新会话的首进程,使其脱离原来的会话和控制终端,不再受键盘输入的影响,也不能再向屏幕输出信息。

3、再次创建子进程。为了防止守护进程重新打开控制终端,需要再次调用fork创建另一个子进程,并让第二次fork后的父进程退出。这样可以确保守护进程不再是会话首进程,从而彻底避免获取控制终端的可能性。

4、设置工作目录。为了防止守护进程影响其他文件系统的挂载点,应该将其工作目录设置为根目录"/"。如果不这样做,守护进程可能会阻止文件系统的卸载操作,因为它可能持有该文件系统的某个目录作为当前工作目录。

5、重定向。由于守护进程没有控制终端,因此需要关闭所有从父进程继承来的文件描述符,特别是标准输入、标准输出和标准错误。通常的做法是:将它们重定向到/dev/null或特定的日志文件中,以便于日志记录和调试。

6、关闭文件描述符。除了上述的标准输入、标准输出和标准错误之外,还需要关闭所有其他不必要的文件描述符。这是因为守护进程不应该保留任何不必要的资源连接,以避免资源泄露或其他潜在问题。

7、处理信号。守护进程应适当地设置信号处理器,特别是对于SIGHUP(挂断信号),它通常用来通知守护进程重新加载配置文件。还有SIGTERM(终止信号),用于正常关闭守护进程。

8、日志记录。由于守护进程脱离了终端,因此需要使用日志记录机制来报告其状态和错误信息。可以使用syslog库进行日志记录,这允许我们将消息发送到系统日志服务,便于集中管理和查看。

具体如何创建守护进程,可参考下面的示例代码。

cpp 复制代码
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include <syslog.h>

void create_daemon()
{
    // 首次fork
    pid_t pid = fork();
    if (pid > 0)
    {
        // 父进程退出
        exit(EXIT_SUCCESS);
    }
    
    // 创建新会话
    setsid();
    
    // 二次fork
    pid = fork();
    if (pid > 0)
    {
        // 父进程退出
        exit(EXIT_SUCCESS);
    }
    
    // 重置文件掩码
    umask(0);
    
    // 更改工作目录
    chdir("/");
    
    // 关闭所有文件描述符
    int fd = 0;
    for (fd = sysconf(_SC_OPEN_MAX); fd >= 0; fd--)
    {
        close(fd);
    }
    
    // 重定向标准I/O
    fd = open("/dev/null", O_RDWR);
    if (fd != -1)
    {
        dup2(fd, STDIN_FILENO);
        dup2(fd, STDOUT_FILENO);
        dup2(fd, STDERR_FILENO);
        if (fd > STDERR_FILENO)
        {
            close(fd);
        }
    }
    
    // 初始化syslog
    openlog("mydaemon", LOG_PID, LOG_DAEMON);
}

int main()
{
    create_daemon();
    syslog(LOG_INFO, "Daemon is running.");
    while (1)
    {
        // 守护进程主循环
        sleep(1);
    }
    
    closelog();
    return 0;
}

守护进程的管理

对于简单的守护进程,我们可以直接通过命令行进行启动和停止操作。使用&符号可以让程序在后台运行,比如:./daemon_program &。使用kill命令发送信号可以终止进程,比如:kill [PID]。为了更温和地关闭进程,可以发送SIGTERM信号而不是默认的SIGKILL。

对于复杂的守护进程,我们可以使用Systemd。Systemd是现代Linux发行版中广泛使用的初始化系统和服务管理器,它提供了强大的工具来管理系统服务,包括守护进程。Systemd中常用的命令包括:创建服务单元文件、启动服务、停止服务、启用开机自启、查看服务状态等,下面进行详细的介绍。

1、创建服务单元文件。在/etc/systemd/system/目录下创建一个.service文件,比如:mydaemon.service,内容如下。

cpp 复制代码
[Unit]
Description=My Daemon Service

[Service]
ExecStart=/path/to/daemon_program
Restart=always

[Install]
WantedBy=multi-user.target

2、启动服务。命令为:sudo systemctl start mydaemon.service。

3、停止服务。命令为:sudo systemctl stop mydaemon.service。

4、启用开机自启。命令为:sudo systemctl enable mydaemon.service。

5、查看服务状态。命令为:sudo systemctl status mydaemon.service。

相关推荐
Forget_855021 分钟前
Linux的例行性工作
linux·运维·服务器
用手码出世界36 分钟前
自定义minshell
linux·服务器
心随雪冻1 小时前
18.PCIe总线入门理解与Linux上PCIe设备配置与使用
linux·arm开发·嵌入式硬件
m0_745364241 小时前
LVS-DR模式配置脚本
linux·运维·服务器·github·lvs
不知名。。。。。。。。1 小时前
Linux--命令行操作
linux·运维·服务器
老年DBA2 小时前
Linux Namespace(网络命名空间)系列三 --- 使用 Open vSwitch 和 VLAN 标签实现网络隔离
linux·运维·服务器·网络
MarkHD3 小时前
第五天 Linux基础命令强化 - Shell与Python交互 - Vim基础操作
linux·python·交互
筑梦之路3 小时前
CentOS 8 Stream 配置在线yum源参考 —— 筑梦之路
linux·centos
林政硕(Cohen0415)3 小时前
Linux下xl9535 gpio扩展芯片bug调试
linux·xl9535
OKay_J3 小时前
Linux学习笔记(应用篇三)
linux·笔记·学习