【Linux】进程间关系与守护进程

devtools/2025/2/23 3:27:49/

文章目录

  • 1. 进程组
  • 2. 会话
    • 2.1 什么是会话
    • 2.2 如何创建会话
    • 2.3 守护进程
  • 3. 作业控制

在这里插入图片描述

1. 进程组

我们运行下面的命令

sleep 10000 | sleep 20000 | sleep 30000

然后查看进程的信息:

在这里插入图片描述

可以看到,其实每一个进程除了有进程PID、PPID之外,还属于一个进程组(PGID)。 进程组是一个或者多个进程的集合,一个进程组可以包含多个进程,每一个进程组也有一个唯一的进程组 ID(PGID)。

进程组的ID一般是组长的ID,即组中最先创建的进程的PID。

  • 进程组组长的作用: 进程组组长可以创建一个进程组或者创建该组中的进程

  • 进程组的生命周期: 从进程组创建开始到其中最后一个进程离开为止。 注意:只要某个进程组中有一个进程存在, 则该进程组就存在, 这与其组长进程是否已经终止无关

2. 会话

2.1 什么是会话

系统登录时,会创建终端文件 + bash进程;会话 = bash进程 + 终端文件 (每一个新的登录,都会形成一个-bash进程)

终端文件:/dev/pts/
向不同的终端文件中输出,就会在其会话页面中显示
在这里插入图片描述

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

在这里插入图片描述

下面是我在同一个会话中运行了两个程序查询出来的结果

在这里插入图片描述

2.2 如何创建会话

那如何创建一个会话呢?

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

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

该接口调用之后会发生:

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

需要注意的是:

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

2.3 守护进程

守护进程(Daemon Process)是一种在后台运行的特殊进程,通常用于执行特定的任务或服务,而不需要用户直接交互。

守护进程的特点

  • 后台运行:守护进程在后台运行,不会在前台显示任何界面或交互窗口。它独立于用户终端,即使用户注销,守护进程仍然可以继续运行
  • 独立于控制终端:守护进程通常与控制终端分离,这意味着它不会因终端的关闭而终止。例如,一些网络服务(如 Web服务器)会以守护进程的形式运行,确保服务的持续可用性。

那如何创建守护进程呢?

  1. 创建子进程并退出父进程:通过 fork() 创建子进程,父进程退出。这样可以确保子进程不会被终端挂起。
  2. 创建新会话:通过 setsid() 创建一个新的会话,使子进程成为会话的领导者,从而脱离控制终端
  3. 改变工作目录:通常将工作目录更改为根目录(/),以避免守护进程因当前工作目录被卸载而导致问题。
  4. 关闭文件描述符:关闭所有打开的文件描述符,避免资源泄漏。
const char *root = "/";
const char *dev_null = "/dev/null";     
void Daemon(bool ischdir,bool isclose)
{//1. 忽略可能引起程序异常退出的信号signal(SIGCHLD,SIG_IGN);signal(SIGPIPE,SIG_IGN);int rid = fork();   //2.创建子进程if(rid > 0){exit(1);    //父进程直接退出}int id = setsid();  //3.子进程调用,子进程独立成立一个会话// 4. 每一个进程都有自己的 CWD, 是否将当前进程的 CWD 更改成为 /根目录//以避免守护进程因当前工作目录被卸载而导致问题if(ischdir)chdir(root);// 5. 已经变成守护进程啦,不需要和用户的输入输出,错误进行关联了if(isclose){close(0);close(1);close(2);}else{//避免后面有向0,1,2中操作的,为了防止失败//一般将0、1、2重定向到“黑洞”(任何写入到 /dev/null 的数据都会被永久丢弃,不会保存在任何地方)int fd = open(dev_null,O_RDWR);if(fd > 0){dup2(fd,0);dup2(fd,1);dup2(fd,2);close(fd);  //避免fd泄漏,重定向后关闭}}
}

如何将一个服务器守护进程化呢?特别简单,直接调用Daemon即可

// ./server port
int main(int argc, char *argv[])
{if (argc != 2){std::cout << "Usage : " << argv[0] << " port" << std::endl;return 0;}uint16_t localport = std::stoi(argv[1]);Daemon(false, false);std::unique_ptr<TcpServer> svr(new TcpServer(localport,HandlerRequest));svr->Loop();return 0;
}

即使当前会话关闭了,守护进程依旧还在。它是一个独立的会话

3. 作业控制

  1. 什么是作业?

