【Linux网络#6】:进程间关系 与 守护进程

【Linux网络#6】:进程间关系 与 守护进程

 

📃个人主页island1314

🔥个人专栏:Linux—登神长阶

⛺️ 欢迎关注:👍点赞 👂🏽留言 😍收藏  💞 💞 💞


进程间关系与守护进程目录

1. 前言

2. 进程间关系 -- 作业控制

 2.1 什么是作业(job)和作业控制(Job Control)?

2.2 作业号

2.3 作业状态

2.4 作业切回和挂起 

作业切回

作业挂起

2.5 查看后台执行或挂起的作业

2.6 作业控制的信号

3. 进程组 

3.1 进程组概念

3.2 组长进程

4. 会话 

4.1 什么是会话

4.2 如何创建会话

5. 控制终端

6. 守护进程

6.1 基本概念

6.2 如何将服务守护进程化

7. 共勉



1. 前言

还记得我们之前学的前台和后台任务嘛,如下:

我们一般把不需要交互的任务放在 后台,我们之前也说了,如下:

我们可以发现在前台任务执行时,输入其他指令也不会产生别的影响,而在后台任务中,我们输入的每个指令都会有相对应的输出,因此我们可以知道:

  • 前台进程:用户直接与之交互的任务或程序。用户可以通过输入、点击等方式与这些任务进行实时的交互。通常会占用用户的注意力
  • 后台进程:不需要用户直接交互,且可以在用户进行其他操作时继续运行的任务。用户不需要关注它们的进程

2. 进程间关系 -- 作业控制

 2.1 什么是作业(job)和作业控制(Job Control)?

🧀 作业是针对用户来讲,用户完成某项任务而启动的进程

  • 一个作业既可以只包含一个进程,也可以包含多个进程,进程之间互相协作完成任务, 通常是一个进程管道(之前上面有演示的)

Shell 分前后台来控制的不是进程,而是 作业 或者 进程组

  • 一个前台作业可以由多个进程组成,一个后台作业也可以由多个进程组成
  • Shell 可以同时运行一个前台作业和任意多个后台作业,这称为作业控制。

2.2 作业号

🔥 放在后台执行的程序或命令称为后台命令,可以在命令的后面加上&符号从而让 Shell 识别这是一个后台命令,后台命令不用等待该命令执行完成,就可立即接收新的命令

  • 此外:后台进程执行完后会返回一个作业号以及一个进程号(PID)

第一行表示作业号和进程 ID, 可以看到作业号是 1, 进程 ID 是 3251046

2.3 作业状态

作业状态含义
正在运行【Running】后台作业(&),表示正在执行
完成【Done】作业已完成,返回的状态码为 0 
完成并退出【Done(code)】作业已完成并退出,返回的状态码为 0
已停止【Stopped】前台作业,当前被 Ctrl + Z 挂起
已终止【Terminated】作业被终止

2.4 作业切回和挂起 

作业切回

🐇 如果想将挂起的作业切回,可以通过 fg 命令,fg 后面可以跟作业号或作业的命令名称。如果参数缺省则会默认将作业号为 1 的作业切到前台来执行,若当前系统只有一个作业在后台进行,则可以直接使用 fg 命令不带参数直接切回。

具体的参数参考如下:

如下操作: 

注意: 当通过 fg 命令切回作业时,若没有指定作业参数,此时会将默认作业切到前台执行,即带有 “+” 的作业号的作业
 

作业挂起

🐇 我们在执行某个作业时,可以通过 Ctrl+Z 键将该作业挂起,然后 Shell 会显示相关的作业号、状态以及所执行的命令信息。

例如我们把刚刚切回起来的作业挂起到后台:

  • Ctrl + z 就相当于暂停前台,因为我们不能让一个暂停的任务来拥有终端,因此这个进程自动会被放到后台
  • bg 1:让其在后台运行起来

2.5 查看后台执行或挂起的作业

我们可以直接通过输入 jobs 命令,查看本用户当前后台执行或挂起的作业

  • 参数 -l 则显示作业的详细信息
  • 参数 -p 则只显示作业的 PID

