Linux(7)——进程(概念篇)

一、基本概念

书本上的概念:程序的一个执行实例,正在执行的程序等

基于内核的观点:担当分配系统资源(CPU时间,内存)的实体。 

我们知道,我们在写代码的时候,你的代码进行编译链接后生成可执行文件,这个文件就在磁盘当中,当我们双击这个文件之后,该文件就被加载到了内存之中,因为只有加载到了内存之中才能被cpu逐语句执行。而加载了内存中的程序,不再是程序而应该是进程。 

二、描述进程——PCB

实际上我们在系统当中有很多的进程,我们可以通过ps aux命令来查看:

我们知道操作系统是第一个被加载到内存的,而操作系统就是做的管理工作的,那么操作系统是怎么做到管理的呢?

这里就用到了之前在谈操作系统时候提到的六个字:先描述,再组织。操作系统作为管理者,是不需要与进程直接交互的,当进程到来时,操作系统需要对进程进行描述,那么对进程的管理就变成了对描述的管理。进程的描述信息会被放到一个叫进程描述块之中,官方称之为PCB(process control block)。

操作系统将每一个进程都进行描述,形成了一个个的进程控制块(PCB),并将这些PCB以双链表的形式组织起来:

这样一来,操作系统只要拿到这个双链表的头指针,便可以访问到所有的PCB。此后,操作系统对各个进程的管理就变成了对这条双链表的一系列操作。
例如创建一个进程实际上就是先将该进程的代码和数据加载到内存,紧接着操作系统对该进程进行描述形成对应的PCB,并将这个PCB插到该双链表当中。而退出一个进程实际上就是先将该进程的PCB从该双链表当中删除,然后操作系统再将内存当中属于该进程的代码和数据进行释放或是置为无效。
总的来说,操作系统对进程的管理实际上就变成了对该双链表的增、删、查、改等操作。 

1.task_struct——PCB的一种

因为Linux是拿C语言写的,那么这里的task_struct其实是一个结构体。

1)在Linux中描述进程的结构体叫做task_struct。
2)task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。

2.task_struct的内容分类

主要内容:

  • 标示符:描述本进程的唯一标示符,用来区别其他进程
  • 状态: 任务状态,退出代码,退出信号等。
  • 优先级:相对于其他进程的优先级。
  • 程序计数器:程序中即将被执行的下一条指令的地址。
  • 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
  • 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
  • 1/0状态信息:包括显示的I/0请求,分配给进程的!/0设备和被进程使用的文件列表。
  • 记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • 其他信息

三、查看进程

1.通过系统目录查看

我们除了上面的,通过ps aux命令查看以外,还可以在根目录下找系统文件夹proc。

我们打开文件夹,可以看到一些以数字命名的目录

这些数字就是我们之前所说的PID,在对应的目录中记录了进程的相关信息,比如我们查看1的文件内容:

2.通过ps命令查看

单独使用ps:

ps aux

ps结合grep可以看到更加标准的进程信息:

四、通过系统调用获取进程的PID和PPID 

这里我们要用到两个调用函数,分别是getpid()和getppid()来分别获取PID和PPID。

我们可以写个代码来测试一下:

输出: 

我们也可以看一看进程的信息是不是和我们调用函数获取的一致

五、通过系统调用创建进程

1.fork函数创建子进程

fork函数是一个系统调用函数,他可以创建一个子进程:

例如:

运行结果如下:

运行结果中的第一行数据是该进程的PID和PPID,第二行是、fork函数创建的PID和PPID,不难发现fork函数创建的进程的PPID就是proc的PID,也就是说proc进程和fork创建的进程是父子关系。

没出现一个进程,操作系统都会为其创建PCB,fork也不例外。

我们知道加载到内存的代码和数据是父进程的,那么fork创建的子进程的代码和数据是哪里来的呢?

我们来写个代码来看看:

运行结果:

实际上,fork函数创建子进程,在fork函数之前的代码要被父进程执行,而fork()之后的代码默认是父子进程都可以执行的。

敲黑板:

