Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

系列文章目录

C++技能系列
Linux通信架构系列
C++高性能优化编程系列
深入理解软件架构设计系列
高级C++并发线程编程

期待你的关注哦!!!
在这里插入图片描述

现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。
Now everything is for the future of dream weaving wings, let the dream fly in reality.

Linux终端与进程的关系

  • 系列文章目录
  • 一、Linux 进程关系
    • 1.1 Linux 进程相关概念「进程关系、进程组、进程会话」
  • 二、终端与进程的关系
    • 2.1 终端与bash进程
    • 2.2 终端上的开启进程
    • 2.3 为什么终端一退,进程就退出了
    • 2.4 strace 工具的使用
    • 2.5 终端关闭时如何让进程不退出
      • 2.5.1 nginx进程拦截SIGHUP信号
      • 2.5.2 nginx进程和bash进程在不同的session中(不同的session ID)
      • 2.5.3 直接使用setsid命令
      • 2.5.4 直接使用 nohup命令(no hang up 不挂断)
    • 2.6 后台执行(运行)的简单理解

一、Linux 进程关系

1.1 Linux 进程相关概念「进程关系、进程组、进程会话」

进程之间是一种属性的结构关系,如图:

在这里插入图片描述

图1.1 进程关系图

最上面的init进程是“老祖宗”进程(操作系统启动时内核自己创建出来的,具有超级用户特权)。init进程的进程ID(PID)是1,系统内的其他进程应该都是由它或者它的子进程fork(创建)出来的。

终端输入ps -ef命令查看结果:
在这里插入图片描述

图1.2 PID为1的进程就是init进程

进程关系进一步分析:

进程有进程ID(PID),有父进程ID(PPID)。还要知道一点:每个进程还可以属于一个进程组(分成组方便管理,例如可以给整个组发送一条信息等)。 “进程组”代表一个或多个进程的集合 。每个进程组有一个唯一的进程组ID,一个进程组中的各个进程可以独立接收来自终端的各种信号。可以通过调用系统函数创建进程组、加入进程组等。

了解进程组概念之后,我们引入一个新的概念: “会话”(Sessiond)是一个或多个进程组的集合。

在这里插入图片描述

图1.3 一个session包含一个或多个进程组,一个进程组包含一个或多个进程

一般来讲,如果不调用特殊的系统函数进行特殊处理,1个bash(shell)上运行的所有程序将属于一个会话(session),而这个会话有1个会话首进程(session leader,就是创建该会话的进程),这个shell(bash)通常就是session leader。另外,还可以调用函数创建新的session。

二、终端与进程的关系

2.1 终端与bash进程

启动Ubuntu Linux虚拟机,用Xshell链接虚拟机,输入如下命令:
ps -ef | grep bash

进程ID为 19501就是bash进程(既Xshell 链接到Ubuntu Linux虚拟机产生的进程),如图2.1所示:
在这里插入图片描述

图2.1.1 查找包含bash字样的进程(1个)

再建一个Xshell链接虚拟机会话窗口,输入**ps -ef | grep bash**命令,如图2.12所示:
在这里插入图片描述

图2.1.2 查找包含bash字样的进程(2个)

可以看出,此时有2个bash进程(因为有两个Xshell连入了Ubuntu Linux虚拟机),进程ID分别为 19501和3253,我们可以看到图中pts/0和pts/1,pts就是虚拟终端

每个虚拟机终端链接到Ubuntu Linux虚拟机上,都会打开一个bash进程(也叫shell - 壳)。向其中输入各种命令的黑窗口就是bash,用于解释用户输入的命令。在Xshell中输入一条命令并按回车键后,命令就会被bash进程解释并执行(bash就是shell,也就是命令行解释器是一个可执行程序)。

可以使用whereis命令查找bash可执行程序的位置,如图2.13所示:
whereis bash
在这里插入图片描述

图2.1.3 使用whereis命令查找bash可执行程序的位置

