Bootstrap

对于进程(process),你真的了解吗?

目录

1、操作系统

1.1 什么是操作系统

1.2 操作系统的功能

2、进程(process)

2.1 什么是进程

2.2 管理进程

3、PCB(Process Control Block)

3.1 pid

3.2 内存指针

3.2.1 操作系统和CPU的关系

3.3 文件描述表

4、进程调度

4.1 并发

4.1.1 单任务操作系统

4.1.2 多任务操作系统

4.1.3 并发执行与并行执行

4.2 进程状态

4.3 进程优先级

4.4 进程的上下文

4.5 进程的记账信息


1、操作系统

1.1 什么是操作系统

操作系统是计算机中最重要的“软件”,是⼀组做计算机资源管理的软件的统称。目前常见的操作系统有Windows、Linux、Mac OS、Android、iOS等等。

系统与系统间最直接的区别是两者中的系统提供的API(系统函数)不同。

对于我们Java程序员来说,在后端开发的过程中,服务器系统用到的就是Linux系统。

  1. 使用Linux,一般都是命令行操作(命令);
  2. 使用Windows一般使用的是图形化界面的操作(Linux也有图形化界面,但开发中一般不会使用)。

使用图形化界面,可以降低门槛;

而使用命令行界面,才是生产力的工具;所以这是Linux成为后端开发主流系统的主要原因。 

1.2 操作系统的功能

我们上文说到,操作系统是一个软件,更确切的说,操作系统其实是一个搞管理的软件,具有以下两点重要功能:

  • 管理各种硬件设备

操作系统通过驱动程序,间接管理硬件设备。驱动程序/软件,是和硬件配套的软件,由研发硬件的人提供的,例如:我们购买的蓝牙键盘,需要下载厂商提供的驱动软件才可以使用键盘全部的功能。

  • 给各种应用程序,提供一个稳定的运行环境。

就是在说,在各种应用程序工作的过程中,他们之间是互不干扰的。例如:即使某个应用程序给崩溃了,也不会影响到其他应用程序的运行。

对于我们Java程序员来说,我们需要着重了解第二点功能——给各种应用程序,提供一个稳定的运行环境。

说到这里,就需要引出操作系统中的一个重要概念——进程(process)。

2、进程(process)

2.1 什么是进程

进程,其实就是一个运行起来的程序。

我们可以查看任务管理器,上面显示所运行的程序都是一个进程。

如上图所示,对于现代的计算机来说,同时跑上百个进程是很常见的一个事情。

2.2 管理进程

我们上文说道,作为Java程序员,我们需要额外关注操作系统给各种应用程序,提供一个了稳定的运行环境。

而一台电脑上同时跑这么多进程,操作系统为了保障稳定的运行环境,避免乱套,就需要对这么多的进程进行很好的管理。

那操作系统是如何管理进程呢?

首先举个学校中的例子:

在学校中,假如有个200号人的大班,班里的人很多,如何将所有的同学很好的管理起来呢?

  1. 因为每个同学都有自己的学号、姓名、年龄、宿舍、....等信息,——所以我们可以将这些属性抽象成一个类(对象)或者结构体,使用结构体将信息保存起来。
  2. 将属性抽象成类后,再将每个同学对应的对象组织起来(以数据结构的方式组织数据,比如使用:数值、链表、哈希、数据库中的表、...)

综上,我们可以以 先描述出核心属性+使用数据结构再组织数据 的方式,以编程思维解决现实问题。

同样,站在操作系统的角度,同样也是以先描述再组织的方式来管理进程的:

  • 先描述出一个进程是啥样的——使用结构体,描述出进程的核心属性。而在操作系统中,这个结构体就叫做:进程控制板块(PCB,即Process Control Block),这是一个非常大的结构体,里面包含了进程很多很多的属性。
  • 再使用数据结构将多个进程组织起来。比如Linux操作系统,其底层就是以链表的形式,将多个PCB(进程)串到一起的。
  • 也就是说:管理 = 描述 + 组织

为什么这里就只说将属性抽象成结构体,而不是一个类或者对象呢???

——因为主流的操作系统都是使用C语言写的!(Linux纯C,Windows大部分使用C外加一点点C++)

