【在Linux世界中追寻伟大的One Piece】进程间关系与守护进程

news2024/9/21 23:19:21

目录

1 -> 进程组

1.1 -> 什么是进程组

1.2 -> 组长进程

2 -> 会话

2.1 -> 什么是会话

2.2 -> 如何创建会话

2.3 -> 会话ID(SID)

3 -> 控制终端

4 -> 作业控制

4.1 -> 什么是作业(job)和作业控制(Job Control)

4.2 -> 作业号

4.3 -> 作业状态

4.4 -> 作业的挂起与切回

4.4.1 -> 作业挂起

4.4.2 -> 作业切回

4.5 -> 查看后台执行或挂起的作业

4.6 -> 作业控制相关的信号

5 -> 守护进程

6 -> 如何将服务守护进程化


1 -> 进程组

1.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 选项以逗号操作符(,)作为定界符,可以指定要输出的列

1.2 -> 组长进程

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

[node@localhost code]$ ps -o pid,pgid,ppid,comm | cat
# 输出结果
PID
PGID
PPID COMMAND
2806
2806
2805 bash
2880
2880
2806 ps
2881
2880
2806 cat

从结果上看ps进程的PID和PGID相同, 那也就是说明ps进程是该进程组的组长进程, 该进程组包括ps和cat两个进程。

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

2 -> 会话

2.1 -> 什么是会话

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

通常我们都是使用管道将几个进程编成一个进程组。 如上图的进程组2和进程组3可能是由下列命令形成的:

[node@localhost code]$ proc2 | proc3 &
[node@localhost code]$ proc4 | proc5 | proc6 &
# &表示将进程组放在后台执行

举一个例子观察一下这个现象:

# 用管道和 sleep 组成一个进程组放在后台运行
[node@localhost code]$ sleep 100 | sleep 200 | sleep 300 &
# 查看 ps 命令打出来的列描述信息
[node@localhost code]$ ps axj | head -n1
# 过滤 sleep 相关的进程信息
[node@localhost code]$ ps axj | grep sleep | grep -v grep
# a 选项表示不仅列当前⽤户的进程,也列出所有其他⽤户的进程
# x 选项表示不仅列有控制终端的进程,也列出所有⽆控制终端的进程
# j 选项表示列出与作业控制相关的信息, 作业控制后续会讲
# grep 的-v 选项表示反向过滤, 即不过滤带有 grep 字段相关的进程
# 结果如下
PPID
PID
PGID
SID TTY
TPGID STAT
UID
TIME
COMMAND
2806
4223
4223
2780 pts/2
4229 S
1000
0:00 sleep
100
2806
4224
4223
2780 pts/2
4229 S
1000
0:00 sleep
200
2806
4225
4223
2780 pts/2
4229 S
1000
0:00 sleep
300

从上述结果来看3个进程对应的PGID相同, 即属于同一个进程组。

2.2 -> 如何创建会话

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

#define _CRT_SECURE_NO_WARNINGS 1

#include <unistd.h>

/*
*功能:创建会话
*返回值:创建成功返回 SID, 失败返回-1
*/

pid_t setsid(void);

该接口调用之后会发生:

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

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

2.3 -> 会话ID(SID)

上边我们提到了会话ID, 那么会话ID是什么呢? 我们可以先说一下会话首进程, 会话首进程是具有唯一进程ID的单个进程, 那么我们可以将会话首进程的进程ID当做是会话ID。注意:会话ID在有些地方也被称为 会话首进程的进程组ID, 因为会话首进程总是一个进程组的组长进程, 所以两者是等价的。

3 -> 控制终端

在UNIX系统中,用户通过终端登录系统后得到一个Shell进程,这个终端成为Shell进程的控制终端。控制终端是保存在PCB中的信息,我们知道fork进程会复制PCB中的信息,因此由Shell进程启动的其它进程的控制终端也是这个终端。默认情况下没有重定向,每个进程的标准输入、标准输出和标准错误都指向控制终端,进程从标准输入读也就是读用户的键盘输入,进程往标准输出或标准错误输出写也就是输出到显示器上。另外会话、进程组以及控制终端还有一些其他的关系,我们在下边详细介绍一下:

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

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

