当前位置: 首页 > news >正文

进程间关系和守护进程

序言

 当我们使用指令 ps 查看进程的相关信息时,在以前我们只是关注该进程的 PID(该进程的标识符)PPID(其父进程的标识符) 以及 STAT(该进程的状态)
 那 PGIDSID 又是什么?有什么作用呢?


1. 进程组

1.1 什么是进程组?

 当我们启动程序执行相应的任务时,我们的任务可能只是创建了一个进程:

 1 #include <iostream>2 #include <unistd.h>3 4 int main()5 {6     while(1)7     {8         std::cout << "I am running, my pid is " << getpid() << std::endl;9         sleep(1);10     }11     return 0;12 }

我们使用指令 ps 查看进程信息:
在这里插入图片描述

在这里的 PGID 就是代表进程组,进程组的 idPID 保持一致。当我们的进程组只包含一个进程时,进程的 ID 等于其进程 ID

 那如果我们的任务包含多个进程呢?举个栗子:
在这里插入图片描述
当一个进程组包含多个进程时,进程组的 ID 和第一个创建的进程的 ID 保持一致。

 总结一下,进程组是一个或者多个进程的集合, 一个进程组可以包含多个进程

1.2 组长进程

 每一个进程组都包含一个组长进程,组长进程的 ID 就是该进程组的 ID。根据上面代码的举例,我们不难得出以下结论:

  • 当一个进程组只有一个进程时,该进程就是组长进程
  • 当一个进程组包含多个进程时,首先创建的进程为组长进程

一个进程组的生每周期取决于最后终止的进程而非是组长进程。


2. 会话

2.1 什么是会话

 会话可以看成是 一个或多个进程组的集合, 一个会话可以包含多个进程组。每一个会话也有一个会话 ID(SID)
 创建一个新的会话时可以简单理解为 创建终端文件和启动 bash 进程

  • 终端:终端是用户与操作系统进行交互的界面。
  • bashbashLinux上 最常用的 Shell 之一,Shell 是运行在终端上的程序,它提供了用户与操作系统交互的接口。

怎么来证明呢?现在我在 XShell 上只是启动一个会话,查看我们的终端文件和 bash 进程:
在这里插入图片描述
在这里插入图片描述

可以看到只存在一个终端文件和 bash 进程,那我们再创建一个回话呢:
在这里插入图片描述在这里插入图片描述
现在就变成了两个终端文件和两个 bash 进程。

 所以在每一次登录时,都会为我们自动建立一次会话。会话的 id 和第一个创建的进程的 id 保持一致,在大多数情况下都是我们的 bash,除非是我们手动创建的会话。

2.2 创建一个会话

 可以调用 setsid 函数来创建一个会话, 前提是 调用进程不能是一个进程组的组长。
 大家都知道可以使用 ctrl + c 来终止当前程序(需要是前台的程序,后面会说)的执行吧,但是该程序必须是当前会话下的程序。那不是废话吗,我在我会话下启动的程序肯定就是啊,难不成跑到别处去了?
 没认识 setsid 之前你的话是对的,但是认识之后就不一定了,举个栗子:

   1 #include <iostream>2 #include <unistd.h>3 4 int main()5 {6     // child7     if(fork() ==  0)8     {// 创建新的会话9         setsid();10         while(1)11         {12             std::cout << "I am child process, my pid is " << getpid() << std::endl;13             sleep(2);14         }15     }16     // parent17     else18     {19         while(1)20         {21             std::cout << "I am parent process, my pid is " << getpid() << std::endl;22             sleep(2);23          }24     }25 26     return 0;27 }

现在我们运行这段程序:
在这里插入图片描述
程序正常运行,但是终止进程后子进程依然执行,这是因为子进程属于其他会话,不归当前会话管。那除了重启大法没办法终止他了吗?肯定不是,我们还有 kill 指令。


3. 前后台任务

3.1 前台任务

 前台任务会占据终端的输入输出,即它会接收你通过键盘输入的命令或数据,并将它的输出结果直接显示在终端上。前台任务会阻塞终端,直到它完成或者被你明确地放到后台执行。简而言之,前台任务会占有终端文件! 比如:

   1 #include <iostream>2 #include <unistd.h>3 4 int main()5 {6     while(true) sleep(1);7     return 0;8 } 

现在我们运行该程序,并向终端输入指令:
在这里插入图片描述
可以看到并没有任何结果,这是因为我们输入的指令都是被 bash 指令接受之后创建子进程执行的,但是现在终端文件被该进程占有了,自然 bash 收不到了。

