操作系统
操作系统是一组做计算机资源管理器的软件的统称, 目前常见的操作系统有: Windows, Unix, Linux, OSX, Android, IOS, Harmony等.
操作系统相当于一个大型的计算机软件,是一个非常复杂的软件, 对下管理好各种硬件设备, 对上给软件提供稳定的运行环境.比如某个软件要使用硬件设备, 此时就需要通过操作系统, 然后间接的控制硬件设备, 操作系统在他们之间起到了相互协调的作用.
操作系统里面有很多功能, 其中有一个非常重要的功能, 也和我们每个软件工程师密切相关的功能模块, 叫做进程管理
什么是进程?
进程(process) / 任务(task) , 如图
这是一个带有 .exe后缀的文件, 被称为可执行文件,也被称作程序, 他就是一个文件在你的硬盘里面躺着, 没有进行任何操作. 但是你双击这个文件执行这个程序后, 那么这个程序就运行起来了, 那么就在系统中形成了其对应的进程,例如我们可以查看任务管理器里面的进程标签页:
如果你再任务管理器里面关闭了这个进程, 那么对应的应用窗口就会消失(当然有的进程是在后台运行的, 没有窗口显示), 你下次想使用这个应用的时候, 你就需要再次去执行那个可执行文件, 让程序运行起来.
这就是所谓的进程
进程管理
当进程多了的时候, 就需要我们去管理这些进程, 这就叫做进程管理
进程管理, 分两步:
描述一个进程, 使用 结构体(C) / 类(Java), 把一个进程有哪些信息, 表示出来.
组织这些进程, 使用一定的数据结构, 把这些 结构体或者是对象, 放在一起管理.
接下来详细讲讲进程的结构体
进程结构体(PCB)
进程结构体, 有一个专有名词叫做PCB(process control block进程控制块), 这个结构体里面有很多属性, 我们之讲最核心的几个:
pid, 每个进程需要有一个唯一的身份标识, 类似于数据库里面表的主码.
内存指针, 表明当前这个进程使用的内存是哪一部分, 进程要运行起来, 就需要消耗一定的硬件资源, 比如内存!
文件描述符表, 文件:比如硬盘上存储的文件, 往往就是以文件为单位进行整理的~ 进程每次打开一个文件, 就会产生一个文件描述符, 一个进程可能会打开很多文件, 对应这一组文件描述符, 把这些描述符存储在一个顺序表一样的结构里面, 就构成文件描述表.
进程的运行需要从操作系统这里申请资源, 进程是操作系统进行资源分配的基本单位, 此处的资源包括但是不限于:内存, 硬盘, CPU等
接下来的一组属性都是描述和CPU资源相关的属性, 这些属性都是辅助进行进程调度. 每个程序相当于一组二进制指令集合, CPU有个很关键的概念, 叫做核心数和逻辑处理器, 举个例子, 这里有个处理器的参数如下, 其拥有12核心数和16线程, 但是我们后台一般有100多个进程, 那么他是怎么来处理的呢?.
于是就有了并发和并行的概念.
并行: 同一时刻, 两个核心, 同时执行两个进程, 此时这两个进程就是并行执行的.
并发: 一个核心, 先执行进程1, 执行完之后再去执行进程2, 以此类推, 只要这里的切换速度足够快, 这里的进程1,2,....就看起来像是"同时"执行, 这样就可以利用较少的线程数来运行很多个进程.
很多时候把并行和并发统称为 并发
这就有了后面的进程调度
进程调度
下面这四个属性来支持进程的调度(这四个内容也是PCB中的属性)
进程状态:
就绪态: 该进程已经准备好, 随时准备进CPU执行
阻塞态: 该状态暂时无法进CPU执行
进程优先级
进程之间的调度, 他不一定是"公平的", 有的需要优先调度, 根据需要对不同的进程进行优先级分配.
进程的上下文
上下文就是描述了当前进程执行到哪里的"存档记录", 进程在离开CPU 的时候就要把运行的中间结果进行"存档记录", 等下次进程回到CPU的时候, 进行一个"读档"的操作, 让这个进程在上次的结果位置继续执行.
上下文, 具体就是指进程运行的过程中,CPU内部一系列寄存器中存储的值. 进程离开CPU, 就需要把这些值保存到PCB(进程结构体)的上下文字段当中去.
进程的记账信息
统计了每个进程, 在CPU上执行了多长时间, 有了这个统计之后就可以作为进程调度的参考依据.
组织PCB的数据结构
操作系统往往使用双向链表, 这样的结构来组织PCB!
创建一个进程, 就是创建一个链表的节点.
销毁一个进程, 就是把一个链表的节点给删除了.
遍历进程列表就是遍历整个链表.
内存分配-内存管理
操作系统给进程分配的内存, 是以"虚拟地址空间"的方式进行分配的, 每个进程访问的内存地址都不是真实的内存地址. 如图, 真实的内存地址都是存放在内存颗粒里面的.
下图中的PCB不是值得进程结构体,而是 PCB( Printed Circuit Board),中文名称为 印制电路板,又称印刷线路板,是重要的电子部件,是电子元器件的支撑体,是电子元器件电气相互连接的载体
假设如上图所示, 进程1,2都是直接使用的物理上的内存空间, 这个时候就会产生一个非常重要的问题, 玩意进程1出现bug, 他修改勒0xaa00 - 0xaaff以外的内存(比如数组下表越界, 野指针), 这个时候就有可能会干扰到进程2或者是其他进程的运行, 这就严重影响了操作系统的稳定性.
于是就是用页表的形式来限制这种情况的发生, 如上图, 从这两个线程的角度来说, 他们内存地址就是0x00-0xff这个区间, 这里访问的内存就会被操作系统自动映射到真实的物理内存上, 但是进程自身感知不到这个实际的物理地址是啥, 也就无法影响到物理内存上的其他进程. 这个时候如果进程1出现bug想要修改它自己内存区域以外的内存, 那么就无法通过页表的翻译, 拿着违法的地址是在也标上不存在的, 也就是说无法通过页表的翻译, 也就无法真正修改物理内存, 就不会对别的进程造成干扰.
一个进程无法直接敢于另外一个进程的内容, 这种情况就被称作为进程的独立性, 每个进程都有自己的独立的地址空间, 这就大大的提升了操作系统的稳定性.
进程间通信
有的时候进程之间需要进行交互, 相互配合, 这个时候就需要用到进程间通信了. 如果每个进程可以直接访问物理内存, 这个时候是没有隔离性的, 也就不需要进程间通信. 也就相当于直接一个进程的结果写到另外一个进程的内存空间去. 这种操作必然是不合理的.
所谓进程通信, 就是在隔离性的前提下, 找一个公共的区域, 让两个进程借助这个区域来完成数据交换.