Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【网络编程】四、守护进程实现 && 前后台作业 && 会话与进程组

【网络编程】四、守护进程实现 && 前后台作业 && 会话与进程组

作者头像
利刃大大
发布于 2025-05-10 00:08:35
发布于 2025-05-10 00:08:35
8200
代码可运行
举报
文章被收录于专栏:csdn文章搬运csdn文章搬运
运行总次数:0
代码可运行

Ⅰ. 守护进程的概念

​ 守护进程(daemon),也称 精灵进程本质上是一个孤儿进程。是在计算机操作系统中运行的一种 特殊类型的后台进程。它们通常在系统启动时启动,并在系统关闭时终止。守护进程在后台运行,不与用户交互,通常没有控制终端

​ 守护进程的主要目的是执行特定的任务或提供特定的服务,而不需要用户的干预。它们可以是网络服务、系统监控程序、定时任务等。守护进程通常以超级用户(root)权限运行,以便执行需要特权的操作。

​ 守护进程通常会在系统启动时由启动脚本或系统配置文件启动。它们会在后台运行,并通过日志文件记录其活动。守护进程还可以通过进程间通信(IPC)机制与其他进程进行通信。 ​ 守护进程的一个重要特点是它们能够在系统崩溃或重启后自动重新启动,以确保服务的连续性。它们通常会监视系统状态,并在必要时采取措施来恢复服务。

​ 我们可以通过 ps ajx | grep sshd 指令来查看当前系统中的守护进程信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[liren@VM-8-7-centos tcp]$ ps ajx | head -1 && ps ajx | grep sshd
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    1  3461  3461  3461 ?           -1 Ss       0   1:09 /usr/sbin/sshd -D
12517  5486  5485 12517 pts/6     5485 S+    1001   0:00 grep --color=auto sshd
 3461 11352 11352 11352 ?           -1 Ss       0   0:00 sshd: liren [priv]
11352 11368 11352 11352 ?           -1 S     1001   0:00 sshd: liren@pts/4
 3461 11510 11510 11510 ?           -1 Ss       0   0:00 sshd: liren [priv]
11510 11513 11510 11510 ?           -1 S     1001   0:00 sshd: liren@pts/5
 3461 12513 12513 12513 ?           -1 Ss       0   0:00 sshd: liren [priv]
12513 12516 12513 12513 ?           -1 S     1001   0:01 sshd: liren@pts/6
……

​ 其实这些守护进程和打印信息中的 SID 也是有关系,下面我们就来研究一下!

Ⅱ. 理解会话和作业

会话(session)是一个或多个进程组的集合。比如下图:

💥注意:这里的会话和 http 中常讲到的会话是不同的概念,不要搞混了!

一个会话只有 1 个前台进程组和 n 个后台进程组,作业之间可以前后台转换。这样的任务可能会收到用户登录和注销而被清理。一般来说当我们启动云服务器之后,就会创建一个会话,会话中创建了一个前台进程组,这个前台进程组就是由 bash 为进程组组长控制的,配合其它后台进程组一起工作运转!

​ 就继续拿上面的例子来说,进程组和会话如何看,如下图所示:

​ 一般我们可以 shell 提供的管道将几个进程编成一组,如下所示:

上图在操作的时候 使用 & 符号将想要执行的命令放到后台执行,而不会阻塞终端或当前会话。 下面是对**作业号后面的符号**的解释:

  1. - 符号:表示该作业在 后台运行。当在终端中运行一个命令时,可以使用 Ctrl + Z 将其暂停,并将其放入后台运行。此时,操作系统会返回一个作业号,并在作业号后面添加 - 符号。
  2. + 符号:表示该作业在 前台运行。当有多个作业在后台运行时,可以使用 fg 命令将指定作业调至前台运行。在作业号后面添加 + 符号,表示将该作业调至前台运行。

作业状态 通常用以下符号表示:

  • Running(运行中):作业正在后台运行。
  • Stopped(已停止):作业已被暂停或停止。
  • DoneExited(已完成):作业已经完成。
  • Terminated(已终止):作业被终止。

​ 下面我们看看这几个进程的 ID 情况:

​ 我们通过 PGID,也就是进程组标识,就能看出各自是属于哪个进程组的;而通过 SID,也就是会话标识,就能看出是属于哪个会话的!

