【Linux】进程的基础概念 进程的相关操作 进程的状态
进程
- 一、进程的基本知识
- 1、基本概念
- 2、进程的描述 —— PCB
- 3、task_ struct内容分类
- 二、进程的相关操作
- 1、在Linux下查看进程
- 2、通过系统调用在代码中获取进程标示符
- 3、如何创建子进程
- 4、关于fork()的一些深度理解
- 三、进程的状态
- Linux中的进程的状态
- 四、僵尸进程与孤儿进程
- 僵尸进程
- 孤儿进程
一、进程的基本知识
1、基本概念
在通常的课本中进程的概念:程序的一个执行实例,正在执行的程序等。
如果以内核的观点来看应该是:内核关于进程的数据结构(PCB) + 当前进程的代码与数据。
2、进程的描述 —— PCB
PCB(process control block)进程的相关信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合,Linux操作系统下的PCB是: task_struct
一个结构体类型。
task_struct
是PCB的一种,也是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。
进程的组织:所有运行在Linux系统里的进程都以task_struct
链表的形式存在内核里,可以在内核源代码里找到它。
3、task_ struct内容分类
标示符: 描述本进程的唯一标示符,用来区别其他进程。
状态: 任务状态,退出代码,退出信号等。
优先级: 相对于其他进程的优先级。
程序计数器: 程序中即将被执行的下一条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据: 进程执行时处理器的寄存器中的数据。
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
其他信息。
二、进程的相关操作
1、在Linux下查看进程
在Linux下查看当前进程的命令是ps
命令。
ps命令来自于英文词组“process status”的缩写,其功能是用于显示当前系统的进程状态。
使用ps命令可以查看到进程的所有信息,例如进程的号码、发起者、系统资源使用占比(处理器与内存)、运行状态等等。帮助我们及时的发现哪些进程出现”僵死“或”不可中断“等异常情况。
常用参数:
- a 显示现行终端机下的所有程序,包括其他用户的程序
- x 显示所有程序,不以终端机来区分
- j 采用工作控制的格式显示程序状况
ps -axj
查看当前正在运行的进程
可以看到每个进程都有自己的pid,每个进程的pid都是不一样的,通过pid操作系统和我们可以区别不同的进程。
当然在Linux中还有其他方式查看当前运行的进程,进程的信息可以通过/proc 系统文件夹查看,这是一个特殊的目录,里面有许多以pid来命名的目录,以pid命名的目录存放的是当前pid进程的相关信息,当当前进程结束后pid会消失,同时以pid命名的目录也会被删除。所以这个目录是一个动态的目录。
2、通过系统调用在代码中获取进程标示符
在Linux中进程id是(PID),进程的父进程id是(PPID)那么我们怎么在代码中获取一个进程的pid呢?
答案是:Linux系统为我们提供了两个系统调用接口getpid()
getppid()
通过这两个函数我们可以拿到当前进程的pid与当前进程的父进程的pid
注意:getpid() 与 getppid()的返回值是 pid_t ,此类型是int 的一个 typedef
那么我们来看一下下面程序运行起来后的结果:
运行结果
可以看到getpid()与getppid()为我们返回了相应进程的pid与其父进程的pid,我们再用查看进程的相关指令查看一下当前进程。
ps -axj |head -1 && ps -axj |grep processC|grep -v grep
我们查看到的进程pid与我们程序返回的pid相同,再次证明结果没有错。
那么我们还有一个疑问,我们当前进程的父进程又是谁呢?我们再次查看一下
ps 22927
我们可以看到我们程序的父进程是bash,也就是shell外壳,为什么会是这样呢?原来在Linux中我们让bash去执行命令./processC
让bash帮我们运行我们所写的程序,但是我们所写的程序可能有问题,如果bash自己帮我们运行了有问题程序,那么bash就无法进行正常的命令解释了,那么我们整个Linux接下来就无法进行任何操作了,所以bash为了保护自己同时为了能让Linux的其他程序能正常运行不受干扰,bash采用了创建子进程的方式来帮助我们运行程序,换句话说,在shell下直接运行的程序其父进程都是shell,shell是通过创建子进程的方式完成我们所交给shell的命令。
通过这个现象我们可以知道:
- bash命令行解释器,本质上它也是一个进程!
- 命令行启动的所有的程序,最终都会变成进程,而该进程对应的父进程都是bash
3、如何创建子进程
shell可以通过创建子进程的方式来完成我们交给shell的命令,那我们可不可以在我们所运行的进程里面再创建一个子进程来帮我们完成相应的任务呢?答案是可以的,Linux系统给我们提供了一个系统调用接口fork()
函数,通过这个函数我们能够将创建一个新的子进程,我们先来看看fork()
的man
手册吧。
简单的了解完了fork(),我们先动手用一下fork()吧。我们看以下代码,如果创建成功了那么下面两条printf
函数都应该会被执行!
实验结果:
这说明我们的fork()函数,确实能帮我们创建一个新的子进程,fork()通过返回给父进程子进程不同的值让父进程与子进程运行了不同的代码。
fork()的执行过程:
- fork之后,执行流会变成2个执行流.
- fork之后,两个执行流谁先运行由调度器决定。
- fork之后的代码共享,通常我们通过 if 和 elseif 来进行执行流分流完成不同的任务!
4、关于fork()的一些深度理解
- fork()做了什么
fork()函数创建子进程并不是在内存中重新拷贝一份代码与数据,而是在内存中以父进程为模板创建一个新的PCB结构,子进程的PCB与父进程大部分属性都是一致的但是也有一部分不一样,如:pid,ppid。
这个子进程的PCB与父进程PCB一样指向同一份代码与数据!
- fork创建的子进程与父进程是怎么保证相互独立性的
我们都知道,进程在运行时是具有独立性的,关闭A进程不会影响B进程,同样我们杀死父进程也不会影响子进程,但是我们知道fork()
创建子进程时并没有复制父进程的代码,那么父子进程之间又是怎么保持进程之间的相互独立性的呢?
首先,进程 = 内核数据结构(PCB) + 当前进程的代码与数据。
子进程与父进程有不同的PCB ,可以保证内核数据结构的独立性,然后就是讨论代码与数据是怎么保持独立性的呢?
我们知道对于代码,当我们编译完成以后代码便变成了是只读的常量,常量无法修改,于是父子进程执行同一份代码就不会影响父子进程彼此之间的代码逻辑了。
那么对于数据呢?我们看上面的代码中有一个变量 pid 明明是同一个pid,为什么一个变量里面存放了两个不同的值呢?答案是写时拷贝,当我们父子进程尝试去修改数据的值时,便会触发写时拷贝,写时拷贝会给我们复制一份当前数据的值,让我们的父子进程去其他位置修改数据,而不是真正在原数据的基础是修改数据。
通过上面的解释我们知道,父子进程拥有自己独立的PCB,父子进程的代码是只读的,所以父子进程都无法修改代码,也就无法相互影响,父子进程的数据是以写时拷贝的方式各自私有一份,通过这些便让我们的父子进程保持了相互的独立性。
- fork为什么有两个返回值
我们知道,当一个函数准备执行 return
语句的时候,该函数的主体功能就已经完成了,return 语句不影响函数的功能,仅仅起到返回结果的作用。因此, fork 系统调用函数在执行 return 语句之前,子进程就已经创建完成甚至已经被操作系统调度了,所以当执行 return 语句返回结果的时候,由于在复制时复制了父进程的堆栈段,所以两个进程都停留在fork函数中,等待返回。因此fork函数会返回两次,一次是在父进程中返回,另一次是在子进程中返回,这两次的返回值是不一样的。就要给父进程与子进程各自返回一份结果,即执行了两次。最终返回结果被赋值给变量 pid 的时候,OS自动触发了写时拷贝,分别把结果存入两者的备份空间中。
三、进程的状态
在了解进程的状态之前我们先来了解两个概念:
阻塞:进程因为等待某种条件就绪,而处于一种不推进的状态。
为什么会有阻塞状态呢?因为计算机是资源是有限的,当某种资源被其他进程占据时,一些也要使用此资源的进程便要进入阻塞状态,等待资源处于就绪状态后才能继续运行。
挂起:进程在处于阻塞状态时,内存里面既有task_struct结构体又有代码和数据,为了缓解内存的压力,暂时将代码和数据存放进磁盘中,将内存中的代码和数据进行删除,此时内存里面只有该进程的task_struct结构体而没有此进程的代码与数据的状态被称为挂起状态。
Linux中的进程的状态
- R运行状态 (running):运行状态并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
- S睡眠状态(sleeping):意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠( interruptible sleep)
我们来看看下面的代码运行起来后进程的状态:
要查看进程的状态我们可以用:
ps -axj | head -1 && ps -axj | grep test1c | grep -v grep
我们明明可以看到右边的程序一直在运行,但是我们的Linux却告诉我们,我们的进程处于睡眠状态,(+号表示正在台前运行而不是后台运行)为什么会这样呢?
这是因为我们的代码要使用显示器设备,在我们执行printf
函数时由于我们的进程需要使用显示器资源,但是显示器资源没有处于就绪状态,于是我们的CPU就无法继续执行此进程,此时进程便不在CPU队列里排队了,而跑去显示器队列里排队,所以此进程便被设置为了睡眠状态,当显示器资源就绪后我们的进程才会重新进入CPU队列里变为运行状态,而外设的运行速度是远远低于CPU的,所以运行此代码我们几乎99%时间看到的都是处于睡眠状态。
之所以S状态又被称为可中断睡眠是因为我们可以使用ctrl+C
或kill命令去杀掉此进程。
kill -9 对应进程的pid # 杀死一个进程
当我们对代码进行修改,不去访问外设时我们再看看此时进程的运行状态:
通过观察我们发现我们的进程一直处于R状态,这是因为我们的代码中没有去访问外设降低代码的执行速度。
-
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
当我们的处于R状态进程需要向磁盘内部写入一些数据时,我们的进程便会从R状态切换为D状态,这是因为CPU的执行速度很快,但是向磁盘写入数据很慢,同时呢,为了防止进程在向磁盘中写数据时被杀掉(Linux操作系统会在内存不足时进行杀后台)造成数据丢失,处于此状态的进程不能被中断,那怕是操作系统,kill
命令,CTRL+C
,都无法使此状态的进程中止。 -
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程,这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
通过kill -l
命令我们能列出kill
命令下的所有信号。
通过观察我们可以知道当我们想要暂停一个进程时可以发送
kill -19 对应进程的pid
当我们想要让一个暂停的进程继续运行时可以发送
kill -18 对应进程的pid
我们来看下面一段代码运行后,我们发送 kill -19
号信号
我们发现进程确实暂停了,但现在我们又想要让进程运行起来,我们需要发送 kill -18
号信号
-
t (tracing stop)追踪状态,此状态也是暂停状态的一种,当我们用gdb调试我们C语言代码时我们运行至断点时便处于此种状态。
-
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
-
Z(zombie)-僵尸状态
僵尸状态(Zombies)是一个比较特殊的状态。在子进程退出过程中,进程占有的所有资源将被回收,但是如果子进程立即退出,那么父进程就会拿不到子进程中的一些关键信息,于是,系统为了方便父进程拿到子进程的相关信息,在子进程退出时,子进程并不会立即退出,而是维持一段时间的僵尸状态。等到父进程拿到子进程中的关键信息后,子进程才能退出进程,变为X死亡状态。
在僵尸状态时操作系统会去除除了task_struct结构(以及少数资源)以外的所有资源。于是进程就只剩下task_struct这么个空壳,故称为僵尸。之所以保留task_struct,是因为task_struct里面保存了进程的退出码、以及一些统计信息。而其父进程很可能会关心这些信息。比如在shell中,$?
变量就保存了最后一个退出的前台进程的退出码,而这个退出码往往被作为if语句的判断条件。
四、僵尸进程与孤儿进程
僵尸进程
处于僵尸状态的进程被称为僵尸进程,僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程就会一直处于Z状态。
我们先来看下面一段代码
我们先运行起来
我们可以利用kill
命令去杀死子进程,然后观察子进程的状态是直接死亡状态还是僵尸状态。
通过观察我们发现子进程处于僵尸状态,因为父进程一直没有去读取子进程的退出状态代码,于是子进程要一直维持僵尸状态。此时子进程就是一个僵尸进程。
僵尸进程危害
维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,Z状态一直不退出,PCB一直都要维护那个子进程的僵尸状态,如果父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费!
孤儿进程
在僵尸进程中,如果子进程先退出,父进程后退出,子进程要进入僵尸状态一段时间直到父进程读取到子进程的退出状态代码。那如果父进程先退出,子进程后退出呢?
父进程先退出,子进程就被称之为“孤儿进程”,孤儿进程会被1号进程(操作系统)领养,当然要有操作系统进程回收子进程喽。如果操作系统不进行领养会造成子进程一直处于僵尸状态,从而造成内存泄漏。
注意:操作系统领养后,子进程会由前台进程转换为后台进程。
我们还是运行上面的代码,然后先用kill
命令杀死父进程(父进程死亡后会由bash回收)然后观察子进程。
kill
命令前
kill
命令后
相关文章:

