操作系统 第二章 进程的描述与控制(下半部分:算法理论篇))

同步机制

硬件同步机制
  1. 关中断
    在进入锁测试之前关闭中断,直到完成锁测试并上锁才能打开中断。
  2. 利用Text-and-Set指令实现进程互斥
    lock=FALSE时候资源空闲,lock=TRUE的时候资源正在被使用。
/* TS指令的一般性描述 */
boolean TS(boolean *lock) {
    boolean old;
    old = *lock;
    *lock = TRUE;
    return old;
}

/* 利用TS指令实现互斥 */
do{
    …    
    while TS(&lock);   // 使用的时候就一直在这个true的循环里走
    critical section;     // 
    lock = FALSE;      // 当操作打开锁的时候,
    remainder section;
} while(TRUE)

  1. 使用Swap指令实现进程互斥
/* swap指令的一般性描述 */
void swap(boolean *a, boolean *b)    // 交换
{
    boolean temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

/* 利用swap指令实现互斥 */
Do {
    key = TRUE    // 让key=True
    do { 
        swap(&lock, &key);   // 交换lock和key  也就是让lock=true
    } while (key != FALSE);   // 如果key不是false,证明交换前lock不是false,也证明当初是锁着的,那就一直循环锁死。什么时候解锁了再说。
    critical section;
    lock = FALSE;
    remainder section;
} while(TRUE)

软件方式
  • 信号量机制
  1. 整型信号量
  2. 记录型信号量
  3. AND型信号量
  4. 信号量集
整型信号量
  • wait(s)和signal(s)两个操作
// S表示资源数目的整型量S
/*wait(s)操作*/
wait(S){
	while (S≤0){  /*do no-op*/ };   
	S = S – 1;  // 表明有一个需要资源
}

/* signal(s)操作*/
 signal(S){
            S++;     //让信号量 +1 表明资源数增加了一个
}
// 这种方式无法做到让权等待
记录型信号量
// 引入进程阻塞机制,解决忙等问题
// 增加了对阻塞进程的记录
/*记录型的数据结构*/
typedef struct{
	int value;				// 资源数目
	struct process_control_block *list;		// 等待进程链表
}

/*wait(s) 请求资源*/
wait(semaphore *S){
	S->value--;       // 资源数量 -1 表明当前需求了一个信号量
	if(S->value < 0){ 	
		block(S->list);	// 如果资源数量少于0,那就把请求加入阻塞队列
	}
}

/*signal操作 释放资源*/   
signal(semaphore *S){
	S->value++;	// 释放一个资源
	if(S->value <= 0){
		wakeup(S->list);		// 如果是负数证明还有阻塞队列,打开一下
	}
}

记录型信号量特点
  1. S.value的含义

    S.value = 1,这时候wait(S)一下就进入了阻塞状态,保证了只允许一个资源访问,为互斥状态。
    且当S大于0,代表资源充足,S为负数,代表资源需求量。S为0,代表阻塞。

  2. 遵循让权等待

  3. 资源访问过程是原语,不存在执行一半这种情况。

AND型信号量
  • 基本思想:将多次对多个信号量申请修改为一次,用一个语句完成,要么一次获得所有,要么一个也得不到
  • 目的:防止资源不同导致的死锁。
信号量集
  • 一次需要N个某类资源,需要进行N次wait(S)操作,这样效果低下,且容易出现数量问题死锁,
  • Swait(S,d,d)对信号量S,允许申请d个,当自远少于d个不予分配
  • Swait(S,1,1),等价于一般的记录型信号量或互斥信号量
  • Swait(S,1,0)。低于0个不予分配,只能申请1个,相当于可控开关,可以做if语句用

信号量的应用

利用信号量实现进程的互斥
// 设有程序A和B,都需要同一个临界区
semaphore mutex = 1;	// 互斥信号量,这里表示的是mutex->value = 1;
Pa(){
	while(1){
		wait(mutex){};
		// Pa in 临界区
		signal(mutex);
		// Pa 其他操作
		
	}
}

Pb(){
	while(1){
		wait(mutex){};
		// Pb in 临界区
		signal(mutex);
		// Pb 其他操作
	}
}
利用信号量实现前驱关系
p1(){
	S1;
	signal(s);		// 释放信号量
}

p2(){
	wait(s);		// 必须等到p1释放信号量才能执行p2d的剩余操作
	S2;
}

void main(){
	semaphore s;
	s.value = 0;
	cobegin()		//	 并发执行开始标记
		p1();
		p2();
	coend
}

// 这里可以根据前趋图实现更加复杂的前驱关系,
前驱关系逻辑

p1(){S1;signal(a);signal(b);}
p2(){wait(a);S2;signal(c);signal(d);}
p3(){wait(c);S3;signal(e);}
p4(){wait(d);S4;signal(f);}
p5(){wait(b);S5;signal(g);}
p6(){wait(e);wait(f);wait(g);S6;}
main() {
    semaphore a,b,c,d,e,f,g;
    a.value=b.value=c.value=d.value=e.value=0;
    f.value=g.value=0;
    cobegin
        p1();p2();p3();p4();p5();p6();
    coend
}


经典的进程同步机制

生产者-消费者问题
  • 描述:这是一个生产者生产消息后,消费者消费的合作关系
  • 消费者消费的空白缓冲块由生产者生产
  • 进程在队列操作上是互斥的。
  • 缓冲池是有大小限制的,也就是最多就生产X个
  • 确定信号量
  • full:表示缓冲池中满缓冲块数量
  • empty:表示缓冲池中空缓冲块数量
  • mutex,表示对缓冲池的互斥访问
  • 因此可以确定,缓冲区数量必然等于full+empty,mutex=1来执行互斥访问。
int in=0,out=0;
item buffer[n];
semaphore mutex=1,empty=n,full=0;
	
// 生产者
void producer(){
	do{
		produce an item nextp;
		.....
		wait(empty);	// 空白块加工
		wait(mutex);		// 访问临界区
		buffer[in] = nextp;	// 处理临界区
		in = (in + 1)%n;
		signal(mutex);   // 结束访问
		signal(full);	// 满区块 增加
	}while(True);
	
}

void consumer(){
    do {
        wait(full);	// 消费满内容块
        wait(mutex);  // 互斥信号量
        nextc=buffer[out];  // 缓冲块处理  临界区
        out= (out+1) % n;  
        signal(mutex);  // 
        signal(empty);   // 增加空白块
        consume the item in nextc;
              …
    }while(TRUE)
}

void main(){
	cobegin
		producer();
		consumer();
	coend;


}


  • P操作的顺序很重要,不然会产生死锁
利用AND信号量解决生产者-消费者问题
int in=0,out=0;
item buffer[n];
semaphore mutex=,empty=n,full=0;
void producer(){
	do{
		producer an item nextp;
		Swait(empty, mutex);
		buttfer[in]=nextp;
		in = (in+1)%n;
		Ssignal(mutex,full);
		}while(1);	//	这里其实就是把双重申请换成了单独S申请,其他的是一样的
}
// 下略

哲学家进餐问题
  • 五个哲学家,五根儿筷子

  • 解决冲突的第一种方案,现制定顺序,比如先拿左边的筷子,

  • 如果出现都拿了自己左手边的,等待右手边的筷子空闲下来,那就死锁了,所以要想办法改变一下,变成四个哲学家拿左边的,这样就能保证至少一个人能吃上饭,然后释放。

  • 但是这玩意代码咋写…

  • 代码略

reader & writer 问题
  • 允许同时读
  • 不允许同时写
  • 不允许有读有写
  • 设计:读rmutex,写wmutex
  • 读的时候加上写锁,写的时候加上写锁和读锁

// reader开始读
void reader(){
	do{
		wait(rmutex);  // 读的时候,上读锁
		if(readcount==0)
			wait(wmutex);   // 第一个读者开始的时候不允许其他人写
		readcount++;
		signal(remutex);		// 释放临界区(readcount)
		.....
		// 写完了,需要destroy了
		wait(rmutex);    // 访问读者的临界区
		readcount --;
		if(readcount==0)
			signal(wmutex);	//	最后一个读者都没有了这个时候允许写
		signal(rmutex);
	}while(1);

}

void writer(){
	do{
		wait(wmutex);		//	 写临界区锁开始打开
		........
		write...
		.......
		signal(wmutex);		// 临界区锁关闭
	}while(1);

}

  • 感悟
  • 基本上mutex是用来表示临界区的打开关闭的,wait打开临界区,上临界区锁,signal关闭临界区,上临界区锁…
  • 记录数据的,基本上可以算作是临界区里的,也就是这里的readcount ++ - - 对它的操作必须是互斥的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值