​ 前两个 sleep 命令,它们 构成一个进程组,共同来完成一个作业,作业号在上上副图中,是作业二;而后三个 sleep 命令它们构成一个进程组,共同完成一个作业,它们的作业号是三号!

​ 这些灵感其实都来源于生活,就像在工地,一个包工头将多名员工分为多个组,每个组完成不同的任务!

​ 而 这些作业的包工头就是 bash 进程,我们可以检验一下:

​ 此时这个关系我们可以再画一下:

​ 这就是为什么平时我们连接服务器,打开就是一个命令行输入,其实就是默认访问会话 1 中前台进程组的 bash 进程!

🎏 会话和进程组的特性小总结

1、一个会话可以有一个控制终端(controlling terminal),这通常是终端设备(在终端登录情况下)或者伪终端设备(在网络登录情况下)。

2、建立和控制终端连接的会话首进程被称为控制进程(controlling process)。

3、一个会话中的几个进程组可被分为一个前台进程组和多个后台进程组。

4、如果一个会话有一个控制终端,则它有一个前台进程组,其它进程组为后台进程组。

5、无论何时键入终端的中断键(如 ctrl + c)或者退出键(比如 ctrl + \),都会将中断信号发送至前台进程组的所有进程。

6、如果终端接口检测到调制解调器(或网络)已经断开连接,则将挂断信号发送至控制进程,即会话首进程。

Ⅳ. 作业的前后台转换

1、fg 指令

fg 是一个在命令行中使用的 内置命令(不需要通过外部可执行文件来执行,而是由命令解释器直接处理。),用于 将一个后台运行的作业调至前台运行

​ 当在终端中运行多个命令时,可以 使用 & 符号将它们放入后台运行,这些后台运行的命令就成为了作业。然后再使用 fg 命令可以将指定的作业调至前台运行,使其成为当前终端会话的活动作业。

以下是 fg 命令的一些常见用法:

  • fg:将最近的一个后台作业调至前台运行。
  • fg n:将作业号为 n 的后台作业调至前台运行,其中 n 是作业的编号。

​ 调至前台运行的作业将重新获得终端的控制权,并在终端中显示其输出。在作业运行期间,终端会被该作业占用,直到作业完成或被暂停。

​ 需要注意的是,只有在当前终端会话中的作业才能被调至前台运行。如果有多个后台作业,可以使用 jobs -l 命令查看作业列表,并确定要调至前台运行的作业的作业号。

2、bg 指令

bg 是一个在命令行中使用的 内置指令,用于 将一个已停止的作业(stopped job)调至后台继续运行

​ 当在终端中运行多个命令时,可以使用 & 符号将它们放入后台运行,这些后台运行的命令就成为了作业。有时,一个作业可能会被暂停或停止,例如通过按下 Ctrl + Z 键来 暂停一个正在前台运行的作业

​ 然后使用 bg 命令可以将一个已停止的作业调至后台继续运行,该作业将恢复运行,并在后台继续执行。

以下是 bg 命令的一些常见用法:

  • bg:将最近的一个已停止的作业调至后台继续运行。
  • bg n:将作业号为 n 的已停止的作业调至后台继续运行,其中 n 是作业的编号。

​ 调至后台运行的作业将继续在后台执行,不会占用终端的控制权。作业的输出通常不会直接显示在终端上,但可以通过重定向输出或使用其他工具来监视作业的输出。

​ 需要注意的是,只有在当前终端会话中的已停止的作业才能被调至后台继续运行。如果有多个已停止的作业,可以使用 jobs 命令查看作业列表,并确定要调至后台运行的作业的作业号。

​ 下面我们来演示一下这两个指令的使用:

Ⅴ. 守护进程实现

​ 通过上面的学习我们也知道,我们默认创建的作业和后台进程,都 bash 这个包工头指挥着,如果说我们退出了服务器,有可能因为 bash 等进程也退出了,此时我们的后台进程,就全被干掉了,这不符合我们的预期呀!

​ 所以如果想让我们写的服务端不受用户登录注销的影响,就必须 让服务端自成会话,自成进程组,使其与终端设备的状态无关,可以一直运行的进程。这样子相当于我们就让我们想要后台执行的进程,自己当包工头!

1、常见接口

① 创建守护进程 – daemon

