持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情
三、进程 (process)
💦 概念
- 课本概念:进程就是一个运行起来的程序。
- 内核观点:进程就是担当分配系统资源 (CPU 时间、内存) 的实体。
当然对于进程的理解不能这么肤浅,我们接着来了解一下 PCB。
💦 描述进程 - PCB
- 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
- 课本上称之为
PCB (process control block)
,Linux 操作系统下的PCB
是task_struct
。 - 在 Linux 中描述进程的结构体叫做
task_struct
。task_struct
是 Linux 内核的一种数据结构,它会被装载到RAM(内存)
里并且包含着进程的信息。
📝说明
-
List item
test.c 文件在运行前是一个普通的磁盘文件,而要运行它,就必须先加载到内存中,此时 OS 中就增加了一个需要管理的进程。
-
List item
OS 能否一次性运行多个程序 ❓
当然可以的。
-
List item
正如校长和学生的例子,OS 如何管理运行起来的程序 ❓
先描述,在组织 !!!
管理进程不仅仅是把磁盘加载到内存里 (这只是第一步),其次还会在 OS 中创建一个描述该进程的结构体,这个结构体在操作系统学科或 Linux kernel 中叫做 PCB(进程控制块),说人话就是在 Linux 下这个进程控制块是用 struct (因为 Linux kernel 是用 C 语言写的) 来描述的
task_struct
。其中被加载到内存中的程序就是学生,PCB 就是描述学生的属性信息。将来这些 PCB 是一定能够帮我们找到对应代码和数据的,就如同学校系统中是一定包含你的个人信息的。其次进程多了之后,操作系统为了更好的管理,需要使用 “ 双向循环链表 ” 将所有的 PCB 进行关联起来。
所以本质我们在 Linux 中 ./a.out 时主要做两个工作,其一先加载到内存,其二 OS 立马为该进程创建进程控制块来描述该进程。OS 要进行管理,只要把每一个进程的 PCB 管理好就行了,对我们来讲,要调整一个进程的优先级、设置一个进程的状态等都是对 PCB 进行操作。
💨小结:
描述:每个进程对应的 PCB 几乎包含了进程相关的所有属性信息。
组织:OS 对进程的管理转化成了对进程之间数据结构的管理。
所以站在程序员以更深入的角度来看待进程就是等于:你的程序 + 内核申请的数据结构(PCB)。
💦 task_ struct 内容分类
-
标示符 PID:描述本进程的唯一标示符,用来区别其他进程。
ps ajx
,查看系统当前所有进程。 -
状态:任务状态,退出代码,退出信号等。
稍后我们会见到 Linux 进程的具体状态,细节下面再说。
-
优先级:相对于其他进程的优先级。
比如去食堂干饭,需要排队,而排队就是在确定优先级,这口饭你是能吃上的,只不过因为排队导致你是先吃上,还是后吃上,所以优先级决定进程先得到资源还是后得到资源。在排队打饭时有人会插队,本质就是更改自己的优先级,你插队了,就一定导致其它人的优先级降低,对其它人就不公平,所以一般不让插队。其中 CPU、网卡等等同于食堂的饭,进程等同于要干饭的人。
为啥需要排队 ❓
也就是说为啥要有优先级呢 ?假设世界上有
无限的资源
,那么就不会存在优先级
了。而这里因为窗口太少了,所以优先级是在有限资源(CPU、网卡等) 的前提下,确立谁先访问资源
,谁后访问
的问题。所以优先级存在的本质是资源有限。 到目前为止,除了进行文件访问、输入输出等操作,大部分所写的代码竞争的都是 CPU 资源,比如说遍历数组、二叉树等,最终都会变成进程,然后竞争 CPU 资源,而我们后面需要竞争网络资源。优先级 and 权限有什么区别 ❓
优先级
一定能
得到某种资源,只不过是时间长短问题;而权限是决定你能还是不能
得到某种资源。 -
程序计数器 epi:程序中即将被执行的下一条指令的地址。
CPU 运行的代码,都是进程的代码,CPU 如何知道,应该取进程中的哪条指令 ❓
我们都知道语言中一般有三种流程语句 a) 顺序语句。 b) 判断语句。c) 循环语句。一般程序中默认是从上至下执行代码的。
在 CPU 内有一个寄存器,我们通常称之为
eip
,也称为pc
指针,它的工作是保存当前正在执行指令的下一条指令的地址。当进程没有结束,却不想运行时,我们可以将当前 eip 里的内容保存到 PCB 里(其实不太准确,这里只是先为了好理解,后面知识储备够了,再回头校准),目前是为了恢复,具体细节后面会谈。你说 eip 是指向当前正在执行的下一条指令的地址,那么第一次 eip 在干啥 ❓
这里是属于硬件上下文的概念,下面在谈进程切换时再学习。
-
内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
CPU 不能直接去访问代码,需要通过 PCB 去访问代码。内存指针可以理解为它是代码和进程相关数据结构的指针,通过这些内存指针可以帮助我们通过 PCB 找到该进程所对应的代码。
-
上下文数据:进程执行时处理器的寄存器中的数据。
其中寄存器信息可以通过 VS 集成开发环境下查看:
代码 ➡ 调试 ➡ 转到反汇编 ➡ 打开寄存器窗口。
我们常说的什么多核处理器,如四核八线程,注意它不是指 CPU 里的控制器,而是 CPU 里的运算器变多了,所以它计算的更快。后面我们会听过一个概念叫超线程,它其实是 CPU 开启了 并发指令流
的一种技术,所以它就允许有多种执行流在 CPU 上同时跑。
进程快速切换 && 运行队列 ❓
比如你是一名大二的学生, 已经上了二十几节课了,但因为身体原因,需要休一年的学,于是你就走了,而当你一年后回来时,你发现你能挂的科都已经挂完了,甚至你已经被退学了,原因是学校的资源都给你分配着呢,但因为你的一走了之,且没有跟导员打招呼而休学。所以正确方式是在你休学前,你应该跟导员打招呼,待导员向上级申明并把你当前的学籍信息(你大几、挂了几科、累计学分、先把当前正在学习的课程停了) 保存后,才能离开,一年后,你回来了,但是你在上课时并没有你的位置,老师点名册上也没有你的名字,根本原因是你没有恢复学籍,你应该跟导员说恢复学籍,然后把你安排到对应的班级,此时你就接着上次保存学籍的学习状态继续学习。
也就是说当一个进程运行时,因为某些原因需要被暂时停止执行,让出 CPU,此时当前 CPU 里有很多当前进程的临时数据,所以需要在 PCB 里先保存当前进程的上下文数据,而保存的目的是为了下一次运行前先恢复。所以对于多个进程,一个运算器的情况下,为了实现伪并行,进程对应的时间片到了,就把进程从从 CPU 上剥离下来,在这之前会把上下文数据保存至 PCB,然后再换下一个进程,在这之前如果这个进程内有曾经保存的临时数据,那么它会先恢复数据,CPU 再运行上次运行的结果,这个过程就叫做 上下文保存恢复
以及 进程快速切换
。
系统里当前有 4 个进程是处于运行状态的,此时会形成运行队列 (runqueue)
,它也是一种数据结构,你可以理解为通过运行队列也能将所有在运行的 PCB 连接起来,凡是在运行队列中的进程的状态都是 R
,也就是说每一个 PCB 结构在操作系统中有可能是链表,也有可能是队列,这个 PCB 里面会通过某种方式包含了大量的指针结构。注意以上所有的结构都是在内核中由操作系统自动完成的,这其中细节很多,后面每个阶段我们都会对细节进行完善,其次还包括阻塞队列
、等待队列
会再详谈。
-
I/O状态信息:包括显示的 I/O 请求,分配给进程的 I/O 设备和被进程使用的文件列表。
白话就是哪些 I/O 设备是允许进程访问的。
-
记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
白话就是你的一个进程,在调度时所使用的时间、在切换时切换的次数等。
记帐信息的意义 ❓
现实中也存在 “ 记帐信息 ”,也有一定的意义,比如每个人的年龄,每过一年,第人都会增长一岁,那么不同人累计下来的 “ 记帐信息 ” 值不同时,会有不同的状态,如六个月,你不会走路;六年,学习;二十四年,工作;八十年,有人主动让座。所以对系统来讲可以通过 “ 记帐信息 ” 来指导系统,比如有 2 个优先级相同的进程,一个累计调度了 10 秒钟,另一个累计调度了 5 秒钟,下一次则优先调度另一个进程,因为调度器应该公平或较为公平的让所有进程享受 CPU 资源。
调度 ???
调度就是在从多的进程中,选择一个去执行,好比高铁站,你能准时准点的坐上高铁,根本原因是高铁站内部有自己的调度规则。
-
其他信息。
💦 查看进程
通过系统调用获取进程标示符 ❓
- 进程 id:
PID
- 父进程 id:
PPID
我们可以使用 man 2 getpid/getppid
命令来查看人生中第一个系统调用接口:
代码一跑起来就查看当前进程的 pid and ppid
:
当然我们也可以查看当前命令行进程的父进程:
父进程和子进程之间的关系就如同村长家的儿子指明道姓要找王婆找如花媳妇,可是如花已经跟李四跑了,王婆一看生意没法做,风险太大,此时王婆就面临着两难,其一,张三是村长的儿子;其二,如花已经跟李四跑了。所以王婆就在婚介所招聘有能力说这桩媒的媒婆实习生,王婆不自己去,而让实习生去。如果事说成了,王婆脸上也有光,如果事没说成,那么对王婆也没影响。 同样的 bash 在执行命令时,往往不是由 bash 在进行解释和执行,而是由 bash 创建子进程,让子进程执行。所以一般情况我们执行的每一个命令行进程都是命令行解释器的子进程。其细节,后面再谈。
其它方式查看进程 ❓
-
可以使用
top
命令来查看进程,类似于 Windows 下的任务管理器,一般用的少。 -
可以使用
ls /proc
命令来查看,proc 在 Linux 的根目录下。
如果要查看对应进程的信息,可以使用 ls/proc/pid -al
命令:
接着我们再看下 1号进程
:
当然因为权限问题有部分进程不让我们看,我今天还非看不可,直接换 root 用户(我这里就拎两个看得懂的进程出来):