目录
一、多道批处理
首先从操作系统的发展史说起,先是串行操作系统,到后来的单道批处理系统,单道批处理系统即首先把多个任务放到外存里,然后再由监控程序(最早期的操作系统)从外存中一个一个的把任务放到内存中执行,单道批处理系统的问题是等待io的时间太长了,为了提高cpu资源的利用率出现了多道批处理系统,它使得多个任务能同时驻留在内存上,并发的执行这多个任务,因为在内存中存在多个任务,所以os为了能够更好的去管理这些任务就出现了进程的概念,把每一个任务当做一个或多个进程来表示出来。
此外多道批处理系统还考虑了如下功能:
- 内存保护:任务执行时不能够改变操作系统的内存区域,如映射表
- 定时器:任务开始时开始计时,时间到了就会停止该任务,防止任务独占系统
- 特权指令:某些机器指令被设计为只有os系统能使用,如io操作的指令就属于特权指令,如果任务要用到io的功能,就必须请求os来替他执行这一操作
- 中断:提高处理器的效率,中断。
特权指令和内存保护引出了运行级别的概念,即用户模式和内核模式。
二、进程
进程的定义如下:
- 一个正在执行的程序
- 一个正在计算机上执行的程序实例
- 能分配给处理器并由处理器执行的实体
- 由一组执行的指令、一个当前状态和一组相关的系统资源表征的活动单元
进程由三部分组成:
- 一段可执行的程序,如我们开发的软件的代码,运行时驻留在内存中,由CPU取值执行
- 该程序执行所需的相关数据(变量、工作空间、缓冲区等)
- 程序的执行上下文:又称为进程状态,用来管理进程所需的内部数据,该部分内容操作系统不允许进程能够直接访问,包括各种处理器寄存器的信息,PC和数据寄存器,还包括os使用的信息,如进程优先级及进程是否在等待特定的I/O事件的完成。
进程=程序+数据+PCB
![](https://img-blog.csdnimg.cn/0cf943655986453a8fdae3f15e1ee8e9.png)
基址寄存器保存该进程在内存的起始地址,界限寄存器保存该区域的大小,它们可以保护内部进程间相互不干扰。进程看似地址范围是连续的,但实际情况并非如此
其中进程的上下文用一个进程控制块(PCB)的数据结构表示,它里面包含了有关该进程充分的信息,操作系统就是靠进程的PCB来感知该进程,PCB有:
- 标志符:进程的唯一标志符
- 状态:进程当前所处状态,后面会讲
- 优先级:相对于其他进程的优先级
- 程序计数器:在该进程中下一条要执行的指令地址
- 内存指针:包含程序代码和相关数据的指针,以及与其他进程共享内存块的指针
- 上下文数据:进程执行时处理器寄存器中的数据,如PSW寄存器
- I/O状态信息:包括显式I/O请求和分配给进程的I/O设备等
操作系统通过维护内存表、I/O表、文件表、进程表来表示进程的资源分配情况,这些表关联到对应进程,如I/O表存放哪些进程占用了哪些I/O设备;进程表存放所有的进程地址。
三、进程的状态
操作系统在调度和给进程分配资源时,必须考虑到进程的状态,且能够让操作系统能够跟踪到它。
五阶段进程模型
其中的五个状态分别为:
- 运行态:进程正在执行,单处理器中一次只能有一个进程处于该状态
- 就绪态:进程做好了准备,等待调度器调度进入运行态执行,放入就绪队列等待调度
- 阻塞态:进程在某些事件发生前不能执行,如I/O操作,该进程放入阻塞队列中等待。和就绪态的区别是就绪态等待的是CPU资源,阻塞等待的是其他资源
- 新建态:刚刚创建的进程,但是可能因为内存空间满,还未加载到内存
- 退出态:进程运行完或者因为某种原因被取消,操作系统可能需要提取进程的历史信息才暂时的保留该进程。
转换关系为:
- 新建->就绪:操作系统准备接纳该进程,把它变为就绪态。
- 就绪->运行:os空闲了,需要选择一个就绪的进程调度执行,这是调度器的职责。
- 运行->退出:当前正运行的进程完成或取消。
- 运行->就绪:该进程的时间片到了或者是被高优先级的其他进程抢占了。
- 运行->阻塞:进程请求其必须等待的某件事时,进入阻塞态。如系统调用请求一个无法立即得到的资源或一个进程等待另一个进程的输入时都可能被阻塞。
- 阻塞->就绪:所等待的事件发生时。
- 就绪->退出:状态图中没有标明出来,但是可能会发生的,如父进程在任何时候都能够终止子进程,如果父进程终止,相关的子进程也都会被终止。
- 阻塞->退出:如上。
如果只有一个阻塞队列,那么当一个事件发生时,必须扫描整个阻塞队列,搜索那些等待该事件的进程,该方法在进程比较多的时候效率较低,所以一般采用下图的多阻塞队列方法,一种事件可以对应一个队列。
![](https://img-blog.csdnimg.cn/2c4eb97b757c42bdb5c0f813abdf1b00.png)
七阶段进程模型
实际很多操作系统是按照上面的五种状态构建的,但是还可以进一步的升级,增加更多的状态。
单道系统到多道系统的演变是因为单道系统的处理器大多处于空闲状态,而多道系统当某个进程被阻塞时可以移到其他的进程运行,增加了系统利用率,但是处理器远快于I/O(大概10^6倍),可能出现一种情况就是内存中的进程都在等待I/O的现象,因此处理器还是浪费了。
解决的方法有:1.加大内存,容纳更多的进程,但这种方法治标不治本,容纳的进程太多os速度也会下降;2.当内存中不存在就绪的进程时,操作系统就把阻塞的进程从内存移出到磁盘中的挂起队列,释放空间。此后操作系统要么从挂起队列中取出另一个进程,要么接收一个新进程的请求(新建态的进程),将其放入内存运行。
但是挂起的阻塞进程取回内存没有意义,因为它任然未做好执行的准备。所以为了区分,把这挂起到磁盘的进程区分阻塞和就绪态。此时的大致状态为:
- 就绪态:进程已在内存中并可以执行
- 阻塞态:进程已在内存中并等待一个事件
- 阻塞/挂起态:进程已在外存中并等待一个事件
- 就绪/挂起态:进程已在外存中,但只要载入到内存就可执行
转换关系为:
- 阻塞->阻塞/挂起:内存中没有就绪进程,则至少换出一个阻塞进程,腾出空间
- 阻塞/挂起->就绪/挂起:等待的事件发生
- 就绪/挂起->就绪:内存中没有就绪进程;就绪/挂起状态的进程比就绪态的进程优先级更高
- 就绪->就绪/挂起:通常是挂起阻塞态的进程,但是有些时候不得不挂起就绪态的进程释放内存空间。此外若os确信阻塞态的某个进程很快就会完成,且比就绪态的进程优先级高,它可能会挂起就绪的进程
- 阻塞/挂起->阻塞:这种情况比较少,一个进程运行完释放后,会选择换入磁盘中的进程,如果此时的阻塞/挂起中的一个进程比就绪/挂起中的进程优先级都高,且os确信该进程很快就会完成,则有可能去换入阻塞/挂起的进程
- 运行->就绪/挂起:如果阻塞/挂起的进程不再被阻塞且优先级很高,它可能会抢占运行时的进程,且把运行时的进程直接换出到就绪/挂起状态,释放一部分空间给需要换入的进程
四、进程的控制
1.执行模式切换
操作系统中,某些指令只能在特权模式下运行,如改变PSW等寄存器的值。目的是为了保护系统,进行区分隔离。
特权模式称为:系统模式、内核模式;非特权模式称为用户模式
需要在内核模式下才能执行的比较常用的操作有:
- 进程管理方面:进程的创建和终止、进程的调度和分派、进程切换、进程同步和进程通信的支持、管理进程控制块
- 内存管理方面:为进程分配地址空间、交换、页和段管理
- I/O管理方面:缓冲区管理、为进程分配I/O通道和设备
- 其他:中断处理
程序状态字PSW中通常含有一个指示执行指令的位。典型情况下,当用户进程进行系统调用或中断时(此时由操作系统接管),执行模式会变为内核模式,当从系统服务返回到用户进程时执行模式会还原为用户模式。
如64位IA-64体系结构的一些处理器中,都有一个包含2bit的CPL(当前特权级别)的寄存器,级别是0表示最高特权级别,级别3最低。当发生终端时清空CPL字段,设置为0,此时操作系统处理完终端后,中断处理程序的末尾的最后一个指令是IRT(中断返回),它会使得处理器恢复用户程序的CPL,恢复原先的特权级别。
模式切换也被称为用户态和内核态的相互切换
2.操作系统控制结构
操作系统为了管理进程和资源,必须掌握每个进程的资源和资源的当前状态,采用的方法是构造并维护其管理的每个实体的信息表。如下图
内存表用于跟踪内存和外存,内存表包含如下信息:
- 分配给进程的内存和外存
- 内存块或虚存的任何保护属性,如哪些进程可以访问哪些共享内存区域
- 管理虚存所需要的任何信息
I/O表管理系统中的I/O设备和通道
- 管理哪些设备已经被分配或者未被分配
- 管理I/O设备的状态
- 管理缓存空间
文件表用于管理文件在外存中的哪个位置、文件是否存在、文件的属性和状态等
进程表标志进程的位置、管理进程的属性……
3.进程管理
进程的创建、终止、调度、状态装换、同步和通信等都是操作系统内核要完成的事,也就是进程管理。操作系统通过提供了一些原语(原子操作的程序,执行时不可被中断)操作来实现,原语有:
- 进程切换:如时钟中断、I/O中断、存储访问失效、陷阱、系统调用等都需要切换进程
- 创建与终止
- 阻塞和唤醒
- 挂起与激活
进程切换
进程切换的时候首先需要先保护现场,即当前CPU的PC指针和一些寄存器,保存在进程的PCB中,再把PCB移入到相应的队列中(阻塞或就绪),此时意味着当前仅已经结束运行了,下一步开始选择要切换的进程(由调度器调度),把该进程的状态设置为运行态,把它PCB中的内容(PC和寄存器)加载到处理器,该进程可以开始执行了。
用代码表示:
//将pCur放到阻塞队列或就绪队列
schedule();
schedule()
{
pNew = getNext(ReadyQueue); //从就绪队列获取到下一个需要执行的进程
switch_to(pCur,pNew); //切换进程
}
switch_to(pCur,pNew)
{
pCur.ax = CPU.ax; //保护线程
pCur.bx = CPU.bx;
……
pCur.cs = CPU.cs;
pCur.retpc = CPU.pc;
CPU.ax = pNew.ax; //切换场景
CPU.bx = pNew.bx;
CPU.cs = pNew.cs;
……
CPU.retpc = pNew.pc;
}
进程切换和模式切换的关系:
- 进程切换:作用于进程之间,调度器回收当前线程,CPU并准备把它分派给其他进程时讲被引用,进程切换时会引用模式切换,因为切换进程上面讲过必须要在内核态被调用。
- 模式切换:进程内部的一种操作,当一个进程中的程序引用了核心子系统所提供的系统调用时该操作将被引用,没有涉及到进程之间的切换,除非操作系统是以进程的方式运行才会发生用户进程切换到操作系统对应的进程。
进程创建
创建一个新的进程,fork()系统函数。在提交一个新的作业、用户登录系统、提供一些服务(如打印服务)、父进程创建子进程等情况下都会创建对应的进程。
创建原语包含的步骤如下:
- 为新进程分配唯一的标志符,主进程中新增一个表项
- 为进程分配内存空间,包括程序、用户栈、数据和PCB需要的空间,若共享已有空间,则应建立相应的链接
- 初始化PCB:初始化寄存器的值、设定PC值、进程状态等
- 建立链接:将新进程插入到对应的队列中(就绪/挂起)
- 建立或扩展其他数据结构
进程的终止
- 根据被终止进程的标识符ID,找到其PCB,读出该进程的状态
- 若该进程为执行状态,则终止其执行,调度新进程执行
- 若该进程有子孙进程,则立即终止其所有子孙进程将该进程的全部资源,或归还给其父进程,或归还给系统
- 将被终止进程(的PCB)从所在的队列中移出,等待其它程序来统计该进程的信息
阻塞和唤醒
阻塞原语block(),由当前进程就可以调用该方法,不需要模式切换,调用后状态变为阻塞,进入相应的阻塞队列。
唤醒原语wakeup(),自己不能够唤醒自己