关于默认作业:对于一个用户来说,只能有一个默认作业(+),同时也只能有一个即将成为默认作业的作业(-),当默认作业退出后,该作业会成为默认作业。

  •  +: 表示该作业号是默认作业
  •  - : 表示该作业即将成为默认作业
  • 无符号: 表示其他作业

2.6 作业控制的信号

上面提到了键入 Ctrl + Z 可以将前台作业挂起,实际上是将 STGTSTP 信号发送至前台进程组作业中的所有进程, 后台进程组中的作业不受影响。 在 unix 系统中, 存在 3 个特殊字符可以使得终端驱动程序产生信号, 并将信号发送至前台进程组作业, 它们分别是:

  • Ctrl + C: 中断字符, 会产生 SIGINT 信号
  • Ctrl + \: 退出字符, 会产生 SIGQUIT 信号
  • Ctrl + Z:挂起字符, 会产生 STGTSTP 信号

 🔥 结论:

  1. 任何时刻,只允许有一个前台进程,多个或者 0 个 后台进程,因为键盘(标准输入) 只有1 个
  2. 命令行启动任何进程,bash 会自动变成后台,直到前台进程接收

3. 进程组 

💢 对于上面学的,我们基于管道(|)把所有的任务级联起来  

 

 

  • 这些进程级联起来想完成一个任务,我们就把这些进程 叫作 一个 进程组
  • 他们的进程组的 PGID 都是一样的,即属于一个组,说明进程除了有兄弟、父子关系之外,还有组内关系
  • 也就是说多个进程组如果通过管道级联或者通过某种方式让几个进程协作起来,那么这些进程它们的 PGID 所对应的值其实是多个进程当中创建的第一个进程 pid,以第一个进程作为自己的老大,即作为进程组 ID,这个老大其实也就是组长进程(下面我们会说的)
  • 那 这个 PGID 是什么呢》其实就是我们进程 PCB 中包含的一个字段

补充:通常我们都是使用管道将几个进程编成一个进程组,如上面

下面我们来做个详细了解

3.1 进程组概念

💦 上面我们说到其实每一个进程除了有一个进程 ID(PID)之外 还属于一个进程组。

  • 进程组是一个或者多个进程的集合, 一个进程组可以包含多个进程。
  • 每一个进程组也有一个唯一的进程组 ID(PGID), 并且这个 PGID 类似于进程 ID, 同样是一个正整数, 可以存放在 pid_t 数据类型中。
  • 进程组 是为了完成一个任务的 或 作业的 
$ ps -eo pid,pgid,ppid,comm | grep test #结果如下 PID PGID PPID COMMAND 2830 2830 2259 test # -e 选项表示 every 的意思, 表示输出每一个进程信息 # -o 选项以逗号操作符(,)作为定界符, 可以指定要输出的列

3.2 组长进程

每一个进程组都有一个组长进程。 组长进程的 ID 等于其进程 ID。我们可以通过 ps 命令看到组长进程的现象:

$ ps ajx | head -1 && ps ajx | grep sleep PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 3185939 3288744 3185939 3185939 ? -1 S 1001 0:00 sleep 180 3099626 3289414 3289414 3099626 pts/0 3289414 S+ 1001 0:00 sleep 10000 3099626 3289415 3289414 3099626 pts/0 3289414 S+ 1001 0:00 sleep 20000 3099626 3289416 3289414 3099626 pts/0 3289414 S+ 1001 0:00 sleep 30000 3203921 3289620 3289619 3203921 pts/1 3289619 S+ 1001 0:00 grep --color=auto sleep 

从结果上看 sleep 10000 进程的 PID 和 PGID 相同, 那也就是说明 sleep 10000 进程是该进程组的组长进程, 该进程组包括 sleep 10000 、sleep 20000、sleep 30000 三个进程

  • 进程组组长的作用: 进程组组长可以创建一个进程组或者创建该组中的进程
  • 进程组的生命周期: 从进程组创建开始到其中最后一个进程离开为止。

 注意:主要某个进程组中有一个进程存在, 则该进程组就存在, 这与其组长进程是否已经终止无关。

补充一句,在  Linux 中我们一般使用 ps 命令查看进程的,如下:

$ ps -o pid,ppid,pgid,sid,comm # 输出 PID PPID PGID SID COMMAND 3203921 3203920 3203921 3203921 bash 3292594 3203921 3292594 3203921 ps

