一.进程管理
一个运行起来的程序就是进程,点开一个软件的.exe可执行文件,这个程序就跑起来就产生了一个进程。我们可以打开任务管理器 - 首页进程,可以看到3个正在使用的应用,还有71个后台进程。
对于多进程,需要进行管理。进程管理大致包括两个方面:
- 描述一个进程: 使用结构体或类,把一个进程有哪些信息表示出来
- 组织这些进程: 使用一定的数据结构,把这些结构体或对象放到一起
二.描述进程——PCB的相关属性
PCB(Process Control Block)是进程控制块,每一个进程均有一个PCB,它随进程的创建而出现,随进程的结束而消失。 PCB用于描述进程的当前情况和控制进程运行的全部信息,下面总结PCB的几个核心属性:
pid: 每个进程的唯一身份标识
内存指针: 当前这个进程使用的内存是哪一部分
文件描述符表: 进程每打开一个文件,就会产生一个文件描述符,用来标记这个被打开的文件,一个进程可能打开很多文件,对应一组文件描述符,把这些文件描述符放到顺序表里,构成文件描述符表。
( 此处的文件指的是在硬盘上存储的数据,这些数据往往以"文件"为单位进行整理,并非"文件夹"中"文件"的意思。)
另外,PCB还包括一系列与CPU资源相关的属性, 以便辅助进行进程调度:
进程状态:简单分为就绪态(该进程随时可以上CPU执行)和阻塞态(该进程暂时无法上CPU执行)
进程的优先级:优先级高的进程先调度去执行
进程的上下文:存取和读取上下文的过程可以简单理解为存档读档,上下文是指进程运行过程中CPU内部的一系列寄存器的值。 进程离开CPU,将寄存器的值保存到PCB的上下文字段(存档),进程回来CPU, 将PCB中的值又恢复到寄存器(读档)。 寄存器最典型的作用是: 保存当前进程的中间结果, 包括进程运行到哪一条指令。
进程的记帐信息:统计每个进程在CPU上执行了多久,可以作为进程调度的依据
并行+并发
每个程序,相当于一组“二进制指令”集合,集合就在CPU上运行。CPU有一个概念:核心数。打开任务管理器 - 性能,可以看到此计算机的核心数。内核(8)逻辑处理器(16)即为该计算机为 8 核 16 线程,有 8 个物理核心、16 个逻辑核心,可以理解为有8个核心,但是每个核心一个顶俩,所以有16个核心。
对于文章开头所提到的,计算机在同一时刻,正在运行的应用+后台进程将近一百多个进程,但是只有16个核心来处理这些进程,明显任务多干活的少,要同时运行这么多进程,如何来实现呢?这里需要提到并行和并发两个概念。
并行:同一时刻,两个核心,同时执行两个任务(进程)。
并发:一个核心,先执行进程1,执行一会儿后去执行进程2,执行一会儿进程2再去执行进程3......只要切换速度足够快,就可以认为进程1、2、3...是“同时”执行。
通过合理地安排进程 并行和并发 ,计算机就可以同时处理一百多个进程了。另外还有一点,因为 并行+并发 是完全由操作系统进行控制的,宏观上感知不到,所有也把 并行+并发 统称为 并发 。
三. 组织进程——双向链表
操作系统使用双向链表来组织PCB:
- 创建一个进程:创建一个链表的节点
- 销毁一个进程:删除链表的节点
- 遍历进程列表:就是遍历链表
四.内存分配
操作系统给进程分配的内存,是“虚拟地址空间”。每个进程访问的内存地址,都不是真正的物理内存的地址。
若进程直接访问物理内存的地址,如果进程有异常,就会出现问题。比如进程1出现bug(数组下标越界、野指针),就可能引起进程2的内容也出现问题,因为进程1的bug引起进程2的bug,对操作系统的稳定性产生极大的威胁。
而如果给进程分配的是虚拟地址空间,都是固定范围,比如进程1和2能访问的都是虚拟地址0x00-0xff,建立一个页表将虚拟地址映射到页表上,再把页表上的地址映射到物理内存,进程1对应0xaa00-0xaaff,进程2对应0xbb00-0xbbff。 站在两个进程的角度,进程中的代码操作的内存地址都是0x00-0xff,访问的这个地址就会被操作系统自动映射到真实的物理内存上,进程自身感知不到实际的物理地址。
任何一个内存操作,都需要通过页表翻译一下,对其进行校验,在此加持下,进程就有了"独立性".