Linux操作系统:学习进程_了解并掌握进程的状态

  对进程状态之间转换感到头疼,只听书本概念根本无法理解,死记硬背不是什么好的解决方法。只有进行底层操作去了解每一个进程状态,才能彻底弄清楚进程状态是如何转换的。

一、进程的各个状态

  我们先从Linux内核数据结构来看:

  每一个进程都是有其task_struct和它的代码和数据组成的,进程的状态被定义在task_struct里面,是其中的一条属性,进程状态改变修改这条属性就可以了。

在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 */

};                 

  为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在 Linux内核里,进程有时候也叫做任务)。

  • R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
  • S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠 (interruptible sleep))。
  • T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可 以通过发送 SIGCONT 信号让进程继续运行。
  • D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的 进程通常会等待IO的结束。
  • X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

1、R运行状态

  我们写以下代码:

  死循环,方便我们时刻查看进程的状态

左边查看进程的状态,右边执行可执行程序 

  在状态栏里,我们能够看出,此时进程的状态是R+-运行状态 ,这里的+表示在前台运行

  这个比较容易理解。

2、S睡眠状态

2.1进程等待资源就绪

我们打开刚刚写的代码,在while循环里加上一个printf接口,打印我们的进程PID

  接着,我们再在左边打开我们的查看进程状态的窗口,右边执行可执行程序

  但是这次进程的状态,不再是R运行状态,而是S睡眠状态。

  这里的原因是,这次的代码中有打印接口,会将打印的数据打印到我们的显示器上,但是CPU运算速率很快,而将打印数据传输到我们的显示器上的速度比较慢,和CPU的运算速率不在一个层次上,所以在很大一段时间内,CPU都在等待数据传输到显示器上,等待过程中,就是进程的睡眠状态。所以这时候会显示进程睡眠状态。

  显示器是外设,也就是外设资源,所以呢,我们把这种情况叫做:进程等待资源就绪

2.2可中断睡眠

  我们修改代码,再让他打印前先睡10s

前10s,进程没有打印数据,但仍然是睡眠状态

  这时我们可以ctrl+c终止进程 

  ctrl之后,进程退出,睡眠状态结束。

  所以我们把这种状态又叫可中断睡眠 

3、T/t停止状态

  写出以下代码:

我们运行并查看进程状态:

 这时候是睡眠状态,屏幕中仍在打印着。

 那我们有没有办法把它暂停,我们可以指令kill -19使其暂停,使用之前,我们先来看看kill 都有哪些指令:

  指令有很多,现在只用了解三个,有我们常用的kill -9杀死进程指令,也有我们接下来要用到的,kill -18 唤醒进程,kill -19 暂停进程 。

   我们用kill -19 指令暂停进程   可以看到,进程状态由S变为了T。

   再使用kill -18 唤醒进程

  进程状态又由T变成了S。

4、D磁盘休眠状态

  先描述一个场景:内存中有一个进程,我现在要把这个进程中的一部分数据存到磁盘当中,大小为1GB,这个数据很重要。在传输时,该进程状态会设置成睡眠状态S.但是在传输的过程中,内存空间严重不足了。

  操作系统管理着所有进程,在内存严重不足的时候,我们的操作系统有权利对进程进行杀死来释放空间,操作系统此时看到这个进程在睡觉,直接把它杀死了。那数据没有传输完成,结果导致这么重要的数据丢失了。那拿谁问责呢?

  所以为了防止这个问题产生,就出现了D磁盘休眠状态,也被称为不可中断睡眠

  无法被中断,也无法被杀死,只能等他操作完成后自己醒来,或者强制重启电脑

  

二、僵尸进程和孤儿进程

1、僵尸进程

1.1概念

  在子进程退出时,它的退出信息会保存在它的tast_struct里等待着父进程读取,被父进程读取之后,才会被操作系统回收。

  僵尸进程就是子进程退出之后,它的退出信息并没有被父进程读取,从而处于失效状态,没有被回收。

  1.2僵尸进程带来的问题

  我们知道,进程=task_struct+进程的代码和数据,当子进程退出之后,它的代码和数据不会再被使用,已经被释放掉,但是它的task_struct会一直都在,必须等待操作系统读取。task_struct会一值占用小段内存,这就会造成内存泄漏,使得这小段内存再也无法使用。

1.3演示

写一段以下代码:

  在前五秒的时候,我们的子进程会跟父进程一起打印,五秒后,子进程退出,父进程依然打印,从左边状态可以看出,我们的子进程状态退出后状态变成了Z:僵尸状态

  这就是因为我们的子进程退出之后,父进程依然在打印,没有时间去读取子进程的退出信息,从而使它变成了僵尸进程。         

  那为什么我们在写单进程代码时,不会出现这种情况呢?因为单进程的父进程是bash,

bash是什么,bash可以理解为最顶端的父进程,bash会自动回收Z状态进程。

 

