操作系统笔记(十一)同步

几个概念

互斥:

  • 一个进程占用资源,其他进程不能使用

同步:

  • 按一定顺序执行各个进程

死锁:

  • 各自占用部分资源,造成循环等待
  • 一个例子:低优先级进程拥有临界区资源,高优先级进程拥有CPU,那么高优先级在等待低优先级释放临界区资源,低优先级在等待高优先级释放CPU,造成死锁

饥饿:

  • 其他进程轮流占用资源,某个进程一直得不到资源

实现同步的方式

  • 禁用硬件中断
  • 软件方式
  • 高级抽象:基于硬件的原子语句
    • 忙等待锁
    • 进程在等待过程中,占用CPU时间(并没有调度给其他进程,而是一直在while中等待)
    • 无法按先入先出
    • 无忙等待锁
    • 进程在等待过程中,是进入阻塞状态,释放CPU

信号量

由操作系统提供的协调机制。(与锁同等级,锁是各个线程之间的互相协调,信号量则由操作系统提供)

两个操作:P() 和V()

  • P() 用于申请资源,每次将信号量-1,如果信号量<0,则表示资源已用完,进入等待队列
  • V() 用于释放资源,每次将信号量+1,如果信号量<=0,则表示还有人在等,于是调用唤醒

由操作系统来保证P V两个操作是原子操作。具体实现上,可能由关闭中断或TSB原语等底层支撑来实现来保证原子操作。

管程 (与临界区同等级)

管程相当于是把对临界区的操作,都封装到某几个函数中。这样只要是需要访问临界区,则需要调用管程。因此所有的同步和互斥操作就可以
都写在这里。

管程一开始有一个锁,用于保证每次进入管程的只有一个进程。

这个锁可以使用信号量来实现。

在管程内部,除了一开始的锁,还有各种条件变量,用于表示各个条件。进程可能由于某个条件变量不满足而进入睡眠,也可能由于不满足一开始的锁
而进入睡眠。

进程一旦进入睡眠,优先唤醒已经在管程中的进程(比如因为某个条件变量不满足而进入睡眠的进程),如果管程内部已经没有进程了,则可以唤醒其他进程
进入管程了。

管程的两个函数:wait signal

  • wait
    • 等待某个条件变量满足。在满足之前,进入睡眠。优先唤醒已经在管程中,但由于执行signal而睡眠的进程,其次唤醒在管程外面睡眠的进程。
  • siganl
    • 设置某个条件变量,判断是否有进程在等待此条件。如果没有,直接退出。
    • 如果有,则唤醒它,同时自己进入睡眠(自己成为上面所说的“由于执行signal而睡眠的进程”)

管程的出入口

  • 入口处显然要有一个锁
  • 出口处要判断,是否还有因激活别人而进入睡眠的进程,如果有,唤醒,如果没有了,则释放管程锁,唤醒管程外面的进程

一个例子

  • A B C 三个进程,同时想进入管程,只有一个能申请到锁进入,假设为A,BC在管程外面睡眠。
    • 管程内进程1
    • 管程外进程2
  • 在管程中,A由于条件变量X1不满足,进入睡眠,同时唤醒B进入管程
    • 管程内进程2 A B
    • 管程外进程1 C
    • 等待条件变量X1 的进程1 A
  • B进入管程,B在某个操作中设置条件变量X1,使其满足,于是B将A唤醒,自己进入睡眠
    • 等待条件变量X1 的进程0
    • 管程内,由于执行signal而睡眠的进程1 B
  • A被唤醒,继续执行,由于条件X2不满足,又进入睡眠,接着唤醒B(此时B是“由于执行signal而睡眠的进程”,因此优先唤醒)
    • 等待条件变量X2 的进程1 A
    • 管程内,由于执行signal而睡眠的进程0
  • B被唤醒,B在管程内执行完毕,退出管程,在管程的出口,判断是否还有由于执行signal而睡眠的进程
    • 如果有,唤醒
    • 如果没有,释放管程锁,唤醒进程C
    • 管程内进程2 A C
  • 进程C设置条件变量2 使之满足,唤醒进程A,自己睡眠
  • 进程A在管程内部执行完毕,退出管程,在出口处,唤醒C
  • C继续执行,退出管程

发表评论