一些基本概念:
进程同步:进程间这种等待对方消息的协调关系。
进程互斥:某一资源同一时间仅允许一个进程对它访问。
饥饿:一个进程所申请的资源总是被优于自己的其他进程所占有,而长时间处于不能被调度执行状态。
死锁:进程集合中某些进程处于永远的阻塞状态。
临界资源:一段时间内只允许一个进程使用的资源。
临界区:访问临界资源的代码称为临界区。
实现进程互斥的方法
1,硬件方法
(I) 开关中断指令,进程在进入临界区之前把关中断,屏蔽所有中断。完成临界区内的任务后,开中断。
缺点:如果中断时间过长会使系统效率低下.
(II)测试于设置指令TS
(III)交换指令
2,软件办法
(I)俩标志进程互斥算法
进程P1 进程P2
while(T2); while(T1);//如果T1在临界区就一直等待
T1=1 T2=1;//标识进程2在临界区
临界区 临界区
T1=0 T2=0;
但是检查可能被中断导致俩个进程同时进入临界区。
改进后
进程P1 进程P2
T1 = 1 T2=1
while(T2); while(T1);//如果T1在临界区就一直等待
临界区; 临界区;
T1=0; T2=0;
又有可能存在同时设置为T1=1 和 T2=1的情况导致我们进程1和进程2都进不去临界区。
(II)三标志进程互斥算法
进程1
...
T1=1;
T=2;
while(T2==1&&T==2);
临界区;
T1=0
...
进程2
...
T2=1;
T=1;
while(T1==1&&T==1);
临界区;
T2=0
...
三标志法不会出现俩进程都不能进入临界区或者都进入临界区的情况。
3,信号量机制
(I)整形信号量
int S;
P(S): while(S<=0)
S=S-1;
V(S): S=S+1;
(II)结构体信号量
结构体信号量分为资源信号量和控制信号量。
typedef struct
{
int value;
struct PCB *L;
}Semaphore
Semaphore S;
当结构体型信号量作为资源信号量时,S.L指向因等待同类资源进程阻塞队列的对手。S.value初值为非负整数,代表系统中某类资源的数量。大于0表示当前可用数量,=0表示这类资源为空,<0表示等待该资源阻塞的进程的个数。
当结构体信号量为控制信号量时,S.value表示是否允许当前进程执行。当S.value>=0时当前运行的进程继续执行,当S.value<0时表示运行进程被阻塞放弃执行,此时,S.value的绝对值表示被阻塞于S信号量的个数。当S.value的初始值为1表示,只允许各个进程互斥执行(例如打印机资源).
简单来说所谓的PV操作就是P(S)就是将S.value-=1;V(S)的操作就是将S.value+=1,但是如果执行之后S.value还是小于0证明还有阻塞状态的进程需要被唤醒重新执行相关的进程,即为S.L指向的阻塞队列中。
使用信号量实现进程的互斥
有了以上的基础,我们便可以利用信号量来实现进程互斥的操作。这里的使用方法类似于MFC中的CCriticalSection使用类似。
Semaphore mutex;
mutex.value=1;
cobegin
process Pi()
{
...
P(mutex);
临界区;
V(mutex);
...
}
coend;
使用信号量实现进程同步
若干进程为完成一个共同的任务而相互合作称为进程的同步操作。合理的使用P操作和V操作可以实现进程间的同步。
例如,公共汽车上的司机和售票员的工作类比为俩个需要同步的进程,只有当司机停车后售票员才可以打开车门,只有当售票员关门之后司机才可以启动汽车。这个是需要协调合作完成的工作。
设置信号量Start来控制是否可以启动汽车,即作为是否允许汽车启动的信号量;信号量Open控制是否可以打开车门。它们的初值均设置为0表示不可以启动汽车门是开着的(无须关闭)。当关车门后使用V操作给Start.value+1,使司机可以启动汽车,因此司机在启动汽车之前先给Start.value-1看是否可以启动汽车。若售票员已经关车门表示此时Start.value-1之后的值为0表示可以正常启动汽车,若售票员未关车门(未进行V操作。)此时Start.value-1之后的值为-1表示启动汽车这个操作被阻塞。售票员分析于司机是否可以正常开车类似。
代码表示
Semaphore Start,Open;
Start.value=0,Open.value=0;
cobegin:
process 司机()
{
while(1)
{ P(Start);
启动汽车;
正常形式;
到站停车;
V(Open);
}
}
process 售票员()
{
while(1)
{
关车门;
V(Start);
售票;
P(Open);
开车门;
}
}