2、孤儿进程

  孤儿进程是在我们子进程没有退出之前,其父进程先退出,父亲不见了,其就变成了孤儿,此时他也面临着无法回收问题,但是它一般会被一号进程(OS本身)领养,由一号进程再把它回收。

  我们修改一下代码,让父进程先退出:

  此时子进程将称为孤儿 

   可以看到由前台S+状态变成了后台S状态 


至于如何回收,我们下篇文章再谈。

三、运行、阻塞、挂起

接下来我们重点来说一下这三种状态的概念和相互转换

1、运行

  在我们的操作系统中,进程一般都在进程队列里,新建一个进程就是将这个进程的task_struct和其代码和数据放到这个进程队列里,再通过链表链接起来。

  操作系统想要调度进程,前提是CPU需要去维护一个运行队列,我们想要运行进程,那么就要把这个进程放在运行队列里,这样操作系统就调度进程使,让CPU从运行队列里调度就可以了。

  这时存在于运行队列里的进程都是运行状态。严谨点来说,存在于运行队列里的都是就绪态,被调用时才是运行态

2、阻塞

  先描述一个场景,当我们写代码时,用到scanf函数,进程在等待我们键盘输入的这个过程,是如何等待的呢?

  scanf函数等待我们的硬件-键盘输入,也是需要操作系统进行管理的,我们前篇已经讲过,操作系统对硬件管理时,也是用到内核数据结构,将所有硬件的属性和信息放到一个结构体里,用链表的形式链接。

  我们要明确一个概念,进程在等待键盘输入,既然是等待,那么他就不是在运行,不是运行态就不会在运行队列里,那么它会在哪里呢?

  在设备的等待队列里,此时设备的结构体中会有一个等待队列,来存放等待设备响应的进程。当进程在等待键盘输入时,这个进程的task_struct会被放入这个设备的等待队列里,这时候就变成了阻塞态

  接着我们的键盘输入后,该进程task_struct又会被放入运行队列,这一操作被称为唤醒状态又会变成运行态,此时我们的CPU就可以获取我们输入的数据了

3、挂起

  当我们处于阻塞态时,进程都在等待硬件响应,等待的这段时间里,它占用内存但是不会去做什么实际的事。不断的累计会造成内存吃紧,当内存吃紧的时候,我们就需要一种方式去缓解内存压力

  在我们的磁盘里,有一个叫做swap分区,用来存放内存里的一些数据,当我们内存吃紧的时候,我们可以把处于阻塞态的进程的代码和数据暂存到磁盘中的swap分区里,这样就会腾出可观的空间,缓解内存压力。这时进程的状态就被称为挂起。

  由内存到swap分区这一操作被称为唤出,由swap分区到内存这一操作被称为唤入。

  

  频繁唤入唤出会不会有什么后果呢?

  当操作系统感觉到压力大的时候,会把一些数据暂存到swap分区,如果我们的swap分区较大,那么操作系统每次感觉到压力大的时候,都会把一些数据暂存到swap分区,在swap分区的数据多了,那么电脑的效率必然会降低,因为每次都需要把数据唤入到内存。

  所以,频繁的唤入唤出会导致系统效率降低,我们可以把swap分区空间设置的不要太大,从而让操作系统合理的使用swap分区。

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

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

相关文章

分布式环境下宕机的处理方案有哪些?

大家好,我是锋哥。今天分享关于【分布式环境下宕机的处理方案有哪些?】面试题。希望对大家有帮助; 分布式环境下宕机的处理方案有哪些? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在现代分布式系统中,…

接触角测量(Contact Angle Measurement)

接触角是指液滴在固体表面上的形态,特别是在气、液、固三相交界点处,气-液界面切线与固-液交界线之间的夹角。这个角度是衡量液体对固体表面润湿性的一个重要参数。接触角的大小可以反映液体与固体之间的相互作用强度,从而了解液体在固体表面…

《操作系统 - 清华大学》2 -2:中断、异常和系统调用

文章目录 1. 中断和异常处理机制1.1 中断1.2 异常 2. 系统调用2.1 标志C库的例子2.2 编程接口 3.系统调用的实现4. 程序调用和系统调用的不同处5. 中断、异常和系统调用的开销 1. 中断和异常处理机制 接下来看一看中断和异常的处理过程,看下图就比较清楚&#xff0…

Nginx简易配置将内网网站ssh转发到外网

声明:本内容仅供交流学习使用,部署网站上线还需要根据有关规定申请域名以及备案。 背景 在内网的服务器有一个运行的网页,现使用ssh反向代理,将它转发到外网的服务器。 但是外网的访问ip会被ssh反向代理拦截 所以使用Nginx进行…

Moment.js、Day.js、Miment,日期时间库怎么选?

一直以来,处理时间和日期的JavaScript库,选用的都是Momment.js。它的API清晰简单,使用方便灵巧,功能还特别齐全。 大师兄是Moment.js的重度使用者。凡是遇到时间和日期的操作,就把Moment.js引用上。 直到有天我发现加…

后台管理系统窗体程序:文章管理 > 文章发表

