操作系统 2.2-多进程总体实现
多个进程使用CPU的图像
-
如何使用CPU呢?
-
通过让程序执行起来来使用CPU。
-
-
如何充分利用CPU呢?
-
通过启动多个程序,交替执行来充分利用CPU。
-
-
启动了的程序就是进程,所以是多个进程推进
-
操作系统需要记录这些进程,并按照合理的次序推进它们(分配资源、进行调度)。
-
这就是多进程图像的概念。
-
一个CPU(灯泡图标)负责调度多个进程(PID:1, PID:2, PID:3)。
每个进程有一个进程控制块(PCB),图中展示了PCB1,其中包含了进程的状态信息,例如“算出ax=1,启动磁盘写,正在等待完成...”。
进程的创建和管理
在系统启动时,最终执行的是main点C中的main函数,通过fork创建一个进程,该进程执行INT,最终启动shell,这是用户与计算机交互的开始。
操作系统通过创建初始化进程并执行shell,使用户能够执行任务。shell根据用户输入的命令创建新的进程来完成任务,如执行LS命令时,会创建一个专门执行该命令的进程。
无论是解决实际问题还是执行特定任务,操作系统都是通过启动进程来实现的。
因此,多进程图像在整个系统运行过程中非常重要,它展示了计算机在多个进程中不断推进和解决问题的状态,从而反映了计算机的使用情况。
多进程如何组织
PCB
进程控制块(PCB)是操作系统用于记录和管理进程信息的数据结构,它包含了进程的状态、优先级、程序计数器、CPU寄存器集合和内存管理信息等。PCB是操作系统感知和控制进程的基础。
操作系统通过将进程组织成不同的队列(如就绪队列、阻塞队列等)来实现进程的管理和调度。调度策略决定了哪个进程应该被调度执行,常见的调度策略包括先来先服务(FCFS)、最短作业优先(SJF)、优先级调度和时间片轮转(RR)。
进程调度与切换是操作系统中实现多任务并发的基础。它涉及到保存当前进程的执行状态(上下文),选择下一个要执行的进程,并加载其状态。这一过程包括保存当前进程的上下文到PCB、从就绪队列中选择下一个进程、加载新进程的上下文到CPU寄存器、更新PCB和状态、以及跳转到用户模式执行新进程。
进程状态图
进程状态图是描述操作系统中进程在其生命周期内所经历的不同状态及其状态转换的图形表示。它提供了进程生存期的清晰描述,并作为理解操作系统进程管理的一个窗口。进程状态图通常包括以下几个关键状态:
-
新建态(New):
-
进程正在被创建,操作系统正在为其分配资源和初始化PCB。
-
-
就绪态(Ready):
-
进程已准备好运行,等待被调度器选中以获得CPU时间。
-
-
运行态(Running):
-
进程正在CPU上执行。在单处理器系统上,任何时候只有一个进程处于此状态。
-
-
阻塞态(Blocked):
-
进程正在等待某个事件(如I/O操作完成或资源可用)而不能立即执行。
-
-
终止态(Terminated):
-
进程已完成执行或被强制终止,操作系统正在或已经清理了其资源。
-
进程状态转换
进程状态图还描述了进程状态之间的转换,常见的转换包括:
-
新建 → 就绪:进程创建完成后,变为就绪状态,等待调度执行。
-
就绪 → 运行:调度器选中就绪进程,分配CPU给它,进程开始运行。
-
运行 → 阻塞:运行中的进程需要等待某个事件(如I/O操作),进入阻塞状态。
-
阻塞 → 就绪:进程等待的事件已经发生(如I/O完成),进程重新变为就绪状态。
-
运行 → 就绪:如果进程的时间片用完或被抢占,它将从运行状态返回到就绪状态,等待下一次调度。
-
运行 → 终止:进程完成或被终止,进入终止状态。
-
就绪 → 终止:在某些情况下,就绪进程可能直接被终止。
多进程的交替
总体轮廓
代码:
// 启动磁盘读写操作
pCur.state = 'W'; // 'W' 代表阻塞态,等待磁盘操作
addToDiskWaitQueue(pCur); // 将当前进程pCur加入磁盘等待队列
schedule(); // 调用调度函数进行进程调度
void schedule() {
pNew = getNext(ReadyQueue); // 从就绪队列中选择下一个要执行的进程
switch_to(pCur, pNew); // 进行进程切换,从pCur切换到pNew
}
“多进程图像:多进程如何交替?”
解释了在多进程环境中,当一个进程(pCur
)需要等待磁盘操作时,如何通过操作系统的调度机制切换到另一个进程执行。
-
启动磁盘读写:当前进程发起一个磁盘读写请求。
-
设置进程状态:将当前进程(
pCur
)的状态设置为等待('W'
)。 -
放入等待队列:将
pCur
进程放入磁盘等待队列(DiskWaitQueue
)。 -
调度:调用
schedule()
函数进行进程调度。
在schedule()
函数中:
-
从就绪队列(
ReadyQueue
)中获取下一个要执行的进程(pNew
)。 -
调用
switch_to(pCur, pNew)
函数进行上下文切换,从当前进程pCur
切换到新进程pNew
。
这个过程展示了多进程操作系统如何通过调度算法在多个进程之间进行切换,以实现多任务的并发执行。特别是当一个进程因为等待I/O操作而被阻塞时,操作系统可以将CPU资源分配给其他就绪的进程,从而提高系统的整体效率和响应速度。
交替的三个部分
进程调度:一个深刻的话题,涉及交替的三个部分:队列操作、调度和切换。
其中进程的调度有以下两种方法:
FIFO(先入先出):被认为是一种公平的策略,因为它不考虑进程执行的任务的区别,按照它们到达的顺序进行调度。
优先级调度:涉及如何设定优先级,如果设置不当可能会导致某些进程饥饿,即某些进程长时间得不到执行。
Switch_to函数
进程切换:包括三个主要步骤:保存当前进程的状态、选择下一个要执行的进程、恢复新进程的状态。
switch_to 函数:实现上下文切换,保存当前进程(pCur
)的寄存器状态到PCB,并从新进程(pNew
)的PCB加载寄存器状态到CPU。
进程与进程间的关系
在多进程操作系统中,进程间的合作和冲突是两个需要特别关注的问题。以下是对这两个问题的详细解释:
进程间的合作
进程间的合作通常发生在需要共享资源或数据的情况下。例如,多个进程可能需要访问同一个文件、数据库或缓冲区。在这种情况下,操作系统必须提供一种机制来确保这些共享资源被安全地访问,避免数据损坏或不一致。
生产者-消费者模型是进程合作的一个典型例子,其中一些进程(生产者)生成数据并放入缓冲区,而其他进程(消费者)从缓冲区取出数据进行处理。为了使这种合作能够顺利进行,操作系统需要提供同步机制,如互斥锁、信号量或条件变量,以确保生产者在缓冲区满时不会覆盖旧数据,消费者在缓冲区空时不会尝试读取数据。
进程间的冲突
进程间的冲突可能发生在多个进程试图同时访问或修改同一资源时。这种冲突可能导致数据不一致、死锁或资源竞争条件。为了避免这些冲突,操作系统实现了多种内存管理和进程同步技术:
-
内存管理:通过虚拟内存和物理内存的映射,确保每个进程有自己的内存空间,从而防止进程间直接相互干扰。
-
地址空间分离:每个进程有自己的地址空间,通过地址转换表(如页表)将虚拟地址转换为物理地址,从而实现进程间的内存隔离。
-
进程同步:使用锁、信号量、屏障等机制来控制进程对共享资源的访问,确保在适当的时机进行数据的读写操作。
进程同步
进程同步是确保多个进程能够协调执行,以实现合作而不产生冲突的关键机制。进程同步机制可以防止多个进程同时访问共享资源而导致数据不一致的问题。常见的进程同步机制包括:
-
互斥锁(Mutex):确保同一时间只有一个进程可以访问特定的资源。
-
信号量(Semaphore):允许多个进程访问资源,但限制同时访问的数量。
-
条件变量(Condition Variable):允许进程在满足特定条件时进行同步。
-
屏障(Barrier):确保一组进程在继续执行之前全部达到某个点。
通过这些机制,操作系统可以确保进程间的合作是有序和安全的,从而有效地支持多进程图像的实现。