1、信号量
信号量机制是一种功能较强的机制,可用来解决互斥和同步问题,它只能被两个标准的原语wait(S)(P操作)和signal(S)(V操作 )访问。
注意:原语是一种特殊的程序段,其执行只能一气呵成,不可被中断,原语由关中断/开中断指令实现。
1)整形信号量
整形信号量被定义为一个用于表示资源数目的整型量S,wait和signal操作可描述为:
wait(S){
while(S<=0);
/*注意:怎么理解这里,当资源数小于0时,就一直测试检测,所以会处于忙等
待情况,不是让权等待。*/
S=S-1;//上一句成功才会资源减一
}
signal(S){
S=S+1;//这里是释放资源的意思
}
该机制并未遵循“让权等待”的准则,而是使进程处于“忙等”状态。
2)记录型信号量(解决忙等)
记录型信号量是不存在“忙等”现象的进程同步机制。除需要一个用于代表资源数目的整型变量value外,再增加一个进程链表L,用于链接所有等待该资源的进程。记录型信号量得名于采用记录型的数据结构。记录型信号量可描述为:
typedef struct{
int value;
struct process*L;
}semaphore;
void wait(semaphore S){//申请资源
S.value--;//请求一个该类资源
if(S.value<0){
add this process to S.L;//这里的意思是资源不够就进入链表等待的进程(同一个资源)
block(S.L);//自我阻塞,放弃处理机
}
}
void signal(semaphore S){
S.value++;
if(S.value<=0){//这里意思是还有等待该资源的
remove a process to S.L;
wakeup(P);//所以再次唤醒P操作
}
}
(1)S->value的初值表示系统中某类资源的数目,因而又被称为资源信号量;
(2)对它进行的每次wait(S)操作(P操作),(注意这里主体是一个进程)意味着进程请求一个单位的该类资源,这会使系统中可供分配的该类资源数减少一个,因此描述为S->value–;当S->value<0时,表示该类资源已分配完毕,因此进程应调用block原语,进行自我阻塞,放弃处理机,并将该进程插入信号量链表S->list中。可见,该机制遵循了“让权等待”准则。
1、什么是让权等待准则呢?
当进程不能进入临界区时,应立即释放处理机,防止进程忙等待。
2、什么是临界区?
每个进程中访问临界资源的那段代码称为临界区,每次只允许一个进程进入临界区,进入后,不允许其他进程进入。
3、什么是临界资源?
是一次仅允许一个进程使用的共享资源。各进程采取互斥的方式,实现共享的资源称作临界资源。
(3)对信号量的每次signal(S)操作(V操作),表示执行进程释放一个单位的资源,这会使系统中可供分配的该类资源数增加一个,故S.value++操作表示资源数目加1。若加1后仍是S->value<=0,则表示在该信号链表中仍有等待该资源的进程被阻塞,故还应调用wakeup原语唤醒第一个等待进程。
(4)如果S->value的初值为1,则表示只允许一个进程访问临界资源,此时的信号量会转化为互斥信号量,用于进程互斥。
2、信号量的基本应用
1)实现多个进程的互斥
(1)互斥信号量mutex初值为1;
(2)每个进程中将临界区代码置于P(mutex)和V(mutex)原语之间;
(3)必须成对使用P和V原语(在同一进程中),不能次序错误、重复或遗漏。
遗漏P原语则不能保证互斥访问
遗漏V原语则不能在使用临界资源之后将其释放(给其他等待的进程)。
semaphore mutex=1;
P1(){
...
P(mutex);
临界区代码段...
V(mutex);
...
}
P2(){
...
P(mutex);
临界区代码段...
V(mutex);
...
}
2)实现同步
(1)分析什么地方需要实现“同步关系”,即必须保证“一前一后”执行的两个操作(或两句代码);
(2)设置同步信号量S,初始为0;
(3)在“前操作”之后执行V(S);
(4)在“后操作”之前执行P(S)。
/*信号机制实现同步*/
semaphore S =0;//初始化同步信号量,初始值为0
P1(){
代码1;
代码2;
V(S);//我的理解是这里转去P2
代码3;
}
P2(){
P(S);
代码4;
代码5;
代码6;
}
保证了代码4一定在代码2之后执行。
3)实现前驱关系
其实每一对前驱关系都是一个进程同步问题(需要保证一前一后的操作)因此,
(1)要对每一对前驱关系各设置一个同步变量;
(2)在“前操作”之后对相应的同步变量执行V操作;
(3)在“后操作”之前对相应的同步变量执行P操作。
信号量值为0的点是限制的关键所在;
成对使用P和V原语(在有先后关系的两个进程中),不能次序错误、重复或遗漏,否则同步顺序出错。
信号量题目做题一般方法:
(1)分析问题,找出同步、互斥关系;
(2)根据资源设置信号量变量;
(3)写出代码过程,并注意P、V操作的位置;
(4)检查代码,模拟机器运行,体验信号量的变化和程序运行过程是否正确。
3、管程
1)为什么引入管程?
信号量机制存在:编写程序困难、易出错等问题,而管程—一种高级的同步机制,可以让程序员学程序不需要再关注复杂的PV操作,让写程序更加轻松。
2)管程的定义
管程是一种特殊的软件模块,
管程包含:
(1)多个彼此可以交互并共用资源的线程
(2)多个与资源使用有关的变量
(3)一个互斥锁
(4)一个用来避免竞态条件的不变量
3)管程的基本特征
(1)模块化。
管程是一个基本的软件模块,可以被单独编译。
(2)抽象数据类型。
管程中封装了数据及对数据的操作,这点有点像面对对象编程语言中的类。
(3)信息隐藏。
管程外的进程或其他软件模块只能通过对管程对外的接口来访问管程提供的操作,管程内部的实现细节对外界是透明的。(就是看不见的意思)
(4)使用的互斥性
任何一个时刻,管程只能由一个进程使用。进入管程时的互斥由编译器负责完成。