2.2 终端上的开启进程

在VSCode自己编写nginx.c代码,内容如下:

#include <stdio.h>
#include <unistd.h>
int main(int argc, char *const *argv)
{
        printf("hello nginx\n");
        for(;;)
        {
                sleep(1);
                printf("休息1s\n");
        }
        printf("程序退出!再见!\n");
}

运行./nginx, 并在另一个终端窗口输入:
ps -la
-l代表比较长的输出显示格式;-a代表显示终端上的所有进程(包括其他终端的进程)

在这里插入图片描述

图2.2.1 执行ps -la的结果
这是一个无限循环的程序,会不断重复输出“休息1秒”,可以按`Ctrl + C`组合键停止。

2.3 为什么终端一退,进程就退出了

现在,把自己写的这个nginx可执行程序在某个终端再次启动,然后再打开两个Xshell终端,保持3个终端在运行的状态。

在终端输入如下命令:
ps -eo pid,ppid,sid,tty,pgrp,comm | grep -E 'bash|PID|nginx'

e代表所有进程;o代表自己指定显示哪些列(sid表示session id, tty表示终端, pgrp表示进程组, comm表示执行的命令);grep命令中,-E代表要开启扩展正则表达式功能,用于配合后面的bash|PID|nginx以显示特定名字的进程;| 代表或者关系;bash|PID|nginx代表bash、PID、nginx这几个字符串中的某个出现就会被显示。
在这里插入图片描述

图2.3.1 用ps命令显示指定名字的进程

观察图2.3.1:

(1)nginx进程的PPID是3253,而3253是某个bash的PID,说明nginx是某个bash的子进程。

(2)nginx进程的SID是3253,而3253是这个bash的PID,同时还是这个bash的SID,也就是说这个bash的SID等于PID,这就意味着这个bash是整个会话(session)的会话首进程(session leader)。

nginx进程的PID和PGRP都是14942,说明nginx自成一组。

(3)PID=3253的bash进程和nginx进程,它们的TT(tty)都是pts/0(编号为0的终端)。

(4)bash进程收到SIGHUP信号后,就会把这个信号发送给session 里面的所有进程,收到这个SIGHUP信号的进程默认动作就是退出。

分析到这里应该就明白了 - 为什么终端一退,进程就退出了。

2.4 strace 工具的使用

strace是Linux中的调试分析诊断工具,功能强大,可以用于跟踪程序执行时进程的系统调用(system call)和所接收到的信号。现在来看strace工具如何使用。

(1)执行如下命令:sudo strace -e trace=signal -p 23099

这条命令用于跟踪PID为23099的进程(nginx进程)上与信号(signal)有关的系统调用。也就是说,这条命令把trace工具附着到进程23099上了。如图所示:
在这里插入图片描述

图2.4.1 将strace 工具附着到进程23099(nginx)进程上

(2)现在打开第三个终端窗口,再使用strace附着一下nginx进程PPID为3253的父进程(bash)
sudo strace -e trace=signal -p 3253
在这里插入图片描述

图2.4.2 将strace 工具附着到3253进程(bash)上

(3)现在关闭运行nginx的进程窗口会发现另外2个运行strace的窗口都有内容输出。

首先看下附着nginx进程的终端窗口的输出内容:
在这里插入图片描述

图2.4.3 附着到nginx进程上的strace工具有了输出

从图2.4.3中可以看出,信号是SIGHUP,sid_pid表示谁发来的信号,3253就是bash进程(nginx进程的父进程)。

接着看下附着bash进程的终端窗口的输出内容:
在这里插入图片描述

图2.4.4 附着到bash进程上的strace工具有了输出

图2.4.4所示信息有些复杂,尝试找一找有用的信息(kill在这里表示发送信号的意思)。