作业是针对用户来讲的,一个进程组要完成的任务,一般叫做作业。
一个作业既可以只包含一个进程,也可以包含多个进程,进程之间互相协作完成任务(通常是通过进程管道)。

Shell 分前后台来控制的不是进程而是作业或者进程组。 一个前台作业可以由多个进程组成,一个后台作业也可以由多个进程组成, Shell 可以同时运行一个前台作业和任意多个后台作业,这称为作业控制。

例如:下面通过管道协作的sleep命令就是一个作业
在这里插入图片描述
此时无论执行什么(只要会话不退出),都不会影响后台作业。

放在后台执行的程序或命令称为后台命令,可以在命令的后面加上&符号从而让Shell识别这是一个后台命令,后台命令不用等待该命令执⾏完成,就可立即接收新的命令,另外后台进程执行完后会返回一个作业号以及一个进程号(PID)。

  1. 作业控制

将作业放回前台:fd + 作业号
在这里插入图片描述

如果再想将作业放回后台,要先暂停作业(Ctrl + z),然后再bg + 作业号,才可将作业切回到后台运行。

在这里插入图片描述

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

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

在这里插入图片描述

关于作业号后面的+、-号

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

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

常见的作业状态如下表所示:

在这里插入图片描述

  1. 作业控制相关的信号

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

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

在这里插入图片描述


http://www.ppmy.cn/devtools/159114.html

相关文章

【做一个微信小程序】校园地图页面实现

前言 上一个教程我们实现了小程序的一些的功能&#xff0c;有背景渐变色&#xff0c;发布功能有的呢&#xff0c;已支持图片上传功能&#xff0c;表情和投票功能开发中&#xff08;请期待&#xff09;。下面是一个更高级的微信小程序实现&#xff0c;包含以下功能&#xff1a;…

6.appender

文章目录 一、前言二、源码解析AppenderUnsynchronizedAppenderBaseOutputStreamAppenderConsoleAppenderFileAppenderRollingFileAppenderFileNamePattern 三、总结 一、前言 前一篇文章介绍了appender、conversionRule、root和logger节点的解析, 为的是为本篇详细介绍它们的…

Spring 项目接入 DeepSeek,分享两种超简单的方式!

⭐自荐一个非常不错的开源 Java 面试指南&#xff1a;JavaGuide &#xff08;Github 收获148k Star&#xff09;。这是我在大三开始准备秋招面试的时候创建的&#xff0c;目前已经持续维护 6 年多了&#xff0c;累计提交了 5600 commit &#xff0c;共有 550 多位贡献者共同参与…

ubuntu和windows编译godot

官方文档-编译教程 https://docs.godotengine.org/zh-cn/4.x/contributing/development/compiling/compiling_for_windows.html源码地址 本示例使用4.3-stable版本 https://github.com/godotengine/godot/archive/refs/heads/master.zippython; nasm; mingw下载安装包地址 h…

DeepSeek本地化部署【window下安装】【linux下安装】

一、window 本地安装指导 1.1、下载window安装包 https://ollama.com/download/OllamaSetup.exe 1.2、点击下载好的安装包进行安装 检测安装是否成功&#xff1a; C:\Users\admin>ollama -v ollama version is 0.5.7有上面的输出&#xff0c;则证明已经安装成功。 配置…

一文深入了解DeepSeek-R1:模型架构

本文深入探讨了 DeepSeek-R1 模型架构。让我们从输入到输出追踪 DeepSeek-R1 模型&#xff0c;以找到架构中的新发展和关键部分。DeepSeek-R1 基于 DeepSeek-V3-Base 模型架构。本文旨在涵盖其设计的所有重要方面。 &#x1f4dd; 1. 输入上下文长度 DeepSeek-R1的输入上下文长…

qt的QMainWindow保存窗口和恢复窗口状态

保存窗口状态 QSettings settings("MyCompany", "MyApp"); // 指定存储的应用信息 settings.setValue("mainWindowState", saveState());saveState() 返回一个 QByteArray&#xff0c;包含 所有停靠窗口和工具栏的状态。QSettings 用于存储数据…

双ESP8266-01S通讯UDP配置

第一台ESP8266(发送命令需要勾---发送新行) ATCWMODE3 ATCWSAP_DEF"CAR_wifi_Master","12345678",5,3 //设置本地wifi名称以及密码 ATCIPSTA_DEF"192.168.4.1" //设置本地IP ATCIFSR …