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指令强制杀死后,可以看到该后台进程成功关闭了。​​​​​​​

相关推荐
留白_4 分钟前
代运营面试问答
学习
是隼人7 分钟前
buuctf-pwn PWN8题解(学习过程持续更新)
c语言·学习·安全·pwn入门·ctf入门
hi_ro_a8 分钟前
基于正倒排索引的boost搜索引擎
linux·c++·搜索引擎·项目
GG-_-Bond25 分钟前
9.1kv存储持久化模拟面试
linux·c语言·数据结构·c++
Dola_Zou44 分钟前
Linux工控机核心算法防逆向与自动化加固方案
linux·自动化·软件加密
欧米欧1 小时前
解析Linux从umask位运算到粘滞位
linux
ps酷教程1 小时前
数据权限DataPersmission
java·学习
无敌贵点大王1 小时前
RTThread学习记录10——C语言实现面向对象的编程
c语言·stm32·学习·链表
郝学胜-神的一滴1 小时前
C++20模板元编程 04:变量模板 别名模板与模板Lambda实战
服务器·开发语言·数据结构·c++·程序人生
江屿风2 小时前
【Linux系统】【Linux进程终止等待详解与程序替换机制初体验】流食般投喂
linux·运维·笔记·系统架构·centos·unix