【Linux】进程的基础概念 进程的相关操作 进程的状态
进程一、进程的基本知识1、基本概念2、进程的描述 —— PCB3、task_ struct内容分类二、进程的相关操作1、在Linux下查看进程2、通过系统调用在代码中获取进程标示符3、如何创建子进程4、关于fork()的一些深度理解三、进程的状态Linux中的进程的状态四、僵尸进程与孤儿进程僵尸…...

【小猫爪】AUTOSAR学习笔记06-Communication Stack之ComM模块
【小猫爪】AUTOSAR学习笔记06-Communication Stack之ComM模块前言1 ComM简介2 ComM功能介绍2.1 PNC 状态管理2.2 Channel状态管理2.3 通信禁止功能2.4 不同类型的NM2.5 User、PNC 与 Channel 的映射2.6 状态保存END前言 因为一个偶然的机会让我接触到了AUTOSAR,所以…...

BP插件开发(JAVA)
本文会包括BP插件开发流程及打包,API,javaswing(UI)部分的内容。阅读完本文后,读者将初步具有开发BP插件的能力。1 开始开发我们使用IDEA作为开发工具(使用其他IDE也绰绰有余)。引入依赖包&…...

【Zookeeper】介绍与配置
目录 概述 工作机制 特点 数据结构 应用场景 统一配置管理 统一集群管理 编辑 服务器动态上下线 软负载均衡 下载 启动 启动客户端 配置参数 集群配置 配置服务器编号 配置zoo.cfg 分发zoo.cfg配置文件 选举机制 第一次启动 非第一次启动 集群启动停止脚本…...
chrome快捷键
Ctrl T:打开新标签页。Ctrl W:关闭当前标签页。Ctrl Shift T:重新打开最近关闭的标签页。Ctrl Tab:在打开的标签页之间切换。Ctrl Shift Tab:在打开的标签页之间反向切换。Ctrl N:打开新窗口。Ctrl…...