4 -> 作业控制

4.1 -> 什么是作业(job)和作业控制(Job Control)

作业是针对用户来讲,用户完成某项任务而启动的进程,一个作业既可以只包含一个进程,也可以包含多个进程,进程之间互相协作完成任务, 通常是一个进程管道。

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

例如下列命令就是一个作业,它包括两个命令,在执⾏时Shell将在前台启动由两个进程组成的作业。

[node@localhost code]$ cat /etc/filesystems | head -n 5

运行结果如下:

xfs
ext4
ext3
ext2
nodev proc

4.2 -> 作业号

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

例如下面的命令在后台启动了一个作业, 该作业由两个进程组成, 两个进程都在后台运⾏:

[node@localhost code]$ cat /etc/filesystems | grep ext &

运行结果如下:

[1] 2202
ext4
ext3
ext2
# 按下回车
[1]+ 完成
cat /etc/filesystems | grep --
color=auto ext
  • 第一⾏表示作业号和进程ID, 可以看到作业号是1, 进程ID是2202。
  • 第3-4⾏表示该程序运⾏的结果, 过滤/etc/filesystems有关ext的内容。
  • 第6行分别表示作业号、默认作业、作业状态以及所执⾏的命令关于默认作业:对于一个用户来说,只能有一个默认作业(+),同时也只能有一个即将成为默认作业的作业(-),当默认作业退出后,该作业会成为默认作业。
    • + : 表示该作业号是默认作业。
    • -:表示该作业即将成为默认作业。
    • 无符号: 表示其他作业。

4.3 -> 作业状态

常见的作业状态如下表:

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

4.4 -> 作业的挂起与切回

4.4.1 -> 作业挂起

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

例如我们运⾏一个死循环的程序, 通过Ctrl+Z将该作业挂起, 观察一下对应的作业状态:

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

int main()
{
	while (1)
	{
		printf("hello\n");
	}

	return 0;
}

下面运⾏这个程序, 通过Ctrl+Z将该作业挂起:

# 运行可执行程序
[node@localhost code]$ ./test
#键入 Ctrl + Z 观察现象

运行结果如下:

# 结果依次对应作业号 默认作业 作业状态 运行程序信息
[1]+ 已停止
./test7

可以发现通过Ctrl+Z将作业挂起, 该作业状态已经变为了停止状态。

4.4.2 -> 作业切回

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

参数含义
%nn为正整数,表示作业号
%string以字符串开头的命令所对应的作业
%?string包含字符串的命令所对应的作业
%+或%%最近提交的一个作业
%-倒数第二个提交的作业

例如我们把刚刚挂起来的./test作业切回到前台:

[node@localhost code]$ fg %%

运⾏结果为开始无限循环打印hello, 可以发现该作业已经切换到前台了。

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

4.5 -> 查看后台执行或挂起的作业

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

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

例如, 我们先在后台及前台运⾏两个作业, 并将前台作业挂起, 来用jobs命令查看作业相关的信息:

# 在后台运行一个作业 sleep
[node@localhost code]$ sleep 300 &


# 运行刚才的死循环可执行程序
[node@localhost code]$ ./test


# 键入 Ctrl + Z 挂起作业
# 使用 jobs 命令查看后台及挂起的作业
[node@localhost code]$ jobs -l

运行结果如下:

# 结果依次对应作业号 默认作业 作业状态 运行程序信息
[1]- 2265 运行中 sleep 300 &
[2]+ 2267 停止 ./test7

4.6 -> 作业控制相关的信号

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

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

终端的I/O(即标准输入和标准输出)和终端产生的信号总是从前台进程组作业连接打破实际终端。可以通过下图看到作业控制的功能:

5 -> 守护进程

#pragma once

#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

const char* root = "/";
const char* dev_null = "/dev/null";