目录 文章列表的的功能介绍: 1、进入页面 2、页面内的各种功能设计 (1)进入选择 (2)当获取到唯一标识符时 (3)当没有标识符时 (4)发布按钮,存为草稿 一、网…

Linux服务控制及系统基本加固

一. liunx操作系统的开机引导的过程 1. 开机自检 根据bios的设置,对cpu,内存,显卡,键盘等等设备进行初步检测如果以上检测设备工作正常,系统会把控制权移交到硬盘 总结:检测出包含系统启动操作系统的设备,硬盘&#…

通过 SSH 隧道将本地端口转发到远程主机

由于服务器防火墙,只开放了22端口,想要通过5901访问服务器上的远程桌面,可以通过下面的方式进行隧道转发。 一、示例命令 这条代码的作用是通过 SSH 创建一个 本地端口转发,将你本地的端口(5901)通过加密的 SSH 隧道连接到远程服务器上的端口(5901)。这种方式通常用于在…

WPF+MVVM案例实战与特效(二十八)- 自定义WPF ComboBox样式:打造个性化下拉菜单

文章目录 1. 引言案例效果3. ComboBox 基础4. 自定义 ComboBox 样式4.1 定义 ComboBox 样式4.2 定义 ComboBoxItem 样式4.3 定义 ToggleButton 样式4.4 定义 Popup 样式5. 示例代码6. 结论1. 引言 在WPF应用程序中,ComboBox控件是一个常用的输入控件,用于从多个选项中选择一…

C#中日期和时间的处理

目录 前言 时间对于我们的作用 一些关于时间的名词说明 格里高利历 格林尼治时间(GMT) 协调世界时(UTC) 时间戳 DateTime 初始化 获取时间 计算时间 字符串转DateTime 存储时间 TimeSpan 初始化它来代表时间间隔 用它相互计算 自带常量方便用于和ticks进行计…

pdb和gdb的双剑合璧,在python中调试c代码

左手编程,右手年华。大家好,我是一点,关注我,带你走入编程的世界。 公众号:一点sir,关注领取python编程资料 问题背景 正常情况下,调试python代码用pdb,调试c代码用gdb,…

【Apache ECharts】<农作物病害发生防治面积>

在vs Code里打开, 实现 1. 首先引入 echarts.min.js 资源 2. 在body部分设一个 div,设置 id 为 main 3. 设置 script 3.1 基于准备好的dom,初始化echarts实例 var myChart echarts.init(document.getElementById(main)); 3.2 指定图表的…

Docker + Jenkins + gitee 实现CICD环境搭建

目录 前言 关于Jenkins 安装Jenkins docker中运行Jenkins注意事项 通过容器中的Jenkins,把服务打包到docker进行部署 启动Jenkins 创建第一个任务 前言 CI/CD(持续集成和持续交付/持续部署),它可以实现自动化的构建、测试和部署…

Leetcode 买卖股票的最佳时机 Ⅱ

使用贪心算法来解决此问题,通过在价格上涨的每一天买入并在第二天卖出的方式,累计所有上涨的利润,以实现最大收益。关键点是从第二天开始遍历,并且只要当前比前一天价格高,我们就在前一天买入然后第二天卖出去。下面是…

【Linux系列】命令行中的文本处理:从中划线到下划线与大写转换

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

使用Docker快速部署FastAPI Web应用

Docker是基于 Linux 内核的cgroup、namespace以及 AUFS 类的Union FS 等技术,对进程进行封装隔离,一种操作系统层面的虚拟化技术。Docker中每个容器都基于镜像Image运行,镜像是容器的只读模板,容器是模板的一个实例。镜像是分层结…

【go从零单排】迭代器(Iterators)

🌈Don’t worry , just coding! 内耗与overthinking只会削弱你的精力,虚度你的光阴,每天迈出一小步,回头时发现已经走了很远。 📗概念 在 Go 语言中,迭代器的实现通常不是通过语言内置的迭代器类型&#x…

C语言--结构体的大小与内存对齐,位段详解

一.前言 为了保证文章的质量和长度,小编将会分两篇介绍,思维导图如下,上篇已经讲过了概念部分,本文主要讲解剩余部分,希望大家有所收获🌹🌹 二.结构体的大小与内存对齐 2.1 存在对齐的原因 平…

UnicodeDecodeError: ‘utf-8‘ codec can‘t decode bytes ... - python 报错解决方案

背景 加载数据集突然出问题, 详细报错为:UnicodeDecodeError: utf-8 codec cant decode bytes in position 606-607: invalid continuation byte 解决方案 源文件另存 UTF-8 版的 csv。 即可运行:

MQTT协议解析 : 物联网领域的最佳选择

1. MQTT协议概述 1.1 MQTT协议是什么 MQTT : Message Queuing Telemetry Transport 模式 : 发布 / 订阅主题优点 : 代码量小、低带宽、实时可靠应用 : 物联网、小型设备、移动应用MQTT 常用端口 : 1883 MQTT是一个网络协议,和HTTP类似,因为轻量简单&…