进程同步

进程互斥

一个进程访问某临界资源时,另一个想访问该临界资源的进程必须等待

进程互斥软件实现

单标志法

违反 空闲让进

双标志先检查法

违反 忙则等待

双标志后检查法

违 反空闲让进 和 有限等待

Peterson算法

未遵循 让权等待

进程互斥硬件实现

中断屏蔽法

进程开始访问临界区到结束为止都不允许被中断,就不会发生进程切换

TestAndSet指令

TSL把上锁并检查用硬件方式变成了原子操作

优点:实现简单,适用于多处理机环境

缺点:不满足让权等待,暂时无法进入临界区的进程会占用cpu循环执行TSL

Swap指令

也是原子操作,与TSL基本一样都是把lock记录在old中然后lock变为true

信号量机制

整型信号量

记录型信号量

信号量实现进程同步

生产者-消费者

同步是不同进程间同步,互斥是对临界资源的互斥

读者-写者

读-写互斥,写-写互斥,但是读-读不互斥

上述方法如果在读者读期间不断有读者进入会导致写进程饿死

下面可以让写进程进入后,先阻塞读进程的排队但不影响已经开始读的进程,只要在写进程之前进入的读者读完就可以开始写

哲学家进餐

只有互斥关系,主要是避免死锁

第三种方案就是使用一个全局的互斥锁(Mutex)来保护“拿筷子”这个动作。哲学家在拿筷子前,先查看左右邻居是否在吃饭(设置 THINKING, HUNGRY, EATING 三种状态)。只有左右邻居都不在吃饭时,他才把自己的状态改为 EATING 并同时拿起两根筷子。

管程

为了解决信号量(Semaphore)机制带来的“程序编写难、易出错”而诞生的高级同步原语。由以下四部分组成

共享数据:就是VIP室里的那笔钱(临界资源)。它是私有的,外部绝不能直接修改。

入口函数:也就是操作这笔钱的方法(比如存钱、取钱)。外部线程只能通过调用这些特定的方法来访问共享数据。

对共享数据进行初始化的语句。

管程的名字。

自动互斥机制(最核心):管程天生自带一把互斥锁。任何时刻,最多只能有一个线程在管程内部执行入口函数。 其他试图进去的线程会被自动挡在门外排队(入口队列)。

条件变量(Condition Variables):用于线程间的同步(等待/唤醒)。如果一个线程进了管程,发现条件不满足(比如想取钱但余额不足),它不能占着茅坑不拉屎,必须调用 wait() 把自己挂起,并自动交出管程的控制权,去“条件队列”里等,直到别的线程调用 signal() 或 notify() 把它唤醒。

死锁

必要条件:

互斥:每个资源要么已经分配给了一个进程,要么就是可用的。

不可剥夺:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。

请求和保持:已经得到了某个资源的进程可以再请求新的资源。

循环等待:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。

处理策略:

预防死锁(破坏四个必要条件中一个或多个)

避免死锁(阻止系统进入不安全状态,银行家算法)

死锁的检测和解除(允许死锁发生,os会检测出死锁然后解除)

预防死锁

破坏互斥:SPOOLing技术把独占设备逻辑上改为共享设备,但并不是所有资源都可以,为了系统安全有时要保护这种互斥性;

破坏不剥夺:主动释放或抢占,释放会导致前一阶段工作失效,适用于易保存和恢复的资源;反复申请释放会增大系统开销降低系统吞吐量;主动释放可能会导致饥饿

破坏请求和保持:静态分配,运行前申请完全部资源;资源利用率低,也可能导致某些进程饥饿

破坏循环等待:顺序资源分配,进程只有在已经占有小编号资源后才能申请大编号资源;不方便增加新资源,进程实际使用资源顺序可能与编号递增不一致,会导致资源浪费

避免死锁

安全序列:系统按照这种序列分配资源,每个进程都能顺利完成;

安全状态:能找出至少一个安全序列;

银行家算法

进程申请资源时,先预判此次分配是否会使系统进入不安全状态,如果会进入不安全则不答应此次请求,先阻塞进程

Max - Allocation = Need

Available表示可用资源,Request_i表示进程 i 本次申请的资源量

死锁检测与解除

矩形内为资源总量,绿色边为已经分配量,蓝色边为还需分配量