void Daemon(bool ischdir, bool isclose)
{
	// 1. 忽略可能引起程序异常退出的信号
	signal(SIGCHLD, SIG_IGN);
	signal(SIGPIPE, SIG_IGN);

	// 2. 让自己不要成为组长
	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(dev_null, O_RDWR);
		if (fd > 0)
		{
			dup2(fd, 0);
			dup2(fd, 1);
			dup2(fd, 2);
			close(fd);
		}
	}
}

6 -> 如何将服务守护进程化

// ./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;
}

感谢各位大佬支持!!!

互三啦!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2153574.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring:项目中的统一异常处理和自定义异常

介绍异常的处理方式。在项目中&#xff0c;都会进行自定义异常&#xff0c;并且都是需要配合统一结果返回进行使用。 1.背景引入 &#xff08;1&#xff09;背景介绍 为什么要处理异常&#xff1f;如果不处理项目中的异常信息&#xff0c;前端访问我们后端就是显示访问失败的…

20240921在友善之臂的NanoPC-T6开发板上确认宸芯的数传模块CX6602N的AT命令

console:/dev # cat ttyUSB1 & console:/dev # echo AT > ttyUSB1 20240921在友善之臂的NanoPC-T6开发板上确认宸芯的数传模块CX6602N的AT命令 2024/9/21 21:03 【必须】Android12/Linux&#xff08;Buildroot&#xff09;都必须要&#xff01; 4、【Android12默认打开U…

电脑硬件-机械硬盘

简介 机械硬盘是电脑的主要存储媒介之一&#xff0c;通常用于存储一些文件资料或者学习视频笔记等比较大的内容。 结构 采用磁盘存储数据&#xff0c;使用温彻斯特的结构&#xff0c;特有四个特点&#xff1a; 1.磁头、盘片和运动机构安装在一个密封的腔体内。 2.盘片告诉旋…

一图快速看懂flink source的设计实现

文章目录 整体来说多个处理流程是解偶的&#xff0c;这样可以在面对多数据源情况下&#xff0c;能更加的灵活。 下面只展示了&#xff0c;主要的一些流程 下面补充一点&#xff0c;读取文件状态的保存&#xff0c;切分信息用了一个 ListState 来保存。具体要保存的信息&#x…

day2-1 app端文章查看

首先一共三张表 然后大致过程就是三层架构 用mp实现 具体出现的问题 1 测试的时候后端代码启动不了 先在maven clean一下 具体流程 然后执行完之后建议把这三个模块的target文件删除一下再运行 最后的话 如果还是报错 也是正常的 因为后边的东西都没写有些文件没有用到 2…

常见的中间件漏洞

Tomcat CVE-2017-12615 访问主页进行抓包 修改传参方式为put 放包进行连接 后台弱⼝令部署war包 访问主页试用默认账号密码tomcat/tomcat进行登录后 将哥斯拉生成的jsp木马文件压缩城成zip文件&#xff0c;然后再修改zip后缀文war 然后进行上传 使用哥斯拉进行测试连接 CVE-…

基于SpringBoot+Vue的在线酒店预订系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…

微服务——网关登录校验(一)

1.网关登录校验 微服务中的网关登录校验是微服务架构中常见的一种安全机制&#xff0c;用于在请求到达微服务之前&#xff0c;对用户的身份进行验证&#xff0c;确保只有合法的用户才能访问相应的服务。 在微服务架构中&#xff0c;每个微服务都是独立部署的&#xff0c;它们之…

Apipost IDEA插件新升级,Apipost Helper上架IDEA插件市场

大家好&#xff01;今天向大家介绍一个非常方便的IDEA插件——Apipost Helper&#xff01;相信很多使用过Apipost的朋友在开发过程中都希望能够直接将编写好的API同步至Apipost&#xff0c;而无需手动填写。前段时间&#xff0c;Apipost推出了Apipost IDEA插件的内测版&#xf…

macOS平台编译libidn2库给iOS及macOS用

