大家好,我是旗帜僵尸。今天我将带领大家学习进程的概念。
本篇文章将继续收录于我的linux专栏中,若想查看关于linux其它知识的文章也可以点击右方链接。旗帜僵尸——linux
文章目录
- 一、进程概念
- 冯诺依曼体系结构
- OS(操作系统Operator System)
- 进程的概念
- PCB(进程控制块)
- 关于进程的指令与函数
- 指令
- 函数
- 二、进程状态
- 操作系统层面的进程状态
- LINUX的进程状态
- 总结
一、进程概念
冯诺依曼体系结构
如上图所示,冯诺依曼体系结构将计算机划分为3部分,分别是CPU、存储器、I/O设备
运行速度:CPU>>存储器>>I/O设备
在数据传输时,为了加快程序运行速度,CPU是不会与I/O设备直接交涉的,而是通过存储器作为桥梁来交互。
存储器指系统内存,而磁盘属于I/O设备
OS(操作系统Operator System)
我们从上图来学习OS。
用户希望开发,为了方便用户开发,所以我们有了用户操作接口
而在用户开发时,为了保护底层数据,又有了系统调用接口
通过调用接口,进行各种管理
驱动管理又细分为各个驱动
每个驱动对应着一个底层硬件
从图中看到,操作系统向上便于用户开发,保护隐私数据
向下又管理各种程序
OS作为用户与底层的中间者。其本质是管理各式各样的数据,确保计算机运行稳定。
进程的概念
我们可以看到OS中有对进程的管理。
而进程的本质 = 内核数据结构 + 对应的可执行程序代码。
在运行代码时,我们的程序首先从磁盘中加载到内存。
而此时,内存中除该程序代码外,还要为其创建一个用于描述代码信息的结构体——task_struct.
OS通过这些描述代码的结构体来做出管理。
PCB(进程控制块)
PCB:由task_struct组成的控制模块
可以理解为一个链表,用于管理进程。
tast_struct结构体中的内容大致有如下几类:
示符: 描述本进程的唯一标示符,用来区别其他进程。
状态: 任务状态,退出代码,退出信号等。
优先级: 相对于其他进程的优先级。
程序计数器: 程序中即将被执行的下一条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针.
上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
其他信息
关于进程的指令与函数
指令
ps ajx:查看进程
ps ajx | head -1:输出第一行
演示代码如上
PID:进程标识符
PPID:父进程的标识符
STAY:状态
kill 命令(其有许多选项,现讲解其中几种)
kill -9:杀死进程
kill -19:暂停进程
kill -18:结束暂停
演示代码如上
-9
-19,-18
值得关注的是,当我们暂停一个进程,再运行时,查询它的信息
其状态栏,少了一个+号,+号代表前后台。
有+号,其为前台程序,在执行时,我们不能运行其他指令,可以用ctrl c终止进程
无+号,其为后台程序,在执行时,我们可以运行其他指令,不能用ctrl c终止进程,只能用Kill杀死进程
函数
getpid();
getppid();
通过man查询可知,其返回类型为pid_t(整数类型),第一个返回值是进程的PID,第二个是父进程的PID
演示代码如上
我们发现,其每次运行,PID会变化,而PPID却一直不变
这是因为,我们的进程实际上都是bash(命令行解释器)的子进程,当我们还在使用LINUX时,bash一直在运行,所以其PID不会变化。
fork();
它用于创建子进程,返回值为子进程的PID。
演示代码如上
在这个代码中,出现了一个很奇怪的现象,if的两种情况都打印了
暂时可理解为进程之间互不影响,独立执行。
当我们学习之后的概念与内容就可以更好的理解它。
二、进程状态
操作系统层面的进程状态
对于OS而言,我们有许许多多的代码要执行,也就意味着有许许多多的进程要运行。
其状态诸如运行,新建,就绪,挂起,阻塞,等待,停止,挂机,死亡等等,而我们重点关注运行,阻塞,挂起
运行状态:
CPU在运行代码时,会创建一个运行队列,存放运行程序的各种信息,以及之后要运行的程序
而当进程进入这个队列时,则意味着它处于运行状态,即使它还在等待前面的进程运行结束。
阻塞状态:
当进程被CPU执行时,发现需要调用某个硬件,而该硬件此时还在被别的进程调用。CPU为了避免浪费时间,它会将该进程,放入硬件的等待队列,此时,它的状态便更改为了阻塞。
等硬件可以使用了,CPU会将进程重新纳入运行队列,进行执行。
挂起状态:
内存中,所有的代码与PCB都需要占用一定空间。
而当内存不够用,OS便会将一些处于阻塞状态的进程的代码先释放了,而其PCB仍在等待队列之中。此时,它的状态便是挂起状态。
对于上述状态中的各种队列,它们是对应的模块所创建的结构体,用来与PCB对接,对进程进行管理
LINUX的进程状态
在LINUX中,我们进程的状态被放在一个指针数组里:
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 */
};
R:
运行状态。
S:
睡眠状态。对应阻塞状态。
这个进程运行时,其大部分时间都在等待外设,而运行状态只占很小一部分,所以基本查不到R状态。
D:
深度睡眠状态(这个状态我们无法查询,只能讲解。)
之前我们说过挂起状态。如果一个进程,在向磁盘写入数据,而此时内存紧张,它被挂起了。磁盘写入时发生异常,它在返回异常时,找不到该进程了。磁盘于是就继续执行下一个进程。
这段原本要被写入磁盘的数据也就丢了。
为了避免这种情况,D状态出现了,它标志着,该进程只有断电或者运行结束才能终止。
T:
暂停状态。
手动暂停则状态为T
t:
跟踪暂停状态。
当我们调试代码时,我们设置断点后,再运行。
程序运行至断点处暂停,此时,该进程状态为t状态
X:
死亡状态。
一个进程在结束时,由OS回收它的各种资源,在此时,它处于死亡状态。(时间太短,查询不到)
Z:
僵死状态。
一个进程在结束后,会被回收各种资源,而回收资源的工作一般由其父进程执行。
当子进程结束,父进程还未结束时,它处于僵死状态。
演示代码如上
其子进程结束,COMMAND也发生变化,后缀defunct(失效)
对于OS,若存在大量僵死进程,代表大量内存被占用无法释放,之后我们会讲解如何应对这种情况。
除上述状态外,还有一种较为特殊的进程——孤儿进程:
当一个进程的父进程先结束,该进程无人回收资源,此时它就是孤儿进程。
为了避免子进程结束时无人回收资源,造成内存泄漏,所以OS会让PID为1的进程(root)将其领养
演示代码如上
我们发现,在父进程死亡后,子进程的父进程更改为了1,而它也从前台运行变为后台运行
总结
进程是我们学习LINUX的重点,本篇学习了进程概念与进程状态。
之后将学习有关进程的知识,请继续关注现在想吃🧠的旗帜僵尸,我将会持续更新linux的后续学习内容
下次见