4. 会话 

4.1 什么是会话

刚刚我们谈到了进程组的概念, 那么会话又是什么呢?

会话其实和进程组息息相关,会话可以看成是一个或多个进程组的集合, 一个会话可以包含多个进程组。每一个会话也有一个会话 ID(SID)

 

 

  • 比如:上面中进程组中的 SID 都相同,并且与父进程 Id 一样,这个 SID 究竟是什么呢? 
  • 在操作系统中,SID(Session ID) 是会话标识符,用于标识一个会话(Session)
  •  会话是一组进程的集合,通常与用户登录会话相关联。每个会话都有一个唯一的 SID,用于管理该会话中的进程

上边我们提到了会话 ID, 那么会话 ID 是什么呢? 我们可以先说一下会话首进程, 会话首进程是具有唯一进程 ID 的单个进程, 那么我们可以将会话首进程的进程 ID 当做是会话 ID。

  • 注意:会话 ID 在有些地方也被称为 会话首进程的进程组 ID, 因为会话首进程总是一个进程组的组长进程, 所以两者是等价的

4.2 如何创建会话

可以调用 setsid 函数来创建一个会话, 前提是调用进程不能是一个进程组的组长。

#include <unistd.h> /* *功能:创建会话 *返回值:创建成功返回 SID, 失败返回-1 */ pid_t setsid(void);

该接口调用之后会发生:

  • 调用进程会变成新会话的会话首进程。 此时, 新会话中只有唯一的一个进程
  • 调用进程会变成进程组组长。 新进程组 ID 就是当前调用进程 ID
  • 该进程没有控制终端。 如果在调用 setsid 之前该进程存在控制终端, 则调用之后会切断联系

需要注意的是: 这个接口如果调用进程原来是进程组组长, 则会报错, 为了避免这种情况, 我们通常的使用方法是:先调用 fork 创建子进程, 父进程终止, 子进程继续执行, 因为子进程会继承父进程的进程组 ID, 而进程 ID 则是新分配的, 就不会出现错误的情况

5. 控制终端

先说下什么是控制终端?

🔥 在 UNIX 系统中,用户通过终端登录系统后得到一个 Shell 进程,这个终端成为 Shell 进程的控制终端。

  • 控制终端是保存在 PCB 中的信息,我们知道 fork 进程会复制 PCB中的信息,因此由 Shell 进程启动的其它进程的控制终端也是这个终端。
  • 默认情况下没有重定向,每个进程的标准输入、标准输出和标准错误都指向控制终端,进程从标准输入读也就是读用户的键盘输入,进程往标准输出或标准错误输出写也就是输出到显示器上。

另外会话、进程组以及控制终端还有一些其他的关系,我们在下边详细介绍一下:

  • 一个会话可以有一个控制终端,通常会话首进程打开一个终端(终端设备或伪终端设备)后,该终端就成为该会话的控制终端。
  • 建立与控制终端连接的会话首进程被称为 控制进程
  • 一个会话中的几个进程组可被分成一个前台进程组以及一个或者多个后台进程组。
  • 如果一个会话有一个控制终端,则它有一个前台进程组,会话中的其他进程组则为后台进程组。
  • 无论何时进入终端的中断键(ctrl+c)或退出键(ctrl+\),就会将中断信号发送给前台进程组的所有进程
  • 如果终端接口检测到调制解调器(或网络)已经断开,则将挂断信号发送给控制进程(会话首进程)

这些特性的关系如下图所示:

6. 守护进程

6.1 基本概念

如果我们想要一个不受会话影响的进程,该怎么做呢?

  • 由 bash 创建一个 子进程 或者 进程组,如果进程组中只有一个进程,那么就是单进程的进程组
  • 然后如果不想受登录退出的影响,那么就要把它独立形成一个新的会话,那么此进程与bash 的关系 从 包含 -> 并列关系,此后就不受退出登录的影响,此时就是一个独立的会话
  • 守护进程 就是这样的,

 守护进程也属于后台进程的一种,但是两者有个本质区别

  • 后台进程仍然属于这个会话,而守护进程属于自己独立的会话