​ 这是 linux 中自带的一个生成守护进程的函数!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <unistd.h>
int daemon(int nochdir, int noclose);
  • 用途:该函数的意义在于将进程转变为守护进程,守护进程是在后台运行的进程,通常不与控制台交互,而是在后台执行某些任务,如网络服务器等。通过调用该函数,可以实现以下功能:
    • 将当前进程的父进程置为 init 进程(进程 id1),从而脱离原有的进程组和会话。
    • 将当前进程的工作目录切换到根目录下,以避免守护进程因为当前工作目录被卸载等原因导致崩溃。
    • 关闭标准输入、输出和错误输出,以避免守护进程输出信息到控制台,从而影响用户体验。
  • 参数:
    • nochdir:是否改变当前工作目录。如果为 0,则将当前工作目录切换到根目录下,否则保持不变。
    • noclose:是否关闭标准输入、标准输出、标准错误的文件描述符。如果为 0,则不关闭,否则关闭。
  • 返回值:
    • 调用成功返回 0
    • 调用失败返回 -1,并设置 errno 变量。

​ 一般来说,我们是不会选择这个函数来创建守护进程的,因为局限性就摆在这,而守护进程可能因为需求不同,是需要一直变化限制的,所以我们会用下面其它几个函数来自己写一个 daemon 函数!

② 自成会话函数 – setsid

​ 这是一个在 Unix 和类 Unix 系统(如 Linux)中使用的系统调用,用于创建一个新的会话,并使调用进程成为该新会话的领导者。这个函数通常在创建守护进程时使用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <unistd.h>
pid_t setsid(void);
  • 作用:
    • 使调用进程成为一个新的会话领导者,并且不会受到终端的控制。这对于守护进程和后台进程非常有用,因为它们需要在后台运行,并且不希望受到终端的影响。调用 setsid 后,新进程组 PGID 等于调用进程的 PID该进程成为新会话的领导进程,并且不再有控制终端
    • 进程组的组长不能调用 setsid()函数来创建一个新的会话。因此,通常在调用该函数之前,会先调用 fork(),然后在子进程中调用 setsid(),因为子进程虽然 PGID 是一样的,但是因为子进程的 PID 是新分配的,所以两者不可能相等!
  • 返回值:
    • 成功返回新会话的 SID
    • 失败返回 -1

当调用该函数时,会发生以下几件事情:

  1. 创建一个新的会话,调用进程成为新会话的领导者,也成为新进程组的领导者,并且新会话没有控制终端(如果有的话会被切断)。
  2. 调用进程成为新进程组的领导者。新进程组 PGID 等于调用进程的 PID
  3. 调用进程将与其父进程的会话和进程组脱离。
③ 获取会话ID函数 – getsid
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <unistd.h>
pid_t getsid(pid_t pid);
  • 作用:
    • 获取指定进程的 SID
  • 参数:
    • pid:进程id
  • 返回值:
    • 如果 pid 参数为 0,则返回调用进程的 SID
    • 如果 pid 参数为一个有效的进程 ID,则返回该进程所属会话的 SID
    • 如果出现错误,返回 -1,并设置 errno 以指示错误。

​ 需要注意的是,只有会话的领导者进程才能调用 getsid() 函数来获取会话ID

2、自主实现守护进程函数

​ 具体的步骤都在注释中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#pragma once
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

const char* DEV = "/dev/null";

