初识进程
- 🍉进程
- 🍌操作系统的进程管理
- 🍉PCB 重要属性
- 🍌进程的身份标识
- 🍌内存指针
- 🍌文件描述符表
- 🍌进程的状态
- 🍌优先级
- 🍌记账信息
- 🍌上下文
- 🍉内存分配(内存管理)
- 🍉进程间通信
🍉进程
进程是操作系统提供的一种软件资源
我们现在用到的系统,都属于是“多任务操作系统”
,即在同一时刻,可以同时运行多个任务
这里所说的“任务”,就是指正在运行中的程序,也可以称为“进程”
与其对应的就是“单任务操作系统”,它在同一时刻只能运行一个程序
打开任务管理器,我们可以看到:每个任务在执行过程中,都需要消耗一定的硬件资源(内存、硬盘等)
也就是说,计算机的每个进程在运行的时候,都需要给它分配一定的系统资源。可见进程是系统分配资源的基本单位
🍌操作系统的进程管理
操作系统在管理进程时,会先使用类/结构体
把实体属性
给列出来(因为操作系统一般是用C/C++实现的,所以可以使用结构体),这一步称为“描述”
。表示进程信息的结构体,称为PCB
(进程控制块,Process Control Block)
每个正在运行或等待被调度的进程都有一个对应的PCB,操作系统通过访问和操作PCB来实现进程的管理和调度
补充:PCB 是操作系统学科中的通用概念。Windows 上表示进程的结构,可以称为 PCB;Linux 上的也可以称为 PCB
具体到 Linux 上,PCB 是一个叫作 struct task_struct{…}这样的结构体
在描述之后,使用一定的数据结构,把这些结构体/对象串到一起,这一步叫作再组织
在 Linux 中,若干个 task_struct 之间是使用链式结构
串起来的
当我们看到任务管理器中的进程时,系统内部此时在遍历链表,并且打印每个节点的相关信息
如果运行一个新的程序,那么系统中就会多一个进程,多出来的这个进程就需要构造一个新的 PCB,添加到链表上
与之对应的,如果某个运行中的程序退出了,那就需要把对应进程的 PCB 从链表中删掉,并销毁对应的 PCB 资源
注意:此处的表述是一个简化版本,实际上组织的方式更加复杂(内部不是一个链表,而是更加复杂的链式结构)
🍉PCB 重要属性
🍌进程的身份标识
不同进程之间是通过一个简单的不重复整数来区分的,称为 pid
。后续要针对某个进程进行操作时,就可以用 pid 来区分
比如选中某进程,点击“结束任务”,那么此时任务管理器就会获取到你选中的进程的 pid,然后调用一个系统 api,把 pid 作为参数传进去,从而完成杀死进程的操作
🍌内存指针
内存指针是用来描述某个进程能使用哪些内存,即描述了进程使用内存资源的详细情况
进程在运行过程中需要消耗一些系统资源,其中内存就是一种重要的资源
如果要使用内存,那就得先向系统申请,得系统给你分配一块内存之后,你才能使用
一个进程要想跑起来,需要有指令(代码),也需要有数据,而指令和数据都是要加载到内存中的,所以进程需要知道指令和数据分别存在哪里
而内存指针保存着一个内存地址,指向内存中的特定位置。通过内存指针,可以访问和操作内存中的数据和指令(代码)
🍌文件描述符表
文件描述符表是操作系统中的一个数据结构,它描述了某个进程所涉及的硬盘相关的资源
进程经常需要访问硬盘,操作系统对于硬盘这样的硬件设备进行封装,按照文件
的方式来操作(这里的“文件”,指的就是我们电脑里面的文件,它们是存在硬盘中的)
一个进程要想操作文件,就需要先打开文件,这个过程就是在文件描述符表中给这个进程分配一个表项(构造一个结构体),表示这个文件的相关信息
前面我们说进程是系统分配资源的基本单位,像内存、硬盘、网卡等资源就会在 PCB 中有所体现,而且它们在PCB 中都好体现。但是一个进程消耗 CPU 资源,这个要如何看待呢?
一个 CPU 可能有一个核心,也可能有多个,一个核心在同一时刻只能处理一个进程,但是系统上的进程远多于电脑的核心数,此时就涉及到一个关键的概念——分时复用(并发)
就是 CPU 的核心先执行进程1的代码,执行一段时间之后,让进程1下来,换进程2上去……依此类推
而 CPU 一秒可以执行几十亿条指令,也就是说在短时间内可以进行很多次任务切换,在切换进程的速度足够快的情况下,人是感知不到这个切换的过程
而如果是多核 CPU,比如四核,那么就可以同时执行四个不同的进程,这称为并行执行
。虽然有多个核心,但每个核心仍然要分时复用,快速切换
当代计算机,基本是并行和并发同时存在的,两个进程是并行执行还是并发执行,要看系统的调度
我们把并行和并发统称为“并发”
,对应的编程方式(解决一个问题,分成多个任务来执行,这多个任务协作解决)称为并发编程
说到这里,再回到上面的问题,一个进程消耗 CPU 资源代表该进程在 CPU 上消耗的时间
任务管理器中是按百分比算的,就表示某进程在 CPU 上消耗的时间的百分比
如果某个进程把 CPU 占到 100%,那就意味着其他进程都没有执行的时间,相当于这个进程霸占了 CPU,这时候很可能会造成系统卡顿
为了避免发生这种情况,就需要对进程进行调度,使各个进程可以顺利并发执行。PCB 中提供了一些属性来支持系统调度进程,下面我们来看一下
🍌进程的状态
所谓的状态,是描述某个进程能否到 CPU 上执行,主要有两个状态
就绪状态:进程随时准备好到 CPU 上执行
阻塞状态:进程当前不方便去 CPU 上执行,不应该去调度它,比如进程在等待 IO
这里所说的 IO,可能是来自控制台的输入/输出(比如等待 Scanner 的输入,也可能是硬盘的输入/输出、网卡的输入/输出)
🍌优先级
有多个进程等待系统调度,这些进程获得的资源和调度顺序是有优先级的。优先级越高,获得资源的机会就越大,同时一般会优先执行
比如电脑上在运行游戏和微信,显然游戏的优先级更高
🍌记账信息
针对每个进程占据多少 CPU 时间进行统计,然后根据统计结果进一步调整调度的策略。这样做是为了确保每个进程不会出现完全没有到 CPU 上执行的情况
🍌上下文
每个进程在运行过程中,会产生一些中间结果
,这些结果会保存在 CPU 的寄存器
中。因此,在进程调度出 CPU 之前,需要把当前寄存器中的信息单独保存到一个地方。在该进程下次去 CPU 上执行的时候,再把这些寄存器的信息恢复回来
保存上下文:把 CPU 的关键寄存器中的数据,保存到内存中(PCB的上下文属性中)。相当于游戏中的存档操作
恢复上下文:把内存中关键寄存器中的数据,加载到 CPU 对应的寄存器中,使之前的进程能够继续执行。相当于游戏中的读档操作
🍉内存分配(内存管理)
核心结论:每个进程的内存是彼此独立、互不干扰的
也就是说,通常情况下进程A不能直接访问进程B的内存(这也称为进程独立性
),这样做利于系统的稳定性。
如果某个进程的代码出 bug 了,那么只会影响到当前这个进程,不会影响到其他进程
🍉进程间通信
虽然进程之间有独立性,但是有时候也需要多个进程相互配合完成某个工作,所以进程间需要进行通信
进程间通信和进程的独立性不矛盾,因为系统会提供一些公共的空间
(就是多个进程都可以访问到的),让两个进程借助这种公共空间来交互数据
操作系统提供了多种方式供进程进行通信,但本质上都是上述思路
常用的方式有:
- 文件:Java 程序员主要使用的进程间通信方式
- 网络:可以支持同一个主机的不同进程,也能支持不同主机的不同进程(适用性更高)
- 管道
- 共享内存
- 信号量
- 信号