守护进程(Daemon Process) 是一种在后台运行的特殊进程,通常用于执行系统任务或服务,而不依赖于用户交互。理解守护进程的关键在于它的两个核心特性:

  1. 守护进程要脱离终端,以确保它不会受到终端关闭或用户注销的影响终端依赖问题
    1. 普通进程可能会依赖于终端进行输入输出(例如,从终端读取输入或向终端打印输出)。如果终端关闭,这些进程可能会被终止或无法正常运行。
    2. 守护进程的目标
      守护进程通常用于提供系统服务(如网络服务、日志服务等),这些服务需要在后台长期运行,而不受用户登录或终端关闭的影响。 
  2. 守护进程其实就是一个孤儿进程,它脱离了父进程和终端,由系统的 init 进程(PID 1) 接管
    1.  由于守护进程没有父进程,它不会受到用户注销或终端关闭的影响,可以在后台长期运行

6.2 如何将服务守护进程化

 这里我们以我们之前写的 网络版计算器 为例

【Linux】:应用层自定义协议 & 序列化 & 网络版计算器https://blog.ZEEKLOG.net/island1314/article/details/145140033?sharetype=blogdetail&sharerId=145140033&sharerefer=PC&sharesource=island1314&spm=1011.2480.3001.8118https://blog.ZEEKLOG.net/island1314/article/details/145140033?sharetype=blogdetail&sharerId=145140033&sharerefer=PC&sharesource=island1314&spm=1011.2480.3001.8118https://blog.ZEEKLOG.net/island1314/article/details/145140033?sharetype=blogdetail&sharerId=145140033&sharerefer=PC&sharesource=island1314&spm=1011.2480.3001.8118https://blog.ZEEKLOG.net/island1314/article/details/145140033?sharetype=blogdetail&sharerId=145140033&sharerefer=PC&sharesource=island1314&spm=1011.2480.3001.8118

Daemon.hpp 

#pragma once #include <iostream> #include <cstdlib> #include <signal.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #define ROOT "/" #define devnull "/dev/null" void Daemon(bool ischdir, bool isclose) { // 1. 守护进程一般要屏蔽到特定的异常信号 signal(SIGCHLD, SIG_IGN); signal(SIGPIPE, SIG_IGN); // 2. 成为非组长, fork之后父进程直接退出, 子进程代替父进程向后运行 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(devnull, O_WRONLY); if (fd > 0) { // 各种重定向 dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); close(fd); } } }

TcpServer.cc 修改如下:

 

 

结果如下:

 

 

  • 在上面,我们可以看到 其 PGID = SID,表示其独立的进程了,终端为问号,TPGID = -1,表示其已经与终端去关联了

现在我们就可以把终端关掉了,然后该进程就在我们的云服务器上 24 小时运行了,此时我们也可以把它上传到网上进行使用的,来随时访问了

 

 

  • 使用 killall 可执行文件 删掉守护进程

注意一下:这里当我们直接发送动态链接的可执行文件会出现阶层库的问题

  • 静态链接是将库的代码直接嵌入到最终的可执行文件中。程序在编译时会将所有依赖的库代码复制到可执行文件中
  • 动态链接是在程序运行时加载所需的库。可执行文件只包含对库的引用,而不包含库的实际代码

所以如果有问题的话,我们最好还是发送其静态链接最好,加个 -static,编译的时候

 补充 -- 系统提供的 daemon

7. 共勉

【*★,°*:.☆( ̄▽ ̄)/$:*.°★* 】那么本篇到此就结束啦,如果有不懂 和 发现问题的小伙伴可以在评论区说出来哦,同时我还会继续更新关于【Linux】的内容,请持续关注我 !!

Read more

Fish Speech-1.5多语种语音合成实战:中英混合文本发音规则处理技巧