1)这里虽然是父子进程代码共享,但是父子进程各自开辟空间(写时拷贝)。

2)这里面可能大家都会有一个疑问,那就是这里父子进程的执行顺序是什么样的,其实这里执行的顺序完全是不确定的,取决于操作系统的调度。

2.使用if来引出问题

我们在之前说了,在fork()函数之后的父子进程共享代码,那么这样一来,我们创建子进程就没有了意义,实际上使用的时候是要使用if来分流的,也就是父子进程去做不同的事情。

fork的返回值:

1.如果子进程创建成功了,在父进程中返回子进程的PID,而在子进程中返回0。

2.如果子进程创建失败,则父进程中返回。

既然子进程创建的返回值不一样,那么我们就可以通过这个性质来分流。

代码如下:

运行结果:

六、Linux的进程状态

进程从创建开始到被系统清理消亡的这个时间里,有时会占用CPU,有时在等待CPU分配资源,从这里看,进程是不同于程序的,进程有着自己动态变化的状态。下面的这个图就是我们待会要说明的。

我们可以看看在Linux中的进程状态的内容,下面是Linux内核的部分源代码:

/*
*The task state array is a strange "bitmap" of
*reasons to sleep. Thus "running" is zero, and
*you can test for combinations of others with
*simple bit tests.
*/static const char *const task_state_array[] = {"R (running)", /*0 */"S (sleeping)", /*1 */"D (disk sleep)", /*2 */"T (stopped)", /*4 */"t (tracing stop)", /*8 */"X (dead)", /*16 */"Z (zombie)", /*32 */};

敲黑板:

这里的进程状态实际上是保存在进程控制块(PCB)中的,而在Linux中就是保存在了task_struct中的。

 在Linux中我们可以使用ps aux和ps axj来查看进程的状态。

ps aux

ps axj

1.运行状态——R

这个状态(Runing)不一定就是进程处在运行当中,也有可能是在运行队列当中,也就是说系统中可以同时有多个处于R状态的进程。

敲黑板:

所有处于这个状态的进程,都是可以被调度的,他们在运行队列当中,在操作系统需要切换进程时,可以在这个里面直接选取。 

2.浅度睡眠状态——S 