(1)开头为"kill(-23099, SIGHUP)"的这行,nginx进程的进程组(PGRP)ID就是23099,所以这行表示发送信号给这个数字的绝对值所在的进程组(kill后面如果有负值,一般代表发送信号给一个进程组),也就是说,进程组ID为23099的所有进程都会收到SIGHUP的信号。

(2)kill(3253, SIGHUP)的这行,3253是bash进程自己的PID,bash在第一次收到SIGHUP信号时先把该信号发给session内的其他进程(不仅是上面的23099进程组的进程,如果有其他进程组,其他进程组也会收到SIGHUP信号(只要这些进程的session ID 相同),然后再次发送SIGHUP命令给自己,将自己杀死。下一行的“sid_pid=3253”也就说明了杀死了自己

2.5 终端关闭时如何让进程不退出

当终端关闭时,终端上运行的nginx进程就退出了。现在要解决的问题是,终端关闭时,如何让nginx进程不退出。设想一下,如果nginx进程拦截SIGHUP信号,是否可以?

2.5.1 nginx进程拦截SIGHUP信号

修改一下nginx.c。信号相关内容后面讲,这里写一小段代码,把该信号忽略掉。收到SIGHUP信号后,操作系统默认结束nginx进程,现在通过修改代码,告诉操作系统忽略掉该信号,操作系统就不会执行默认处理行为(不会结束该进程)。

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int main(int argc, char *const *argv)
{
        printf("hello nginx\n");
        //系统函数,设置收到某个信号时的处理程序(用哪个函数来处理)
		signal(SIGHUP, SIG_IGN); //SIG_ING:代表我要求忽略该信号,请求操作系统不要执行默认的
							     //该信号处理动作(不要把本进程杀掉)。
        for(;;)
        {
                sleep(1);
                printf("休息1s\n");
        }
        printf("程序退出!再见!\n");
}

此时编译并执行,如图所示:
在这里插入图片描述

图2.5.1.1 strace工具输出前状态

关闭了nginc进程所在的bash父进程后,我们发现6408 bash进程没了,但是nginx进程依旧在,如图所示:

在这里插入图片描述

图2.5.1.2 关闭了nginc进程所在的bash父进程后strace工具输出后状态

nginx依旧存在,不过它的TT(终端)列显示变成了“?”,这表示没有对应的终端了,而且它的PPID(父进程ID)变成了1,正是前面介绍的老祖宗进程init的PID(进程ID),本来nginx进程的父进程是bash,但是bash被结束了,nginx进程变成孤儿进程,这种孤儿进程就被init进程收养了。

2.5.2 nginx进程和bash进程在不同的session中(不同的session ID)

这就涉及调用系统函数setid了。该函数用于建立一个新会话。修改nginx.c代码如下:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int main(int argc, char *const *argv)
{
        printf("hello nginx\n");
        pid_t pid;
		
		pid = fork();//系统函数,用来创建新进程,后续会继续讲解,子进程会从这句fork调用之后开始
					 //执行,原来的父进程也会接着往下执行
					 //为什么调用fork()函数,而不直接创建会话?是因为主进程是进程组的组长,不允许调用setsid来建立会话
		printf("pid = %d \n", pid);
		if(pid < 0){
			printf("fork进程出错!\n");
		}else if(pid == 0){
			//若执行的是子进程,则该条件就会满足
			printf("子进程开始执行!\n");
			//创建新的session
			setsid();
			for(;;)
        	{
                sleep(1);
                printf("休息1s\n");
        	}
			return 0;
		}else{
			//父进程才会走到这里
			for(;;)
        	{
                sleep(1);
                printf("休息1s\n");
       		}
       		return 0;
		}
        printf("程序退出!再见!\n");
        return 0;
}

这里是调用fork创建了一个子进程(子进程创建成功后回和父进程并行运行),然后在子进程中调用setsid。

此时,子一个终端窗口编译、链接并运行nginx,如图,可以看到父进程和子进程都在运行之中。
在这里插入图片描述

图2.5.2.1 重新编译链接nginx.c并执行,此时父进程和子进程并行执行

可以清楚地看到,PID为16658的nginx进程的父进程ID(PID)是16657,但是16658和16657两个进程的SID并不相同。还可以发现调用fork函数创建出的子进程没有关联的终端(TT/TTY列显示为“?”)。

现在,把正显示nginx执行结果终端断开连接,再看下信息:
在这里插入图片描述

图2.5.2.2 关闭了nginc进程所在的bash父进程后strace工具输出后状态

可以发现PID15806的bash没有了,PID为16657的父进程也没了,但PID为166658的子进程还在,子进程变成了孤儿进程,被老祖宗init收养了,所以子进程的PPID变成了1。

2.5.3 直接使用setsid命令

setsid不但是个函数(可以在代码中使用),也是个命令(可以在命令行中使用)。该命令用于启动一个进程,而且能够使启动的进程在一个新的session中,这样终端关闭后进程就不会退出。

现在,我们"kill 166658" , 把剩余的nginx杀死,并把代码复原:

#include <stdio.h>
#include <unistd.h>
int main(int argc, char *const *argv)
{
        printf("hello nginx\n");
        for(;;)
        {
                sleep(1);
                printf("休息1s\n");
        }
        printf("程序退出!再见!\n");
        
}

再次编译gcc -o nginx nginx.c
在一个终端窗口执行命令:setsid ./nginx
在另一个终端窗口执行命令:ps -ef | grep -E "nginx|PID|bash"

在这里插入图片描述

图2.5.3.1 运行setsid ./nginx 命令后的执行情况

然后我们关闭nginx终端,依旧存在nginx进程:
在这里插入图片描述

图2.5.3.2 关闭nginx终端,显示终端进程的执行情况

可以看出,关闭后依然存在。

2.5.4 直接使用 nohup命令(no hang up 不挂断)

是否觉得这个命令似曾相识?前面介绍了一个终端断开信号(SIGHUP),看起来与这个命令很相似,都是hup。是的,使用nohup命令启动的程序会忽略(SIGHUP信号),与用程序编写忽略SIGHUP信号同理。

nohup命令有点与众不同,执行程序,我们看看:
nohup ./nginx

结果如图:
在这里插入图片描述

图2.5.4.1 使用nohup命令启动一个可执行程序

可以发现,本来屏幕上输出的内容,现在不输出了,光标停在屏幕上。这是nohup命令的特点,该命令会把输出重定向到当前目录的nohup.out文件中,程序需要执行10~20s才能发现nohup的尺寸有变化(变得大于0了)。所以,这种nohup文件的输出程序执行结果并不是实时输出,而是有一定的延迟。

看下当前目录结构:
在这里插入图片描述

图2.5.4.2 查看nohup命令的输出文件nohup.out的大小

查看当前的进程信息:
在这里插入图片描述

图2.5.4.3 查看当前的nginx进程信息

关闭nginx的终端后,查看当前的进程信息:
在这里插入图片描述

图2.5.4.4 关闭nginx的终端后,查看当前的进程信息

nginx依然存在,PPID变成了1,子进程变成了孤儿进程,被老祖宗init收养了。

2.6 后台执行(运行)的简单理解

后台执行(运行)的命令:
./nginx &

在这里插入图片描述

图2.6.1 使用"&"让程序在后台执行

此时,程序在后台执行了(直接输入./nginx执行方式可理解为在前台运行)。

那么,程序在后台运行和在前台运行有什么区别呢?

(1)如果程序在前台执行,在终端输入ls命令,是无法列出目录的;程序在前台执行终端只只能等待该程序指向完成才能继续执行其他程序。
(2)如果程序在前台执行,则按Ctl + C 组合键能够停止该进程的执行;如果程序在后台执行,按Ctl + C 组合键则无法停止该进程的执行。
(3)可以把前台执行的进程切换到前台,使用fg命令即可进行切换:
fg
nginx进程被切换到了前台,这时按Ctl + C组合键就可以停止该进程了。
(4)如果把终端连接断开,就会发现,就算nignx是后台进程,它也被关闭(退出)了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C高级重点

1、请简要描述一下Linux文件系统的层级结构&#xff0c;包括不同目录的作用和功能。 Linux的文件系统结构是一个倒插树结构&#xff0c;所有的文件都从根目录出发。 2、find指令的用途 find 查找的路径 -name 文件名 ----->在指定路径下&#xff0c;以文件名为条件查找文…

总结vue3 的一些知识点:​Vue3 起步

目录 引言 Vue3 混入 实例 选项合并 实例 实例 全局混入 实例 Vue3 起步 Vue 3.0 实例 data 选项 实例 方法 总结 引言 Vue 进阶系列教程将在本号持续发布&#xff0c;一起查漏补缺学个痛快&#xff01;若您有遇到其它相关问题&#xff0c;非常欢迎在评论中留言讨…

Ubuntu 20.04.02 LTS安装virtualbox7.0

ubuntu22.04的软件仓库也有virtualbox&#xff0c;不过版本较老。 使用安装命令&#xff1a;sudo apt install virtualbox 如果想要安装最新版&#xff0c;那么需要去官网下载deb包或者使用官方的仓库。 这里采用安装Oracle官方仓库的方法。 执行如下命令&#xff1a; wge…

HTTP调用:你考虑到超时、重试、并发了吗?

今天&#xff0c;我们一起聊聊进行 HTTP 调用需要注意的超时、重试、并发等问题。 与执行本地方法不同&#xff0c;进行 HTTP 调用本质上是通过 HTTP 协议进行一次网络请求。网络请求必然有超时的可能性&#xff0c;因此我们必须考虑到这三点&#xff1a; 首先&#xff0c;框架…

抖音本地生活团购服务商

抖音本地生活团购服务商市场前景非常广阔。随着移动互联网的普及和人们对本地生活服务需求的增加&#xff0c;本地生活团购行业已成为一个快速增长的市场。而抖音平台拥有庞大的用户基础和强大的社交媒体传播力&#xff0c;为本地生活团购服务商提供了巨大的发展机遇。 抖音…

【博客674】警惕Prometheus 中的重复样本和无序时间戳错误

警惕Prometheus 中的重复样本和无序时间戳错误 1、场景 您的 Prometheus 服务器日志中是否遇到过以下错误&#xff1f; "Error on ingesting out-of-order samples" "Error on ingesting samples with different value but same timestamp" "dupli…

图解CNN中的卷积(卷积运算、池化、Padding、多通道的卷积)

文章目录 卷积操作池化Padding对多通道&#xff08;channels&#xff09;图片的卷积套上激活函数是什么样的参考&#xff1a; 卷积层是深度学习神经网络中经常使用的一种层。它通过卷积运算来提取输入的特征&#xff0c;常用于图像、语音等信号处理任务中。 卷积层有以下几个参…

探索iOS之Metal编程指南

iOS推出Metal渲染库为了取代OpenGL。Metal有自己的Shader语言&#xff0c;渲染效率比OpenGL高。在这里我们一起探索&#xff1a;Metal使用C限制、预处理定义、动态链接配置、GPU编译配置、设备坐标系、视口坐标系、纹理坐标系、矢量类型、矩阵类型、采样器状态、矩阵相乘。 1、…

第 107 场LeetCode双周赛

A 最大字符串配对数目 显然各字符串对 间匹配的先后顺序不影响最大匹配数目, 可以从后往前遍历数组, 判断前面是否有和当前末尾构成匹配的. class Solution { public:int maximumNumberOfStringPairs(vector<string> &words) {int res 0; while (words.size…

使用 Jetpack Compose 构建 RadioButton

欢迎阅读本篇关于使用 Jetpack Compose 构建 RadioButton&#xff08;单选按钮&#xff09;的博客。Jetpack Compose 是 Google 发布的现代化 UI 工具包&#xff0c;用于构建 Android 界面。它的声明式设计使得 UI 开发更加简洁直观。 一、什么是 RadioButton&#xff1f; Rad…

【深度学习】3-4 神经网络的学习- 学习算法的实现

神经网络的学习步骤如下所示&#xff1a; 步骤1(mini-batch) 从训练数据中随机选出一部分数据&#xff0c;目标是减小mini-batch的损失函数的值 步骤2(计算梯度) 为了减小mini-batch的损失函数的值&#xff0c;需要求出各个权重参数的梯度 步骤3(更新参数) 将权重参数沿梯度…

ModaHub魔搭社区:向量数据库MIlvus服务端配置(四)

目录 常见问题 常见问题 除了配置文件外&#xff0c;怎样可以判断我确实在使用 GPU 做搜索&#xff1f; 有以下三种方式&#xff1a; 使用 nvidia-smi 命令查看 GPU 使用情况。用 Prometheus 配置&#xff0c;详见 使用 Grafana 展示监控指标 > 系统运行指标。使用 Milv…

【完美复现】面向配电网韧性提升的移动储能预布局与动态调度策略【IEEE33节点】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

制造企业实施MES系统受到的影响因素有哪些?

实施MES系统会遇到哪些影响因素&#xff1f;或者说企业实施MES系统的交付率为什么低&#xff1f; 我觉得关键点在于&#xff1a;在当前MES产品化程度普遍不高的大环境下&#xff0c;对项目及管理软件本身认知过于简单&#xff0c;且缺失有经验行业人才&#xff0c;是当前大部分…

windows下安装Visual Studio + CMake+OpenCV + OpenCV contrib+TensorRT

目录 1 安装visual studio 2 安装CMake 3 OpenCV源码安装 3.1 OpenCV源码下载 3.2 OpenCV contrib源码下载 3.3 安装OpenCV 3.4 安装OpenCV-crontrib 3.5 VS生成代码 4 环境配置 5 TensorRT安装 5.1 TensorRT安装 5.2 Python下安装TensorRT库 最近在研究windows系统…

Unity渲染工程收集

NPR 非真实渲染 UnityURP-AnimeStyleCelShader SSR 屏幕空间反射 UnitySSReflectionURP

分布式机器学习(Parameter Server)

分布式机器学习中&#xff0c;参数服务器(Parameter Server)用于管理和共享模型参数&#xff0c;其基本思想是将模型参数存储在一个或多个中央服务器上&#xff0c;并通过网络将这些参数共享给参与训练的各个计算节点。每个计算节点可以从参数服务器中获取当前模型参数&#xf…

架构基本概念和架构本质

什么是架构和架构本质 在软件行业&#xff0c;对于什么是架构&#xff0c;都有很多的争论&#xff0c;每个人都有自己的理解。此君说的架构和彼君理解的架构未必是一回事。因此我们在讨论架构之前&#xff0c;我们先讨论架构的概念定义&#xff0c;概念是人认识这个世界的基础…

UWB超宽带定位技术的原理及定位方法

uwb定位技术即超宽带技术&#xff0c;它是一种无载波通信技术&#xff0c;利用纳秒级的非正弦波窄脉冲传输数据&#xff0c;因此其所占的频谱范围很宽。传统的定位技术是根据信号强弱来判别物体位置&#xff0c;信号强弱受外界 影响较大&#xff0c;因此定位出的物体位置与实际…

Redis入门(4)-list

redis中list数据会按照插入顺序进行排序&#xff0c;其底层是一个无头结点的双向链表&#xff0c;因此表头和表尾的操作性能较高&#xff0c;但中间元素操作性能较差。 1.lpush key element [element ] 从表头插入元素 lpush nosql redis hbase lpush nosql mongdb2.lrange…