这里列出可以完全正常工作的算法,其他算法详见教材
//flag[]表示进程是否希望进入临界区或是否正在临界区中执行 //turn用于指示允许进入临界区的进程标志 enum boolean {false,true}; boolean flag[2]; int turn; Po:{ do{ flag[0]=true; turn=1; while(flag[1] && turn==1); //进程1希望或者正在临界区中,且允许进入临界区的进程号为1,进程P0等待 进程P0的临界区代码CS0; flag[0]=false; //P0访问其临界区结束 进程P0的其他代码; } while(true); } P1:{ do{ flag[1]=true; turn=0; while(flag[0] && turn==0); //进程0希望或者正在临界区中,且允许进入临界区的进程号为0,进程P1等待 进程P1的临界区代码CS1; flag[1]=false; //P1访问其临界区结束 进程P1的其他代码; } while(true); }其他算法出现各种问题的原因:临界资源的状态检查和修改两个操作没有作为一个整体来实现!
实践中很少单独采用软件实现互斥!!下面介绍使用硬件方法实现互斥
禁止中断(关中断):当一个进程正在其临界区执行代码时,禁止一切中断发生!这样其他进程就无法进行进程切换,从而保证当前进程的临界区执行完成硬件指令方法:用一条指令完成标志的检查和修改两个操作,从而保证检查操作与修改操作不被打断,如TS指令、Swap指令在锁机制中,通过原语(原语是若干条指令构成的过程,切原语执行时不可中断)保证状态的检查和修改两个操作作为一个整体实现! 锁是一个代表资源状态的变量,0表示资源可用,1表示资源不可用
二中的互斥方法都存在部分缺点,从而提出了信号量机制。
信号量是一个表示资源的实体,由两个成员(s,q)组成,其中s是一个具有非负初值得整型变量,q是一个初始状态为空的队列。 s的含义:s大于0,表示当前可用资源的数目;s<0,其绝对值表示因请求该资源而被阻塞等待的进程数目;s等于0,表示该时刻等待该资源的进程数目为0,但是资源已经完全使用。
struct semaphore{ int count; queueType queue; }; wait(semaphore s) //P操作原语 { s.count--; if(s.count<0) { 阻塞该进程; 将该进程插入等待队列s.queue; } } signal(semaphore s) //V操作 { s.count++; if(s.count<=0) { 从等待队列s.queue取出第一个进程P; 将P插入就绪队列; } }值得注意的是
互斥信号量的初值通常为1同步信号量的初值视具体含义而定以这个前趋关系为例,可以如下求解:
semaphore f1=0,f2=0,f3=0,f4=5,f5=0; //都是同步信号量,表示进程Pi是否执行完成 main() { cobegin P1(); P2(); P3(); P4(); P5(); P6(); coend } P1() { ....... V(f1); V(f1); V(f1); } P2() { P(f1); ....... V(f2); } P3() { P(f1); ....... V(f3); } P4() { P(f1); ....... V(f4); } P5() { P(f2); ...... V(f5); } P6() { P(f3); P(f4); P(f5); ....... }管程同信号量一样是一种实现同步与互斥的机制。和信号量的区别在于,管程本身实现了同步与互斥,PV操作的原语放在管程的过程中; 而使用信号量时,互斥同步属于程序员的责任,PV操作由程序员完成! 具体内容详见教材
互斥与同步是低级的进程间通信,对应的PV操作是低级通信原语。高级通信则是在进程间以较高的效率传送大量数据。
这里以直接通信(消息缓冲通信)为例(一定要看,利于增强理解):