互斥和同步
这部分知识相对于后面的内存管理等要记忆的不多,主要是理解整个流程。
-
原子操作/临界区(一段用于访问共享资源的代码,不允许多个进程同时访问)/死锁/活锁/互斥/竞争条件
-
硬件的支持
- 中断禁用:只适用与单处理器机器中,一个进程在调用一个系统服务或被中断之前将一直运行,因此并发进程不能重叠,只能交替。由系统内核为启用和禁用中断定义的原语提供。进入临界区启用中断。
- 专用机器指令:执行期间任何其他指令访问内存都将被阻止,且这些动作在一个指令周期中完成
- compare_and_swap:使用测试值testval
- 忙等待/自旋等待:试图进入临界区的其他进程进入忙等待模式
/* program mutualexclusion */ const int n = /* number of processes */; int bolt; void P(int i) { while (true) { while (compare_and_swap(&bolt, 0, 1) == 1) /* do nothing */; /* critical section */; bolt = 0; /* remainder */; } } void main() { bolt = 0; parbegin (P(1), P(2), . . . ,P(n)); } /* program mutualexclusion */ int const n = /* number of processes*/; int bolt; void P(int i) { while (true) { int keyi = 1; do exchange (&keyi, &bolt) while (keyi != 0); /* critical section */; bolt = 0; /* remainder */; } } void main() { bolt = 0; parbegin (P(1), P(2), . . ., P(n));
-
信号量S:一个整型变量,除了初始化外只能通过原语wait()(P,荷兰语测试)和signal()访问(V,增加)
信号量的初值为可用资源数量。当进程需要使用资源时,需要对该信号量执行 wait() 操作(减少信号量的计数)。当进程释放资源时,需要对该信号量执行 signal() 操作(增加信号量的计数)。当信号量的计数为 0 时,所有资源都在使用中。之后,需要使用资源的进程将会阻塞,直到计数大于 0。
struct semaphore{//可用于控制访问具有多个实例的某种资源 int count; queueType queue; }; void semWait(semaphore s){//申请使用资源 s.count--;//在此之前是无法提前知道该信号量是否会被阻塞的 if(s.count<0){//==0表示当前进程可以使用最后一个空闲资源 //把当前进程插入队列 //阻塞当前进程 } } void semSignal(semaphore s){//结束使用资源 s.count++; if(s.count<=0){//原本至少是-1,至少有一个进程因等待而阻塞,则唤醒 //把进程P从队列中移除 //把进程P插入就绪队列 } }
解决同步问题的实例:现有两个并发运行的进程:P1 有语句 S1 而 P2 有语句 S2。假设要求只有在 S1 执行后才能执行 S2。我们可以轻松实现这一要求:让 P1 和 P2 共享同一信号量 synch,并且初始化为 0。
//进程P1中插入语句 S1; signal (synch); //在进程 P2 中,插入语句: wait (synch); S2; //因为 synch 初始化为 0,只有在 P1 调用 signal(synch) ,即 S1 语句执行之后,P2 才会执行 S2。
-
二元信号量:只能是0/1
struct binary_semaphore{//可用于控制访问具有多个实例的某种资源 enum {zero,one} value; queueType queue; }; void semWaitB(binary_semaphore s){//申请使用资源 if(s.value==one) s.value=zero;//被占领 else{//已经被占领 //把当前进程插入队列 //阻塞当前进程 } } void semSignalB(binary_semaphore s){//结束使用资源 if(s.queue is empty())//没人等待 s.value=one;//标志当前资源空闲 else{ //把进程P从队列中移除 //把进程P插入就绪队列 } }