Fish Speech-1.5多语种语音合成实战:中英混合文本发音规则处理技巧 1. 引言 语音合成技术正在改变我们与数字内容互动的方式,而多语种混合文本的合成更是其中的技术难点。想象一下,当你需要制作一段同时包含中文和英文的教学音频,或者一段中英混合的产品介绍时,传统的单语种语音合成往往会出现发音不自然、语调突兀的问题。 Fish Speech V1.5作为基于超过100万小时多语言音频数据训练的先进文本转语音模型,特别擅长处理这类混合语言场景。本文将带你从零开始,通过xinference 2.0.0部署Fish Speech-1.5,并重点分享中英混合文本的发音处理技巧,让你能够生成自然流畅的多语言语音内容。 2. Fish Speech-1.5模型概述 2.1 模型特点与优势 Fish Speech V1.5是一个功能强大的多语言文本转语音模型,其核心优势在于支持12种主要语言的高质量语音合成。该模型基于海量音频数据训练,其中中文和英语各超过30万小时,日语超过10万小时,其他语言如德语、法语、西班牙语等也都有充足的训练数据。 这种大规模多语言训练使得模型在处理

By Ne0inhk
他到底喜欢我吗?赛博塔罗Java+前端实现,一键解答!

他到底喜欢我吗?赛博塔罗Java+前端实现,一键解答!

个人主页-爱因斯晨 文章专栏-赛博算命 原来我们在已往的赛博算命系列文章中的源码已经传到我的Github仓库中,有兴趣的家人们可以自己运行查看。 Github 源码中的一些不足,还恳请业界大佬们批评指正! 本文章的源码已经打包至资源绑定,仓库中也同步更新。 一、引言 在数字化浪潮席卷全球的当下,传统塔罗牌占卜这一古老智慧也迎来了新的表达形式 ——“赛博塔罗”。本文档旨在深入剖析塔罗牌的核心原理,并详细介绍如何利用 Java 语言实现一个简易的塔罗牌预测程序,展现传统神秘学与现代编程技术的融合。 二、塔罗牌原理 (一)集体潜意识与原型理论 瑞士心理学家卡尔・荣格提出的 “集体潜意识” 理论,为塔罗牌的运作提供了重要的心理学支撑。该理论认为,人类拥有超越个体经验的共同心理结构,其中蕴含着 “原型”—— 即普遍存在的、象征性的模式或形象。 塔罗牌的 22 张大阿尔卡那牌恰好与这些基本原型相对应。例如,“愚人” 代表着天真与新开始的原型,“魔术师” 象征着创造力与潜能的原型,“女祭司” 则体现了智慧与直觉的原型。这些原型是全人类共通的心理元素,这也正是不同文化背景的人都能

By Ne0inhk
WebGL缓冲区使用与多点绘制实战

WebGL缓冲区使用与多点绘制实战

一、前言 1.1 适用人群 本教程适合已经了解基础的HTML/CSS/JavaScript,对WebGL有基本概念(知道着色器、绘制流程),但希望深入理解其核心性能机制——缓冲区(Buffer) 的开发者。我们将聚焦于“如何使用缓冲区高效地管理和绘制大量顶点数据”,解决“如何绘制成千上万个点而不卡顿”的核心痛点。 效果如图: 1.2 核心目标 * 理解本质:掌握WebGL缓冲区(Buffer)的作用,它如何与GPU通信,以及为何它是高性能绘制的基石。 * 掌握方法:学会创建、绑定、配置和数据填充缓冲区,实现单次绘制调用(draw call)渲染多个点。 * 实战应用:通过完整代码示例,从绘制单个点进阶到动态绘制成百上千个随机点,并理解其性能优势。 二、基础知识:什么是WebGL缓冲区(Buffer)? 功能说明 在WebGL中,缓冲区是GPU上的一块内存区域,用于存储顶点数据(

By Ne0inhk
一文了解Blob文件格式,前端必备技能之一

一文了解Blob文件格式,前端必备技能之一

文章目录 * 前言 * 一、什么是Blob? * 二、Blob的基本特性 * 三、Blob的构造函数 * 四、常见使用场景 * 1. 文件下载 * 2. 图片预览 * 3. 大文件分片上传 * 四、Blob与其他API的关系 * 1. File API * 2. FileReader * 3. URL.createObjectURL() * 4. Response * 五、性能与内存管理 * 六、实际案例:导出Word文档 * 七、浏览器兼容性 * 八、总结 前言 最近在项目中需要导出文档时,我首次接触到了 Blob 文件格式。作为一个前端开发者,虽然经常听到 "Blob" 这个术语,但对其具体原理和应用场景并不十分了解。经过一番研究和实践,

By Ne0inhk