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

这篇具有很好参考价值的文章主要介绍了Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

系列文章目录

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

期待你的关注哦!!!
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

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

一、Linux 进程关系

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

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

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

图1.1 进程关系图

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

终端输入ps -ef命令查看结果:
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

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

进程关系进一步分析:

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

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

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

图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所示:
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

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

再建一个Xshell链接虚拟机会话窗口,输入**ps -ef | grep bash**命令,如图2.12所示:
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

图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
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

图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代表显示终端上的所有进程(包括其他终端的进程)

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

图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这几个字符串中的某个出现就会被显示。
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

图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上了。如图所示:
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

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

(2)现在打开第三个终端窗口,再使用strace附着一下nginx进程PPID为3253的父进程(bash)
sudo strace -e trace=signal -p 3253
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

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

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

首先看下附着nginx进程的终端窗口的输出内容:
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

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

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

接着看下附着bash进程的终端窗口的输出内容:
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

图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");
}

此时编译并执行,如图所示:
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

图2.5.1.1 strace工具输出前状态

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

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

图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,如图,可以看到父进程和子进程都在运行之中。
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

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

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

现在,把正显示nginx执行结果终端断开连接,再看下信息:
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

图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"

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

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

然后我们关闭nginx终端,依旧存在nginx进程:
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

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

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

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

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

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

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

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

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

看下当前目录结构:
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

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

查看当前的进程信息:
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

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

关闭nginx的终端后,查看当前的进程信息:
Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】

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

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

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

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

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

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

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

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

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

到了这里,关于Linux终端与进程的关系 ( 1 ) -【Linux通信架构系列】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux信号概念、认识、处理动作 ( 2 ) -【Linux通信架构系列 】

    点击进入系列文章总目录 C++技能系列 Linux通信架构系列 C++高性能优化编程系列 深入理解软件架构设计系列 高级C++并发线程编程 期待你的关注哦!!! 现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。 Now everything is for the future of dream weaving wings, let the dre

    2024年02月11日
    浏览(43)
  • 【Linux】守护进程(附终端、进程组、会话的介绍)

    橙色 1、终端 可以查看当前终端进程的id 默认情况下(没有重定向),每个进程的标准输入、标准输出和标准错误输出都指向控制终端、进程从标准输入读也就是读用户的键盘输入,进程往标准输出或标准错误输出写也就是输出到显示器上。 2、进程组 进程组和会话在进程之间

    2024年02月06日
    浏览(35)
  • 进程组,会话的基础概念,以及进程组,会话,控制终端,前台后台之间的联系(系列文章第二篇)

    这个系列的文章有四篇,其目的是为了搞清楚 : 进程,shell,shell进程,终端,控制终端,前台进程,后台进程,控制进程,前台进程组,后台进程组,会话,守护进程,init进程,用户进程,系统进程 它们之间的联系与区别 系列文章第一篇传送门:全面理解shell进程、终端

    2024年02月04日
    浏览(81)
  • 【Linux】进程的基本属性|父子进程关系

    个人主页:🍝在肯德基吃麻辣烫 我的gitee:Linux仓库 个人专栏:Linux专栏 分享一句喜欢的话:热烈的火焰,冰封在最沉默的火山深处 本篇文章继上文的进程概念后,现在对进程概念有了一定的理解。前面说过,操作系统管理进程实际上是管理描述进程的 PCB对象 ,而PCB对象是

    2024年02月12日
    浏览(40)
  • 【多线程系列-01】深入理解进程、线程和CPU之间的关系

    多线程系列整体栏目 内容 链接地址 【一】深入理解进程、线程和CPU之间的关系 https://blog.csdn.net/zhenghuishengq/article/details/131714191 【二】java创建线程的方式到底有几种?(详解) https://blog.csdn.net/zhenghuishengq/article/details/127968166 【三】深入理解java中线程的生命周期,任务调度 ht

    2024年02月16日
    浏览(59)
  • 【Linux之进程间通信】04.Linux进程间的信号通信

      【Linux之进程间通信】 项目代码获取: https://gitee.com/chenshao777/linux-processes.git (麻烦点个免费的Star哦,您的Star就是我的写作动力!) 04.Linux进程间的信号通信 【目录】   一、进程间信号发送方式   二、进程间信号接收方式   三、进程间信号处理方式 一、进程间信号发

    2024年02月06日
    浏览(40)
  • 【Linux】进程与可执行程序的关系&&fork创建子进程&&写实拷贝的理解

            系统会将此时在系统运行的进程的各种属性都以文件的形式给你保存在系统的proc目录下。 运行一个程序的时候,本质就是把磁盘中的程序拷贝到内存中,当一个进程运行起来的时候,它本质已经和磁盘中的可执行程序没有直接关系了。   当前我的myprocess程序正在运

    2024年03月19日
    浏览(35)
  • 【Linux】进程通信之管道通信详解

    🍎 作者: 阿润菜菜 📖 专栏: Linux系统编程 其实管道通信是Unix中最古老的进程间通信的形式了: 管道通信是一种进程间通信的方式,它可以让一个进程的输出作为另一个进程的输入,实现数据的传输、资源的共享、事件的通知和进程的控制。 管道通信分为两种类型:匿名

    2023年04月19日
    浏览(39)
  • 【Linux】进程通信之匿名管道通信

    我们往往需要多个进程协同,共同完成一些事情。 数据传输:一个进程需要将它的数据发送给另一个进程 资源共享:多个进程之间共享同样的资源。 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止 时要通知父进程)。

    2024年04月14日
    浏览(44)
  • Linux--进程间的通信--进程池

    进程间的通信–匿名管道 进程池是一种将多个进程组织起来以执行特定任务的机制 。 它由多个预先创建好的资源进程和一个管理进程组成。这些资源进程被管理进程负责分配和调度,用于处理任务 。 当有新的任务提交时,管理进程会从进程池中取出一个资源进程,将任务

    2024年04月25日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包