一个进程处于浅度睡眠状态(sleeping),意味着该进程在等待事件的完成,这时的进程是可以被随时唤醒的(和人一样),也可以被杀亖(由于这个原因,这个状态也被叫做可中断睡眠(interruptible sleep)。

我们可以写个代码来演示一下:

这里我们让程序休眠100秒来模拟进程处于浅度睡眠状态。

ps aux | head -1 && ps aux | grep test | grep -v grep

之前说过处于这个状态的进程是可以被杀亖的,我们来看看:

3.深度睡眠状态——D

一个进程处于深度睡眠状态(disk sleep),表示这个进程是不可以被杀亖的,即使是操作系统也不行,只能该进程自动唤醒才可以恢复。所以这个状态也可以被叫做是不可中断睡眠(uninterruptible sleep),处在这个状态的进程通常需要等待IO的结束。

其实我们通过他的英文也可以知道,他肯定适合磁盘(disk)相关的,具体的场景就是要向硬盘中写数据,这个过程是不能被杀亖的,因为我们等待磁盘的回复(是否写入完毕)来做出反应(磁盘处于休眠状态)。

4.暂停状态——T

在Linux中我们可以发送SIGSTOP信号让进程处于暂停状态(stopped),发送SIGCONT信号让处于暂停状态的进程重新运行起来。

例如:

我们发送一个SIGSTOP信号给test让进程处于暂停状态。

然后我们再发送一个SIGCONT信号让进程重新运行,这里运行的时候尽量快一点,因为时间一代程序就结束了:

补充说明:

我们可以使用kill -l命令来列出命令集

kill -l

5.僵尸状态——Z

一个进程在要退出时,在系统层面,并不是我们想的直接就释放资源,而是会保存一段时间,来供操作系统或父进程读取退出信息,如果没有读取到相关的退出信息,那么数据也不会被释放,一个进程在等待数据被释放的过程就是处于僵尸状态(zombie)。

我们通过上面的描述也能知道僵尸状态其实是很必要的,因为进程就是去被指示去做事情的,那么指示方就应该要知道被指示方的完成情况,而僵尸状态就是指示方来获取完成情况的。

例如:我们之前一直在写的return 0;实际上这个0就是返回给给操作系统的,让操作系统知道我们完成的情况。在Linux中我们可以通过打印$?来获取最近一个进程的退出码。

echo $?

敲黑板:

和运行状态一样,这个退出码也是被保存在PCB中的,在Linux中就是在task_struct中。

6.死亡状态 

死亡状态就是一个理论上的返回状态,当一个进程的退出信息被读取后,该进程申请的资源就被释放掉了,那么该进程也就不存在了,所以我们不可能在我们列出来的信息中看到死亡状态。

七、僵尸进程

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

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

相关文章

【Harmony】【鸿蒙】List列表View如何刷新内部的自定义View的某一个控件

创建自定义View Component export struct TestView{State leftIcon?:Resource $r(app.media.leftIcon)State leftText?:Resource | string $r(app.string.leftText)State rightText?:Resource | string $r(app.string.rightText)State rightIcon?:Resource $r(app.med…

Docker安装MySQL集群(主从复制)

为确保生产环境中的数据安全与可靠性,数据库普遍采用主从集群架构(一主一从)进行部署。本文将系统阐述如何利用Docker镜像实现数据库集群的容器化部署,并完整记录各配置环节的具体实现步骤。 一、主服务实例创建(可以…

开篇:MCP理论理解和学习

文章目录 零 参考资料一 MCP概念二 MCP核心架构和功能三 MCP VS OP(Others Protocol)3.1 函数调用3.2 模型上下文协议3.3 MCP VS Others Protocol3.3.1 MCP与Function Calling的对比优势3.3.2 MCP与AI Agents的协同关系3.3.3 MCP与A2A协议的互补性3.3.4 MCP与传统API的技术革新…

产品经理面经(三)

目录 为什么想做产品经理?为什么适合做产品经理? 解析 我的回答: 你觉得产品经理应该具备什么品质 解析 我的回答 想做什么方向的产品经理呢&你知道产品经理分为哪几种吗? 解题思路 为什么想做产品经理?为…

Vue3 Composition API: 企业级应用最佳实践方案

在当前前端技术迅速发展的环境下,Vue3 Composition API 成为了关注的焦点。它为开发人员提供了更加灵活和可维护的代码结构,适用于构建大规模企业级应用。在本文中,我们将探讨Vue3 Composition API的最佳实践方案,帮助开发人员更好…

CentOS大师班:企业级架构与云端融合实战

一、高级存储管理与灾难恢复 1. LVM动态卷扩展实战 pvcreate /dev/sdb1 # 创建物理卷 vgcreate vg_data /dev/sdb1 # 创建卷组 lvcreate -L 100G -n lv_www vg_data # 创建逻辑卷 mkfs.xfs /dev/vg_data/lv_www # 格式化 mount /dev/vg_da…

使用VGG-16模型来对海贼王中的角色进行图像分类

动漫角色识别是计算机视觉的典型应用场景,可用于周边商品分类、动画制作辅助等。 这个案例是一个经典的深度学习应用,用于图像分类任务,它使用了一个自定义的VGG-16模型来对《海贼王》中的七个角色进行分类,演示如何将经典CNN模型…

[创业之路-377]:企业战略管理案例分析-战略制定/设计-市场洞察“五看”:看宏观之社会发展趋势:数字化、智能化、个性化的趋势对初创公司的战略机会

数字化、智能化、个性化趋势为初创公司带来了捕捉长尾需求、提升运营效率、创新商业模式等战略机会,具体分析如下: 一、数字化趋势带来的战略机会 捕捉长尾需求:数字化技术能够帮助初创公司更好地捕捉市场中的长尾需求,满足那些…

macOS 安装 PostgreSQL

文章目录 安装安装信息 验证GUI 工具下载 安装 最简单的方式是通过 brew 安装 brew install postgresql17该版本在 brew 上的详情页:https://formulae.brew.sh/formula/postgresql17 你也可以根据需要,搜索 安装更新版本 如果你没有安装 brew&#xf…

安装openresty使用nginx+lua,openresty使用jwt解密

yum install -y epel-release yum update yum search openresty # 查看是否有可用包 yum install -y openresty启动systemctl start openresty验证服务状态systemctl status openresty设置开机自启systemctl enable openrestysystemctl stop openresty # 停止服务 system…

全球化 2.0 | 云轴科技ZStack助力中东智慧城市高性能智能安防云平台

在全球智慧城市加速建设的背景下,中东某大型城市通过部署云轴科技ZStack Cloud云平台,成功实现公共安全视频监控系统(CCTV)的智能化升级。该平台以弹性计算、GPU智能调度、高可用架构为核心,支撑千路高清视频流处理及人…

Day35打卡 @浙大疏锦行

知识点回顾: 三种不同的模型可视化方法:推荐torchinfo打印summary权重分布可视化进度条功能:手动和自动写法,让打印结果更加美观推理的写法:评估模式 作业:调整模型定义时的超参数,对比下效果。…

嵌入式软件-如何做好一份技术文档?

嵌入式软件-如何做好一份技术文档? 文章目录 嵌入式软件-如何做好一份技术文档?一.技术文档的核心价值与挑战二.文档体系的结构化设计三.精准表达嵌入式特有概念四. **像管理代码一样管理文档**,代码与文档的协同维护五.质量评估与持续改进5.…

css五边形

五边形 .fu{width: 172rpx;height: 204rpx;overflow: hidden;border-radius: 10rpx;clip-path: polygon(0% 0%, 100% 0%, 100% 75%, 50% 100%, 0% 75%, 0% 25%); }六边形 clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);

【Java高阶面经:微服务篇】1.微服务架构核心:服务注册与发现之AP vs CP选型全攻略

一、CAP理论在服务注册与发现中的落地实践 1.1 CAP三要素的技术权衡 要素AP模型实现CP模型实现一致性最终一致性(Eureka通过异步复制实现)强一致性(ZooKeeper通过ZAB协议保证)可用性服务节点可独立响应(支持分区存活)分区期间无法保证写操作(需多数节点可用)分区容错性…

头歌软工导论作业

一.集成测试和确认测试 第1关:集成测试 1、 集成测试的主要方法有CD A、 自顶向下集成方法 B、 自底向上集成方法 C、 渐增式测试方法 D、 非渐增式测试方法 2、 目前在进行集成测试时普遍采用非渐增式测试方法。B A、 √ B、 3、 自底向上集成策略是从主控制模…

MFC:获取所有打印机的名称(打印机模块-1)

背景: 在一个 MFC 应用程序中,列出本地系统中安装的打印机,并检测是否存在“Microsoft Print to PDF”或“Microsoft XPS Document Writer”虚拟打印机。如果有,则选择其中一个作为默认或后续操作对象;如果没有&#…

设计模式-行为型模式(详解)

模板方法 模板方法模式,它在一个抽象类中定义了一个算法(业务逻辑)的骨架,具体步骤的实现由子类提供,它通过将算法的不变部分放在抽象类中,可变部分放在子类中,达到代码复用和扩展的目的。 复用: 所有子类可以直接复…

STM32中的IIC协议和OLED显示屏

串口通信协议的缺点 串口通信通常需要至少三条线(TX、RX和GND),而 I2C 总线仅需要两条信号线(SDA和SCL); 串口通信仅支持一对一通信,而 I2C 总线支持多机通信,允许单个主机与多个从…

30个性能优化方案

1.用String.format拼接字符串 不知道你有没有拼接过字符串,特别是那种有多个参数,字符串比较长的情况。 比如现在有个需求:要用get请求调用第三方接口,url后需要拼接多个参数。 以前我们的请求地址是这样拼接的: S…