手搓string类
手搓string类 文章目录手搓string类string的成员一.构造,拷贝构造,赋值重载和析构1.构造函数2.拷贝构造3.swap问题4.赋值重载5.析构函数二.简单接口1.c_str2.size(有效字符长度)3.capacity(有效字符容量)4.operator[]5.迭代器和范…...

小白学Pytorch系列--Torch API (7)
小白学Pytorch系列–Torch API (7) Comparison Ops allclose 此函数检查输入和其他是否满足条件: >>> torch.allclose(torch.tensor([10000., 1e-07]), torch.tensor([10000.1, 1e-08])) False >>> torch.allclose(torch.tensor([10000., 1e-…...

函数(上)——“Python”
各位CSDN的uu们你们好呀,今天小雅兰的内容是Python的函数呀,下面,就让我们进入函数的世界吧 首先可以选择性地看一下小雅兰很久之前写的C语言函数章节的知识: 函数——“C”_认真学习的小雅兰.的博客-CSDN博客 函数递归…...
ChatGPT说:如何利用ChatGPT变现?躺着赚钱不是梦。
您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦。 💪🏻 1. Python基础专栏,基础知识一网打尽,9.9元买不了吃亏,买不了上当。 Python从入门到精通 😁 2. 毕业设计专栏&…...

4.网络爬虫—Post请求(实战演示)
网络爬虫—Post请求实战演示POST请求GET请求POST请求和GET请求的区别获取二进制数据爬[百度官网](https://www.baidu.com/)logo实战发送post请求百度翻译实战使用session发送请求模拟登录17k小说网常见问题前言: 📝📝此专栏文章是专门针对…...

【视频文稿】车载Android应用开发与分析 - 开发系统应用
本期视频地址:https://www.bilibili.com/video/BV1NY411z7TK/ 前言 Hello,大家好,我是林栩。 开发车载应用,其实主要都是在Android系统中编写各种系统应用,所以上期视频先介绍了Android系统源码的下载和编译流程&…...

Scala流程控制
目录 单分支 双分支 多分支 for 循环控制 循环守卫 循环步长 循环嵌套 循环返回值 While 和 do..While 循环控制 While循环控制 do..While 循环控制 循环中断 单分支 if (条件表达式) {执行代码块 }var age StdIn.readShort()if (age < 18){println("童年&quo…...

人脸活体检测系统(Python+YOLOv5深度学习模型+清新界面)
摘要:人脸活体检测系统利用视觉方法检测人脸活体对象,区分常见虚假人脸,以便后续人脸识别,提供系统界面记录活体与虚假人脸检测结果。本文详细介绍基于YOLOv5深度学习技术的人脸活体检测系统,在介绍算法原理的同时&…...
prometheus03-如何导出prometheus指标
Prometheus是一个开源的监控系统和时间序列数据库,用于收集和存储服务的指标数据。要导出Prometheus指标,你需要使用或实现一个Prometheus Exporter。以下是一个简单的指南,分为三个主要步骤: 选择或实现Prometheus Exporter Pr…...

Linux驱动开发——串口设备驱动
Linux驱动开发——串口设备驱动 一、串口简介 串口全称叫做串行接口,通常也叫做 COM 接口,串行接口指的是数据一个一个的顺序传输,通信线路简单。使用两条线即可实现双向通信,一条用于发送,一条用于接收。串口通信距…...

LeetCode--缺失的第一个正数(41)和 接雨水(42)
目录 缺失的第一个正数 接雨水 0ms,100% 代码 缺失的第一个正数 来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/first-missing-positive 题目:给你一个未排序的整数数组 nums ,请…...
java源码阅读---ReentrantLock源码解析
ReentrantLock源码解读 在讲ReentrantLock之前我们先看一下Lock接口里的方法 Lock接口中的方法 lock()方法 void lock(); //直接加锁,如果加锁失败什么也不返回lockInterruptibly()方法 void lockInterruptibly() throws InterruptedException;lockInterruptibly()方法能够…...
OpenCv + Qt5.12.2 文字识别
OpenCv Qt5.12.2 文字检测与文本识别 前言 好久没有进行一些相关的更新的了,去年一共更新了四篇,最近一直在做音视频相关的直播服务,又是重新学习积攒经验的一个过程。去年疫情也比较严重,等到解封,又一直很忙&a…...
网络作业1【计算机网络】
网络作业1【计算机网络】前言推荐网络作业1一. 单选题(共7题,58.1分)二. 多选题(共1题,8.3分)三. 判断题(共4题,33.6分)最后前言 2023-3-13 20:11:42 以下内容源自《计…...

常见背包问题
一.前言若你想学习或正在学习动态规划,背包问题一定是你需要了解的一种题型,并且大多数人最初都是从背包问题入坑进而打开动态规划这一大门。背包问题分为多种,你可以先掌握最常见的主要是三类:01背包、完全背包、多重背包二.分析…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...

MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...

FFmpeg avformat_open_input函数分析
函数内部的总体流程如下: avformat_open_input 精简后的代码如下: int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...