Linux学习日记16:守护进程

一、前言

守护进程是 Linux 系统中一类特殊的后台服务进程,是操作系统核心功能的重要支撑(如网络服务、定时任务、系统监控等)。

二、守护进程

2.1、定义

守护进程是在后台运行、脱离终端控制、独立于用户登录会话、周期性执行特定任务或提供服务的进程。它没有控制终端,不会被用户的交互操作(如关闭终端、注销登录)影响,生命周期通常贯穿系统运行全过程(除非主动终止或系统关机)。

2.2、守护进程的特点

1、后台运行:不占用终端,终端关闭不影响其运行。

2、脱离终端控制:无控制终端,不会接收终端的信号(如Ctrl+C、Ctrl+Z)也不会像终端输出。

3、独立会话:不属于任何用户登录会话,父进程通常是 init 进程(PID=1)。

4、生命周期长:系统启动时自动运行,直到系统关机或主动终止。

5、进程组首领:自身是进程组和会话的首领,避免被其他进程意外干扰。

2.3、守护进程的基本原理

在 Linux 中,进程与终端、会话、进程组的关系是:一个终端对应一个会话,一个会话包含多个进程组,一个进程组包含多个进程。

其中进程组是一组相关进程的集合 ,由一个唯一的进程组 ID(PGID) 标识。其核心作用是:对多个进程进行 "批量管理",比如向进程组内所有进程发送信号(如Ctrl+C终止前台进程组)。第一个创建进程组的进程,其 PID = 进程组 PGID;首领进程退出不影响进程组存在(进程组持续到最后一个进程退出)。子进程默认继承父进程的进程组 ID。

会话是一组相关进程组的集合 ,由一个唯一的会话 ID(SID) 标识。其核心作用是:关联终端与进程组,实现终端的 "作业控制"(如前台 / 后台作业切换、终端信号分发)。一个会话通常对应一个 "用户登录会话":用户通过终端登录后,系统会创建一个新会话,登录 shell 是会话首领,后续启动的进程都属于该会话;第一个创建会话的进程(通常是登录 shell,如 bash),其 PID = 会话 SID;首领退出不影响会话存在。一个会话最多关联一个 "控制终端",用于进程与用户交互;会话内的进程组分为:前台进程组(1 个,接收终端输入和信号)、后台进程组(多个,不接收终端输入)。从会话创建开始,到最后一个进程退出或控制终端关闭结束。

2.4、守护进程的创建流程

1、fork 子进程,父进程退出(必须)

此步骤的目的是让子进程成为孤儿进程,被 init 进程(PID=1)收养,保证生命周期独立于父进程;父进程退出后,终端会认为原命令执行完毕,释放终端控制,此时子进程不再与终端关联;

注:父进程是终端启动的进程,属于终端的会话组;子进程 fork 后继承父进程的进程组和会话,但父进程退出后,子进程被 init 收养,脱离原终端会话。

2、子进程setsid()创建新会话(必须)

函数原型如下:

cs 复制代码
#include <unistd.h>
pid_t setsid(void);

该函数的功能是创建一个新的会话,并让当前进程成为:新会话的首领、新进程组的首领​​​​​​​和脱离所有终端控制。

注:setsid() 只能由非进程组首领 调用。步骤 1 中 fork 后,子进程继承父进程的进程组 ID,但子进程的 PID 与进程组 ID 不同(父进程是原进程组首领),因此子进程是非进程组首领,可调用setsid()

3、改变当前工作目录chdir(非必须)

此步骤的避免守护进程的工作目录被删除或挂载点卸载,导致进程异常。常用操作是将工作目录切换到根目录(/)或专用目录。比如:

cs 复制代码
chdir("/"); // 切换到根目录

注:如果守护进程的工作目录是某个可删除的目录(如用户的 /home 目录),当该目录被删除时,进程的工作目录会变成 "不可访问" 状态(pwd显示为?),可能导致文件操作失败。最典型的例子就是在U盘上运行进程后,拔下U盘就运行不了的情况。

4、设置文件权限掩码(umask)(非必须)

函数原型如下:

cs 复制代码
#include <sys/stat.h>
mode_t umask(mode_t mask);

该步骤的作用是清除文件权限掩码,确保守护进程创建的文件 / 目录拥有预期的权限。

注:文件权限掩码(umask)是创建文件时的 "权限屏蔽位",默认 umask 值(如 0022)会屏蔽某些权限(如组用户的写权限)。守护进程通常需要创建日志文件或临时文件,设置umask后,创建文件时权限由open()/mkdir()的mode参数完全控制。

5、关闭所有文件描述符(非必须)

该步骤的作用是释放父进程继承的不必要文件描述符(如标准输入、标准输出、标准错误),避免资源泄漏。

**注:**fork 后,子进程继承父进程的所有文件描述符(包括终端的 stdin、stdout、stderr)。守护进程脱离终端后,这些文件描述符已无用,但会占用系统资源,甚至可能导致进程阻塞(如等待终端输入)。

三、典型示例

首先创建一个setsid.c文件,并输入以下代码:

cs 复制代码
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
        pid_t pid;
        pid = fork();
        if(pid>0)
        {
                exit(1);
        }
        if(pid == 0)
        {
                setsid();
                while(1)
                {
                        sleep(1);
                        printf("............\n");
                }
        }
        return 0;
}

编译并运行,结果如下:

可以发现,我们使用Ctrl+C(SIGINT终止信号)无法终止该进程,这是因为随着父进程挂掉后,Shell 认为该命令已执行完毕,子进程称为新的会话首领,脱离了终端控制,在后台运行。

我们使用ps aux查看下状态:

可以看到此时该进程的TTY列显示?(表示无控制终端),那么我们怎么把这个后台进程给关掉呢?那就是使用kill指令:

打开另一个终端后,使用kill指令强制杀死后,可以看到该后台进程成功关闭了。​​​​​​​

相关推荐
武哥聊编程2 小时前
基于Springboot3+Vue3的仓库管理系统,经典项目,免费学习
java·学习·mysql·vue·springboot·课程设计
一匹电信狗2 小时前
【Linux我做主】进程实践:手动实现Shell
linux·运维·服务器·c++·ubuntu·小程序·开源
学编程的闹钟2 小时前
85【CSS选择器简介】
学习
BUG_MeDe2 小时前
Linux 下VRF的简单应用
linux·运维·服务器
babytiger2 小时前
用python在服务器上开个可以输入帐号密码的代理服务器
运维·服务器
Sleepy MargulisItG2 小时前
Linux 基础指令详解(常用)
linux
向山行_wolf2 小时前
ubuntu22.04鼠标速度配置
运维·服务器
向阳是我2 小时前
v0.app的next.js项目自动部署到宝塔服务器教程
服务器·开发语言·javascript·github·ai编程
python百炼成钢2 小时前
解决——windows和ubuntu之间无法复制粘贴
linux