背景:生产者-消费者问题:并发访和操作同一个数据时的执行结果与访问发生的顺序有关,而进程的同步对操作系统至关重要。
进程并发执行的好处
进程需要与计算机中其他进程或设备进行协作
- 共享资源
- 加速
- I/O操作和CPU计算可以并行
- 程序可划分成多个模块放在多个处理机上并行执行
- 模块化
- 将大程序分解成小程序(gcc会调用 cpp cc1 cc2 as ld)
- 使系统易于复用和扩展
进程的交互关系
- 互斥(Mutual Exclusion)
- 一个进程占用资源 其他进程不能使用
- 死锁(Deadlock)
- 多个进程各占用部分资源 形成循环等待
- 饥饿(Starvation)
- 其他进程可能轮流占用资源 一个进程一直得不到资源
临界区
临界区问题:字面意思
临界区:当一个进程进入临界区时,没有其他进程可以在临界区内执行。
满足的三项要求:
- 互斥:不能同时在临界区执行
- 前进:一定时间内选择进入临界区的进程
- 有限等待:不能等太久
处理临界区问题的两种方法:字面意思
抢占内核
非抢占内核
软件实现
Peterson 算法:两个进程在临界区与剩余区间交替执行
硬件同步:锁
一个进程进入临界区之前必须得到锁,退出后释放锁
对于单处理器环境,只需要在修改共享变量时禁止中断就行了(非抢占式)
多处理器环境,这种做法会降低效率的,解决方法时原子操作,这就需要硬件上的支持了(锁)

信号量
以上都是基础知识,重点是信号量

信号量的用途很广,比较简单的就是司机乘务员之类的,通过信号量解决各种同步问题(比如实现顺序的先后)。
可以用二进制信号量来解决临界区问题(0和1之间的)
有了信号量之后临界区问题变得简单得多,现在来说说信号量的实现
实现
忙等待:看程序可以知道为进入临界区的进程一直在循环之中,这是多道程序不允许的,这样做会浪费cpu时间
自旋锁:会处于忙等待的信号量
克服忙等:修改PV操作呗
优点:节省cpu时间,提高cpu的利用率,让cpu被其他进程有效的利用
缺点:行下文切换花费时间,如果锁占用时间短,那么会得不偿失
实现:在wait操作时,如果要等待,阻塞自己,这个操作将进程放入与信号量相关的等待队列中,并将进程的状态切换为等待状态,然后指向cpu调度程序,选择另一个进程来执行。
当执行signal()操作之后,通过wakeup操作重新执行该进程,即将状态转换为就绪状态,然后该进程放到就绪队列之中

(S是信号量)
注意block和wakeup之间的对应关系
说了那么多,信号量的关键之处在于他们是原子地执行,这个就是硬件的事儿了
死锁:第七章内容
饥饿:进程在信号量内无限期阻塞(栈)
说了那么多还是同步的问题
好像对于临界区地实现没有过多的关注
线面介绍几个经典的同步问题
经典同步问题
有限缓冲问题:生产者-消费者问题
读者-写者问题:字面意思

哲学家就餐问题:
信号量不能很好的解决这个问题,第七章死锁会讲到
管程:略
总结
实现同步互斥需要解决临界区问题
临界区一般是共享变量地操作
信号量可以实现同步互斥
进程互斥
进程互斥:在多个程序中,有两个进程不可以同时进行(例如读,写操作)。
竞争资源(临界资源)
当并发进程竞争使用同一资源时,他们之间就会发生冲突。如果操作系统将资源分配给其中的某一个进程使用,另一个进程就必须等待,直到申请的资源可用时,由操作系统分配给他们。
如果竞争资源的进程太多,这些进程还必须等待在一个队列中,如就绪队列,阻塞队列等。
一种极端的情况是,被阻塞进程永远得不到申请的资源,而死锁。
采用互斥方式,使用临界资源
资源的互斥,进程使用上述这类资源的时候,只能有一个进程对资源进行处理。(临界区)
进程同步
进程同步是一个操作系统级别的概念,是在多道程序的环境下,存在着不同的制约关系,为了协调这种互相制约的关系,实现资源共享和进程协作,从而避免进程之间的冲突,引入了进程同步。比如说进程A需要从缓冲区读取进程B产生的信息,当缓冲区为空时,进程B因为读取不到信息而被阻塞。而当进程A产生信息放入缓冲区时,进程B才会被唤醒。
同步互斥解决方法(同样是实现临界区地方法):软件、硬件、信号量、管程