Modern Operating Systems(Ⅰ)——2014.12.15

时间:2023-03-09 02:54:37
Modern Operating Systems(Ⅰ)——2014.12.15

进程

     进程模型

        进程就是一个正在执行的程序的实例

      值得注意的是,若一个程序运行了两遍,则算作两个进程

     创建进程

     在通用系统中,有四种主要事件导致进程的创建

      ①系统的初始化

      ②执行了 正在运行的进程 所调用的 进城创建系统调用

      ③用户请求创建一个新进程

      ④一个批处理作业的初始化

      进程的状态

    运行态(该时刻进程实际占用CPU)

      就绪态(可运行,但因为其他进程正在运行而暂时停止)

      阻塞态(除非某种外部事件发生,否则进程不能运行)

      在Operating Systems Concept中还提到了new,terminated两种状态

     ※进程的实现(PCB)

      

                       PCB(进程控制块)
Process state running,waiting
Program counter 指示该进程将要执行的下一条指令的地址
CPU registers 累加器(accumulators)、索引寄存器(index registers)、栈指针(stack pointers)、通用寄存器(general-purpose registers)等等
CPU scheduling information
进程优先级(priority)、调度队列指针及其他调度参数
Memory-management information
base and limit registers的值, 页表或段表(page tables, or segment)
Accounting information
CPU used, clock time elapsed since start, time limits
I/O status information
the list of I/O devices allocated to this process, a list of open files, and so on 

线程

     线程的使用

     为什么在进程之下使用线程?

      因为有了进程的抽象概念 我们得以不必考虑中断 定时器 上下文切换  类似的 正因为有了线程的抽象概念 我们得以加入了一个新的元素 并行实体共享同一个地址空间和所有可用数据的能力 而这是多进程模型(它们具有不同地址空间)所无法表达的

     经典的线程模型

        理解进程的一个角度是 用某种方法把相关的资源集中在一起 那么 线程就是在CPU上被调度执行的实体

       多个线程共享一个地址空间和其他资源 而 多个进程共享物理内存 磁盘 打印机 和其他资源

      一个线程可以读 写 甚至清除另一个线程的堆栈 线程之间是没有保护的    之所以这样是因为不可能 没必要

      每个线程自己的内容:程序计数器   寄存器   堆栈   状态(跟进程相同)

      在用户空间中实现线程

       把整个线程包放在用户空间中  内核对线程包一无所知(从内核的角度看,就是按正常的方式管理,即单线程进程)

       每个进程有其专用的线程表 记录线程的属性(程序计数器 堆栈指针 寄存器 状态)

       优点:调度快捷 效率高(都是本地过程) 不需要陷阱 不需要上下文切换 不需要对内存高速缓存进行刷新

         每个进程有自己的调度算法

       缺点:如何实现阻塞系统调用

         如果一个线程开始运行 那么该进程中的其他线程就不能运行 (在用户空间实现线程时)

     在内核中实现线程

      内核中有用来记录系统中所有线程的线程表

进程间通信

     竞争条件

      两个或多个进程读写某些共享数据,而最后的结果取决于进程运行的精确时序

     临界区

        正确高效的避免竞争条件需要满足:

      任何两个进程不能同时处于其临界区

      不应对CPU的速度和数量做任何的假设

      临界区外运行的进程不得阻塞其他进程

      不得使进程无限期等待进入临界区

     忙等待的互斥

      互斥:以某种手段保证当一个进程在使用一个共享变量或文件时,其他进程不能做同样的操作

      屏蔽中断

      每个进程在刚刚进入临界区以后立即屏蔽所有中断,并在就要离开前再打开中断

      锁变量

      设想有一个锁变量,初始值为0。当一个进程想进入临界区,首先测试这把锁,若值为0,进入,并把值设为1;若为1,则该进程将等待其变为0

      缺陷:假设一个进程读出锁变量的值并发现它为0,而恰好在它将其值设置为1之前,另一个程序被调度运行(因为CPU严格按照时钟中断执行顺序进程),将其锁变量置为1,当另一个进程再次运行时同样将锁变量置为1,但此时有两个进程进入临界区之内

      严格轮换法

 while(TRUE){
while(turn != )
critical_region();
turn = ;
noncriti_region();
}
 while(TRUE){
while(turn != )
critical_region();
turn = ;
noncriti_region();
}  

      turn初始值为0,用于记录轮到哪个进程进入临界区,并检查或更新共享内存。

      开始时,进程0检查turn,为0,进入临界区,进程1也发现为0,但进程1的进入条件是turn = 1,于是在一个等待循环中不停地测试

      缺陷:试想这样一个情况,进程0离开临界区时,将turn值设为1,以便进程1进入,进程1离开后,将turn设置为0;此时两个进程都处于临界区之外,turn值又被设置为0.现在进程0完成了整个循环,它退出临界区,将turn值设置为1。此时turn值为1,两个进程都在临界区之外进行;突然,进程0完成了非临界区的操作并返回到循环开始,但是这时它并不能进入临界区,进程0只能只能继续while循环,直到进程1把turn的值改为0,可见,在一个进程比另一个进程慢了很多的情况下,轮流进入临界区并不是一个好方法

      Peterson解法

    睡眠与唤醒

      生产者-消费者问题

    信号量

     管程

      管程是一个由过程、变量以及数据结构等组成的集合,是一个特殊的模块或者软件包。

      管程有一个很重要的特性,即任意时刻管程中只能有一个活跃进程,典型的处理方法是:当一个进程调用管程时,该过程前几条指令将检查在管程中是否有其他的活跃进程,如果有,该进程挂起,直到另一个进程离开管程将其唤醒。

      管程也提供了实现互斥的途径:条件变量以及两个相关的操作,wait(),signal()  当一个管程过程发现它无法继续运行时(例如,生产者发现缓存区满),它会在某个条件变量上(如full)执行wait操作。该操作导致调用进程自身阻塞,并且还将另一个之前等在管程之外的进程调入管程。而另一个进程,可以通过对其伙伴正在等待的一个条件变量执行signal从而唤醒伙伴进程

      wait,signal与sleep,wake的区别在于,后者不存在严重的竞争条件

调度

    何时调度?

      创建一个新的进程后

      在一个进程退出时

      当一个进程阻塞在I/O和信号量上或由于其他原因阻塞时

      一个I/O发生中断

    调度算法

    哲学家进餐问题

    读者-写者问题