1.克隆源码: git clone https://gitlab.com/libidn/libidn2.git --recursive 2.安装依赖库: pkg-config也要安装 3.启动bootstrap生成configure 配置成功 configure生成成功

概率论与数理统计(2)

第一节博客已经整理了求导的公式&#xff0c;一些常用的概念。链接如下&#xff1a;高等数学基础&#xff08;1&#xff09;-CSDN博客。 第二节博客整理了微积分的公式及其相关概念。链接如下&#xff1a;高等数学基础&#xff08;2&#xff09;——微积分-CSDN博客 第三节博客…

主流卷积神经网络CNN总结

ResNet&#xff08;2015&#xff09;残差神经网络 残差结构 ResNet50具体卷积结构图 ResNeXt&#xff08;2016&#xff09;加入了分组卷积的思想&#xff0c;将原ResNet网络中的block替换成由group分组的block&#xff0c;两者得到的feature map一致&#xff0c;只是参数量更少…

计算机网络(月考一知识点)

文章目录 计算机网络背诵默写版计算机网络知识点&#xff08;月考1版&#xff09; 计算机网络背诵默写版 为我自己留个印记&#xff0c;本来荧光笔画的是没记住的&#xff0c;但是后面用紫色的&#xff0c;结果扫描的时候就看不见了。 计算机网络知识点&#xff08;月考1版&a…

耳夹式耳机值得买吗?揭秘耳夹式耳机六大避坑指南!

耳夹式耳机值得买吗&#xff1f;这是很多人的疑问&#xff0c;但是夹耳式耳机火起来跟当下人们对健康运动的需求密不可分&#xff0c;入耳式耳机照顾了听音需求就很难兼顾环境音&#xff0c;还有耳部健康和佩戴舒适度等等&#xff0c;而运动时半入耳式耳机又容易掉&#xff0c;…

T4—猴痘识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 1.导入数据 #设置gpu from tensorflow import keras from tensorflow.keras import layers,models import os, PIL, pathlib import matplotlib.pyplot as pl…

【刷题—双指针】复写0、三数之和、四数之和

目录 一、复写0二、三数之和三、四数之和 一、复写0 题目&#xff1a; 注意&#xff1a;题目要求是原数组上复写 思路&#xff1a; 一、确定最后一个复写的位置。定义两个变量cur等于0&#xff0c;dest等于-1&#xff0c;让cur去遍历数组。如果cur指向的元素是0&#xff0c;…

[SAP ABAP] 创建数据元素

我们可以使用事务码SE11创建数据元素 输入要创建的数据类型的名称&#xff0c;然后点击创建 选择数据元素并进行确定 输入简短描述并为数据元素分配一个域&#xff0c;会自动带出数据类型以及长度 创建域可参考该篇文章 创建域https://blog.csdn.net/Hudas/article/details/…

Hive企业级调优[6]——HQL语法优化之任务并行度

目录 HQL语法优化之任务并行度 优化说明 Map端并行度 Reduce端并行度 优化案例 HQL语法优化之任务并行度 优化说明 对于分布式计算任务来说&#xff0c;设置一个合理的并行度至关重要。Hive的计算任务依赖于MapReduce框架来完成&#xff0c;因此并行度的调整需要从Map端和…

YOLOv10 简介

YOLOv10&#xff0c;由清华大学的研究人员基于 Ultralytics Python 包构建&#xff0c;引入了一种全新的实时目标检测方法&#xff0c;该方法解决了以往 YOLO 版本中后处理和模型架构方面的不足。通过消除非极大值抑制&#xff08;NMS&#xff09;并优化各种模型组件&#xff0…

C# 技巧在 foreach 循环中巧妙获取索引

目录 前言 使用 LINQ 和扩展方法 直接在 LINQ 查询中使用 使用 LINQ 的 Select() 与 Enumerable.Range() 总结 最后 前言 在C#中foreach 循环是处理集合的常见方式&#xff0c;因其简洁性和易读性而广受青睐。 但是在某些情况下&#xff0c;我们需要同时获取集合中元素的…