3.2 后台任务

 后台任务是指那些在终端之外运行的任务,它们 不会直接占据终端的输入输出。后台任务可以在你执行其他任务或关闭终端时继续运行。要将一个任务放到后台执行,你可以在命令的末尾加上 & 符号。
 还是上一段程序,但是在运行时在最后加上 &:
在这里插入图片描述

可以看到,我们指令的执行并没有受到干扰。

 那我怎么查看我后台任务的执行情况呢,使用指令 jobs [-l]:
在这里插入图片描述

3.3 后台任务切回前台

 只需要使用指令 fg nn 代表该任务的编号:
在这里插入图片描述

3.4 前台任务切回后台

 首先我们需要使用指令 ctrl + z 将该任务暂停,之后使用指令 bg n 将该任务切换到后台:
在这里插入图片描述


4. 守护进程

 我们运行一个普通的进程时,不管是前台还是后台,当我们一退出,会话一结束。我们执行的进程也会随之终止,但是在很多应用场景下,服务是不能停的!不可能程序员一下班,我们的应用就罢工了吧!
 所以有了守护进程,守护进程通常用于 提供需要持续运行的服务,如网络服务(Web服务器、FTP服务器等)、数据库服务等。这些服务在系统运行期间 一直保持运行状态,确保用户可以随时访问。
 那我们如何创建一个守护进程呢,关键是 创建一个新的会话,当我们的会话结束时,该会话不受影响!但是只是靠一个进程是做不到的,因为 调用 setsid 的函数不能是进程组长!包含一个进程的进程组,该进程就是组长!解决方法也很简单,一个进程不行那就创建一个子进程嘛,创建的过程如下:

#pragma once#include <unistd.h>
#include <signal.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>void Daemon(const std::string &newpath = "")
{// 防止一些异常退出信号signal(SIGCHLD, SIG_IGN);// 创建子进程,父进程退出if (fork() > 0)exit(0);// 设置一个新的会话setsid();// 关闭原来的文件描述符int fd = open("/dev/null", O_RDWR);if (fd > 0){dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);close(fd);}// 是否更改工作路径if (!newpath.empty()){chdir(newpath.c_str());}
}

 父进程的作用就是创建一个子进程,之后父进程的生命周期就结束了。子进程创建了一个新的会话,脱离了原来的会话。
 在这里为什么需要关闭原来的文件描述符呢?这是因为现有的文件描述符还指向原来会话的文件,这是不严谨的,因为我们当前已经脱离了原来的会话。在这里没有直接的关闭,因为 考虑到后续场景可能使用到读写操作。所以我们让他指向一个空的文件(类似于空指针)。是否需要切换路径和使用场景相关。


5. 总结

 在这篇文章中,我们介绍了进程的组以及会话的概念,还实现了一下守护进程功能的函数。


http://www.mrgr.cn/news/28183.html

相关文章:

  • Web APP的几种类型
  • 【Python模拟websocket登陆-拆包封包】
  • Go-RPC框架分层设计
  • 力扣 LeetCode 225. 用队列实现栈(Day5:栈与队列)
  • 跟李笑来学美式俚语(Most Common American Idioms): Part 02
  • java 泛型中的 ?
  • AG32 MCU的引脚特点及功耗说明
  • AIP接口调用
  • 8. Transforms的使用(三)-- Resize
  • 【C++】学完c语言后的c++基础知识补充!(命名空间、输入和输出、缺省函数、函数重载、引用、内联函数代替宏、nullptr代替NULL)
  • keil5进行stm32编程时常遇到的问题和ST-LINK在线仿真的连接问题
  • 哈哈哈,让 GitHub Copilot 用上 deepseek,性能不输 GPT-4,每月立即省 10 刀!
  • 为什么全双工不需要冲突检测
  • 1184. 公交站间的距离(24.9.16)
  • 初始爬虫7
  • 横向移动-WMI
  • MutationObserver详解+案例——深入理解 JavaScript 中的 MutationObserver:原理与实战案例
  • AtCoder Beginner Contest 371
  • ubuntu20.04编译mesa
  • 大数据新视界 --大数据大厂之数据科学项目实战:从问题定义到结果呈现的完整流程
  • stack - queue
  • linux-系统备份与恢复-系统恢复
  • JVM源码解析
  • 匿名管道详解
  • 最强神器Typora 2024(亲测有效)| Markdown 工具推荐
  • 计算机二级MySQL大题系列01-PHP必考题