在有对以上知识的理解后,那我们就可以理解进程的相关操作:

  • 创建一个新的进程(双击.exe,运行一个应用程序)

创建一个PCB,初始化PCB中的各个属性,将这个PCB插入到链表中。

  • 销毁一个进程(关闭一个应用程序)

找到这个进程的PCB在链表上的位置,将这个PCB从链表上删除。

  • 查看进程列表(查看任务管理器)

遍历链表,取出链表上的每一个元素,将里面信息显示到界面上。

3、PCB(Process Control Block

上文说到,PCB是一个描述进程属性的结构体,而在这个结构体中包含如下重要属性。

  1. pid(进程id)
  2. 内存指针
  3. 文件描述表
  4. 进程状态
  5. 进程的优先级
  6. 进程的上下文
  7. 进程的记账信息

3.1 pid

pid,即进程的“身份标识符”,再同一时刻,每个进程的pid都是不同的。

3.2 内存指针

内存指针指的是一组指针。其中保存了进程所要执行的指令的地址,以及指令所依赖的数据的地址。

举个例子:

比如一个bilibili.exe文件,

 在这个可执行文件中,其中包含了

  1. 哔哩哔哩这个程序在运行过程中需要执行的指令(二进制的)。(.exe是程序员写代码生成的,程序员写的代码最终都被编译成了二进制指令,包含在了.exe中)
  2. 这些指令在运行的过程中,所依赖的数据。

而我们双击这个.exe文件,创建一个进程,那么操作系统在加载的过程中,就会把这个进程所需执行的指令和指令所依赖的数据加载到内存中,而PCB中的内存指针中就保存了指令以及指令所依赖的数据的地址。后续,进程在运行的过程中,就会根据内存指针保存的地址从内存区域里一条的取指令并进行执行。

指令,指的是计算机能够读懂的指令,即二进制的机器语言。

指令包含:操作码(opcode)+操作数

CPU执行指令的过程:

  1. 取指令
  2. 解析指令
  3. 执行指令

3.2.1 操作系统和CPU的关系

由于操作系统本身也是一个软件,也是由一系列的指令构成的。而软件本质上都是靠CPU执行指令才执行的,所以CPU依次执行操作系统上的这些指令,操作系统才跑起来。

但是话又说回来了,

如果运行某个进程,操作系统会进行加载指令和指令所依赖数据到内存的过程,再指示CPU执行这些指令~

所以,如果不考虑操作系统的底层细节,说是操作系统指示CPU执行哪些指令的说法是没问题的;但是考虑细节,操作系统本身也是个软件,操作系统又是因为CPU才跑起来的。

3.3 文件描述表

进程在运行的过程中,在很多时候,都是需要和硬盘来进行交互的,

举个例子: 

我们使用网易云音乐听歌时,每播放一首新歌曲,这首歌就会以文件的形式被缓存到硬盘上存储起来,所以当我们下次听这首歌时就会直接播放。

而硬盘上的数据,都是以文件的形式进行保存的,进程在读写文件的时候,就需要先打开文件,同时每打开一个文件,这个文件的信息就会被保存到文件描述表中,表中的每个项,就对应着一个打开的文件。

在操作系统中,不仅是硬盘上的资源,很多资源都会被抽象成文件来表示,比如:网卡。操作系统在管理网卡时,就是将网卡当中文件一样来进行管理。

综上,

  1. 进程的运行,会依赖到硬盘、网卡等相关的资源设备。
  2. 进程的运行,都是考CPU来执行指令的,所以进程也需要销毁CPU资源。
  3. 故,进程是操作系统中,资源分配的基本单位。(CPU将硬盘、网卡等资源分配给进程,进程再根据自身情况细分资源)

4、进程调度

在上文中,讲解了进程(PCB)的三个重要属性——pid、内存指针、文件描述表。

接下来,我们一起来探索PCB的进一步的关键属性:

  1. 进程状态
  2. 进程优先级
  3. 进程的上下文
  4. 进程的记账信息

而这四个属性共同支撑着进程调度进程调度,也是操作系统在进行进程管理过程中,需要完成的重要工作。

因为需要并发执行,所以操作系统需要进行进程的快速切换,也就需要分析每个进程的状态,对每个不同的进程间分配不同的资源,.....,这就是“进程调度”。

想要理解进程调度,我们需要先了解——并发。

4.1 并发

我们知道,一台电脑上,同时跑着上百个进程,而我电脑的只有12个CPU逻辑核心,那这12个干活的牛马是怎么同时执行上百个任务呢?

4.1.1 单任务操作系统

其实早期的计算机的操作系统,都是“单任务”操作系统,只能串行执行。

也就是说,同一时刻,只能执行一个进程(只能运行一个程序),想要运行另一个程序就必须把上一个程序关掉。

比如:你登QQ就不能刷抖音,你看小说就不能听音乐。

4.1.2 多任务操作系统

多任务操作系统,在有多核处理器之前,多任务操作系统就已经有了。

多任务操作系统,即使CPU只有一个核心,也能“同时”运行多个进程。

但是真的是能同时运行多个进程吗???——其实不是的。

其实这里是由于“分时复用”的原因:

  1. 把一个单位的时间分成很多份
  2. 第一份,运行进程1的指令
  3. 第二份,运行进程2的指令
  4. 第三份,运行进程3的指令
  5. ......

由于CPU执行的速度够快,1s可以执行39亿次计算,程序间的切换也会非常的快,快到超出人类反应的时间,使人感觉这些进程在“同时执行”一样。

 

4.1.3 并发执行与并行执行

  • 并发执行:一个CPU核心上,按照“分时复用”的原则,执行多个进程。(人看起来是同时执行的,微观上,其实是一个CPU在串行执行,只不过执行速度极快)
  • 并行执行:多个CPU核心,同时执行多个进行。(确实是实实在在的“同时执行”)

现代的CPU在执行进程的时候,是并发和并行执行同时存在的: 

  • 多个CPU核心进行并行执行,而其中每个单个的CPU又并发执行。

由于我们程序员在代码的时候,是无法区分当前的进程是并发执行还并行执行的,所以把并发和并行,统称为“并发”。

4.2 进程状态

进程有很多种状态,作为Java程序员,我们需要关注其中最典型的两个状态:

  • 就绪状态(在操作系统中,不分就绪和执行,就绪状态包括了正在执行和等待执行)

可以理解为“随叫随到”,进程可以随时到CPU上执行。

  • 阻塞状态

即进程当前不能够(或者说不适合)到CPU上去执行。

4.3 进程优先级

我们知道,一台电脑上,同时执行了上百个进程,可是这么多进程,他们能够去CPU上运行机会都是相等的吗???——当然不是啦。

有些进程,他们的优先级就要高一些,就会吃到更多的CPU资源。

举个例子:此时,我正在玩一款大型电脑游戏,还登着QQ,这时CPU就会分配更多的资源给到这个游戏程序中,保证我游戏玩的流畅(即使QQ吃的资源少,延迟几秒收到消息也是没关系的)

注意:一个进程的优先级,不是因为你窗口显示在上面优先级就高,窗口显示在下面优先级就低。

4.4 进程的上下文

假设一个进程正在CPU上运行,此时,CPU突然离开了,过了一会CPU又回来了,那么此时,CPU应该接着沿着上次离开时的位置继续执行该进程,而不是重新从头执行。

而,记录的上次执行的状态和进展的内容,就是“上下文”。

而这些“上下文”是怎么被记录起来的呢?

其实,当进程在CPU中运行的过程中,寄存器会记录当前进程执行的“中间状态”:

  1. 保存上下文:当CPU离开时,寄存器中存储的“中间状态”会被加载到内存中(写入PCB的相关属性中)。
  2. 回复上下文:当CPU回来时,PCB中属性保存的上下文信息,会重新写回到寄存器中,使得CPU继续执行原来的位置。

4.5 进程的记账信息

属性实现的就是统计功能,即统计进程在CPU上运行了多久,吃到了CPU多少的资源等等。

当发现某一个进程好久没有吃到CPU资源时,这时就会给该进程倾斜一些资源(及时进行补救)。

综上,CPU会根据 进程状态、进程优先级、进程上下文、进程的记账信息 这四个属性,来制定出操作系统的并发执行方案(可以理解为执行时间表),这就称为进程调度。 


END

;