void daemonSelf(const char* curPath = nullptr)
{
    // 1. 创建子进程并退出父进程
    // 此时就要创建一个子进程,然后将当前进程给退出,让子进程去调用即可
    pid_t pid = fork();
    if(pid > 0) 
    {
        exit(0); // 退出当前父进程
    }
    else if(pid < 0)
    {
        perror("fork error!");
        exit(1);
    }

    // 2. 子进程调用setsid()创建一个自成会话,调用setsid()的进程不能是组长,组长是之前的父进程!
    if(setsid() < 0) 
    {
        perror("setsid error");
        exit(1);
    }

    // 3. 让调用进程忽略掉容易异常的信号:
    // 		忽略SIGHUP信号的目的是为了防止守护进程在终端断开时终止
    // 		忽略SIGPIPE信号是防止当进程向一个已经关闭写端的管道写入数据时,内核会向进程发送SIGPIPE信号,或者当进程向一个已经关闭的socket连接写入数据时,内核也会向进程发送SIGPIPE信号。
    signal(SIGHUP, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGINT, SIG_IGN);

    // 4. 创建孙子进程后再次fork,以防止守护进程重新打开控制终端
    pid = fork();
    if(pid > 0) 
    {
        exit(0); // 退出当前父进程
    }
    else if(pid < 0)
    {
        perror("fork error!");
        exit(1);
    }

    // 5. 守护进程需要脱离终端,需要关闭或者重定向以前进程默认打开的文件
    // 很显然关闭那些文件是不太好的做法,所以优先选择重定向
    // 而我们可以重定向到 ‘/dev/null’ 这个文件中,相当于linux中的一个垃圾桶
    // 如果创建这个重定向文件失败了,我们才采用关闭文件的方式
    int fd = open(DEV, O_RDWR); 
    if(fd != -1)
    {
        // 打开文件成功,则重定向到/dev/null中
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        close(fd);
    }
    else
    {
        // 失败则直接关闭
        close(0);
        close(1);
        close(2);
    }

    // 6. 可选项:根据需求将进程执行路径更改
    if(curPath != nullptr)
        chdir(curPath);
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-05-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
从进程组、会话、终端的概念深入理解守护进程
「守护进程」是 Linux 的一种长期运行的后台服务进程,也有人称它为「精灵进程」。我们常见的 httpd、named、sshd 等服务都是以守护进程 Daemon 方式运行的,通常服务名称以字母d结尾,也就是 Daemon 第一个字母。与普通进程相比它大概有如下特点:
用户3105362
2021/02/04
1.2K0
进程组、会话、控制终端概念,如何创建守护进程?
守护进程,也就是通常所说的Daemon进程,是Linux中的后台服务进程。周期性的执行某种任务或等待处理某些发生的事件。
睡魔的谎言
2020/11/25
1.6K0
linux系统编程之进程(五):终端、作业控制与守护进程
该文介绍了如何在Linux系统中通过fork函数创建守护进程,并给出了具体的示例代码。同时,文章还介绍了守护进程的一些常见用途,如保证程序在后台运行、处理控制台输入输出等。
s1mba
2018/01/03
2.8K0
linux系统编程之进程(五):终端、作业控制与守护进程
Linux 守护进程|应急响应
通常我们都是通过以上两种方式来获得一个shell,之后运行程序的,此时我需要纠正一个概念,我们通常都说获得一个shell,本质上来说,我们获取了一个session(会话,以下session都是会话)
意大利的猫
2021/03/18
4.1K0
Linux 守护进程|应急响应
Linux内核编程--进程组和守护进程
进程组:进程组是多个进程的集合, 接收同一个终端的各类信号信息。进程调用setpgid(pid, pgid)可以加入一个现有的进程组或者创建一个新的进程组。
Coder-ZZ
2022/05/09
3.2K0
Linux内核编程--进程组和守护进程
【在Linux世界中追寻伟大的One Piece】进程间关系与守护进程
其实每一个进程除了有一个进程ID(PID)之外,还属于一个进程组。进程组是一个或者多个进程的集合, 一个进程组可以包含多个进程。 每一个进程组也有一个唯一的进程组ID(PGID), 并且这个 PGID类似于进程ID, 同样是一个正整数, 可以存放在pid_t数据类型中。
枫叶丹
2024/09/24
1330
【在Linux世界中追寻伟大的One Piece】进程间关系与守护进程
什么是守护进程?
在了解守护进程之前,需要先知道什么是什么是终端?什么是作业?什么是进程组?什么是会话?
全栈程序员站长
2022/09/07
1.1K0
【Linux网络编程】:守护进程,前台进程,后台进程
●无控制终端:脱离控制终端,避免收到终端的干扰,它是和客户端进行交流的。和Xshell终端摆脱了联系。
用户11396661
2025/02/04
3480
【Linux网络编程】:守护进程,前台进程,后台进程
linux 后台运行进程:& , nohup
当我们在终端或控制台工作时,可能不希望由于运行一个作业而占住了屏幕,因为可能还有更重要的事情要做,比如阅读电子邮件。对于密集访问磁盘的进程,我们更希望它能够在每天的非负荷高峰时间段运行(例如凌晨)。为了使这些进程能够在后台运行,也就是说不在终端屏幕上运行,有几种选择方法可供使用。
DevOps在路上
2023/05/16
5.3K0
linux 后台运行进程:& , nohup
将 Web 应用丢给守护进程
最近老是要把 Web App/Service 部署在个人的服务器上进行测试,发现自己不怎么熟悉「前提:不上 docker ,逃~」,特写此文章来纪念下??(之前部署的 Web App/Service
Cloud-Cloudys
2020/07/07
1.6K0
将 Web 应用丢给守护进程
Linux - 请允许我静静地后台运行
枕边书
2018/01/04
1.8K0
Linux - 请允许我静静地后台运行
【计算机网络】日志与守护进程
一般使用cout进行打印,但是cout打印是不规范的 实际上 是采用日志进行打印的
lovevivi
2023/11/17
2210
【计算机网络】日志与守护进程
守护进程
在Linux中,session(会话)通常指的是与用户交互的一个环境,它是系统中与某个用户交互的一系列活动的集合。会话在Linux系统中有多种用途,下面是几种常见的会话类型及其相关概念:
ljw695
2025/01/03
2780
守护进程
守护进程「建议收藏」
在UNIX系统中, 用户通过终端登录系统后得到一个Shell进程, 这个终端成为Shell进程的控制终端(Controlling Terminal), 进程中, 控制终端是保存在PCB中的信息, 而fork会复制PCB中的信息, 因此由Shell进程启动的其它进程的控制终端也是这个终端. 默认情况下(没有重定向), 每个进程的标准输入, 标准输出和标准错误输出都指向控制终端, 进程从标准输入读也就是读用户的键盘输入, 进程往标准输出或标准错误输出写也就是输出到显示器上. 信号中还讲过, 在控制终端输入一些特殊的控制键可以给前台进程发信号, 例如Ctrl-C表示SIGINT,Ctrl-\表示SIGQUIT。
全栈程序员站长
2022/09/16
6340
Python实现守护进程
專 欄 ❈汤英康,Python程序员,负责设计和开发大数据监控平台的相关产品。 PyCon China2016 深圳 讲师。 博客:http://blog.tangyingkang.com/ ❈— Daemon场景 考虑如下场景:你编写了一个python服务程序,并且在命令行下启动,而你的命令行会话又被终端所控制,python服务成了终端程序的一个子进程。因此如果你关闭了终端,这个命令行程序也会随之关闭。 要使你的python服务不受终端影响而常驻系统,就需要将它变成守护进程。 守护
Python中文社区
2018/01/31
2K0
Python守护进程daemon实现
守护进程是系统中生存期较长的一种进程,常常在系统引导装入时启动,在系统关闭时终止,没有控制终端,在后台运行。守护进程脱离于终端是为了避免进程在执行过程中的信息在任何终端上显示并且进程也不会被任何终端所产生的终端信息所打断。 在这里,我们在Linux2.6内核的centos中,ps -ef |awk '{print $1"\t "$2"\t "$3"\t  "$8}'看到:PPID=0的进程有两个,分别是PID=1的/sbin/init进程和PID=2的[kthreadd]进程。
py3study
2020/01/07
7.9K0
PHP中的会话
2、当执行php xxx.php 时,默认系统会把当前的进程设置为会话首进程(使用strace查看),所以当前会话首进程不能使用posix_setsid 创建为会话首进程,只能使用子进程调用此函数
北溟有鱼QAQ
2021/06/08
1.4K0
nohup、&、setsid、fork和fg、bg究竟有啥区别?
在后台运行的进程不一定是守护进程!一个进程要成为守护进程,必须做到以下两点:
一见
2018/08/10
2.4K0
Linux守护进程
守护进程在 Linux 系统中极为重要,它们是许多服务器的核心组成部分,例如 Internet 服务器 inetd 和 Web 服务器 httpd。这些进程不仅负责提供网络服务,还执行各种系统任务,例如作业调度进程 crond。
不脱发的程序猿
2024/11/26
3920
Linux守护进程
Linux守护进程
进程组,也叫做作业。BSD于1980年前后向Unix中增加的一个新特性,代表一个或者多个进程的集合,每个进程都属于一个进程组。操作系统设计进程组的概念主要就是为了简化对多个进程的管理。
mindtechnist
2024/08/08
3670
Linux守护进程
相关推荐
从进程组、会话、终端的概念深入理解守护进程
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验