进程是什么
大部分资料都描述进程是资源分配的基本单位,但是这个描述并不能很好的描述进程的特性;另一个种描述是进程是对运行程序的一种抽象,我更赞同这种说法,第一它说明进程和程序有关系,第二它说明必须是运行中的程序。我们看一下Linux下的进程。
进程概念
进程内存布局
进程的内存布局被划分为多个分段,具体如图
- text分段:存放可执行代码,也就是程序的代码
- data分段:存放全局变量
- heap分段:程序运行期间动态分配的内存
- stack分段:方法调用的临时内存(比如方法参数、返回地址和临时变量)
一个进程内存布局例子
进程状态
进程五状态变化图
- new:进程new状态,创建之后就会进入ready状态
- ready:当ready状态的线程被调度器选择时进入running状态,而被中断时又会重新进入ready状态
- running:处于ready状态的进程被调度器调度时会进入running状态执行,此时进程持有cpu执行权
- waiting:当runing状态的进程I/O或事件等待时,会进入waiting状态,当I/O或事件完成时会进入ready状态等待调度器调度
- terminated:当处于running状态的进程运行退出时会进入终止状态
进程控制块
进程控制块定义:在多道程序系统中,多个程序并发执行,也就是多个进程并发执行,依赖调度器调度进程获得CPU执行权。这就需要操作系统维护各个进程的信息,比如说当前运行的线程被中断,那下一次要调度时肯定需要知道中断前的状态(比如代码执行到哪一行,寄存器的值等)。这些信息都被存在进程表中,每个进程对应一个表项,这些表项被称为进程控制块
进程控制块结构(PCB)
主要分为进程管理、内存管理以及进程打开的文件管理三块,有几个重点关注的属性
- Process state:也就是上边介绍的进程的五种状态new, ready, running, waiting, halted等
- Program counter:指向进程下一条执行的指令地址
- CPU registers:cpu寄存器状态,用于被重新调度时,恢复cpu寄存器使进程正确的从上一次被中断处继续运行
- 其他信息在后面的笔记中介绍
线程
在支持线程的系统中,每个进程至少拥有一个线程,此时系统调度的便是线程而不是进程,进程成为线程的容器,PCB中也会额外的记录线程信息。现代的进程一般都拥有多个线程,这样可以提升程序的性能,因为在多核处理器上,多个线程可以并行执行。对于线程的,下一篇笔记中会详解介绍。注意线程是不能脱离进程独立存在的,因为操作系统不会为线程分配系统资源,因此线程要依附进程存在。
进程调度
线程调度是为了充分利用CPU和程序并发执行,因为当一个进程等待I/O或某个事件时,CPU不能做任何事,这个时候就可以调度其他进程执行。那进程是如何调度呢?下面来分析
调度队列
一个进程创建完成时,通常是进入就绪队列,就绪队列是等待调度获得CPU执行权的一个链表队列,如下图所示,链表头指向第一个PCB。
当然也有其他队列,比如wait队列,wait队列中的进程通常都在等待一个确定的事件,等到事件发生时,它们将会加入就绪队列中。一个进程cpu->wait队列->ready队列的转变如下图所示
CPU调度
这一部分在后边的笔记中做详细介绍
上下文切换
上下文:上下文指的是进程运行时的状态,这些状态包括寄存器值、进程状态和内存管理信息,都存储在上边介绍的PCB中
上下文切换:指的是,CPU在通过调度由执行A进程换成B进程时,需要将A进程的状态保存到A的PCB中,然后从B的PCB中加载上下文信息,这是一个纯开销的操作,因为此时CPU不能做其他有用的工作,其速度取决于内存的速度、需要复制的寄存器数和指令。整个过程的示意图如下
进程间通信(IPC)
进程通信的必要性
- 信息共享:如果多个应用都对某个信息有兴趣,必须对信息的访问作并发控制
- 加速计算:把一个任务拆分成多个子任务,子任务可以并行计算
- 模块化:用不同的进程或线程划分系统功能独立运行,使得系统具有模块化
IPC方式-共享内存
共享内存结构
共享内存优点
速度快:相比消息传递,共享内存只需要在创建共享内存时需要系统调用,消息传递每次都需要系统调用
共享内存缺点
实现比消息传递复杂,需要避免冲突,分布式共享内存实现复杂
共享内存的并发(在后续的并发笔记中介绍)
IPC方式-消息传递
消息传递结构
消息传递的优点
- 可以用于分布式环境
- 不需要相同的内存地址空间
消息传递-命名
- 直接相连:当两个进程需要通信时,如果建立直接link,则需要互相精确知道进程
- 间接相连:进程不直接相连而是通过额外的结构,比如mailboxes或ports
消息传递-通信方式
- 阻塞:如果阻塞的方式,那么send方法将阻塞直到被其他进程receive(直接相连)或者发送到mailboxes,receive方法将阻塞直到接收到消息
- 非阻塞:进程在调用send方法时不阻塞,receive方法也是直接返回消息或者null
消息传递-queue缓存区
- Zero容量:queue没有缓冲区,因此sender将阻塞直到recipient接收消息
- Bounded:有界队列,当queue满时,sender阻塞直到queue有空间容纳消息
- Unbounded:无界队列,可以容纳任意数量的消息
总结
进程是操作系统中非常重要的一部分,一篇笔记是不可能记录得了进程的所有内容,这里忽略了很多细节,比如进程的调度算法,进程内存不足时内存置换算法,进程的同步方式,这些都会在后边详细介绍。