《操作系统精髓与设计原理》 第5章 并发性:互斥和同步(学习笔记)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gongjun12345/article/details/83621387

核心问题,进程和线程的管理。

5.1并发的原理:

支持并发进程的基本要求是加强互斥的能力。一个进程被授予互斥能力时,在其活动期间,具有排斥所以其他进程的能力。
支持互斥的硬件机制,操作系统或编译器支持的互斥解决方案,最后,信号量,管程和消息传递。

术语介绍:
原子操作:指令操作期间不会被别的进程打断。
临界区域:一段访问公共区域的代码,一个进程在执行这段代码,别的进程就不能执行。
这里指的是代码。
死锁:两个或两个以上的进程等待其他进程做完某事而不能继续执行。
活锁:
互斥:多个进程共同访问同一个临界区域,只能一个进程访问。
竞争条件:多个进程访问同一共享数据,结果依赖于执行的相对时间。
饥饿:进程长时间无法获得调度器的调度。

进程间的资源竞争 互斥:会导致死锁,饿死。
进程间通过共享合作 通过一个参数,一个文件做共享对象,以实现互斥,做到临界区数据的保护

互斥的要求:
1、一次只能一个进程进入临界区
2、非临界区进程不能干涉其他进程。退出临界区必须释放资源和上述的共享对象
3、不能出现饿死或死锁。(关闭中断,内核做schedule,优先级反转)
4、没有进程在临界区时,别的进程需要立刻能进入。
5、支持多处理器(多处理都能访问共享对象做互斥)
6、进程在临界区处理的时间需要短。

5.2硬件的互斥

中断禁止,专用机器指令

5.3信号量

两个或多个进程可以通过简单的信号进行合作。以做到两个进程各自的某段代码顺序执行。一个执行semSignal,一个执行semWait。
1、信号量初始化为0或1
2、进程执行semWait时,会将信号量减1,如果信号量小于0,则该进程阻塞。如果大于等于0,进程排入就绪队列。
3、当进程执行semSignal时,会将信号量加1。如果小于或等于0,则从阻塞队列里取出一个进程。
信号量负值的大小,代表有多少进程在阻塞队列中。

5.3.1互斥

通过信号量做到互斥,避免多个进程同时进入到某一个临界区域。

semWait
临界区域
semSignal

5.3.2生产者/消费者问题

5.3.3信号量的实现

信号量的操作必须是原子操作,可以通过软件的算法Dekker/Peterson。或者硬件来实现compare&swap指令。
单处理器系统,可以禁止中断,避免信号量的操作被中断打断。

5.4管程

https://blog.csdn.net/qq_35212671/article/details/52713822

5.5 消息传递

消息传递的原语是,send(dest, message),receive(source, message)
发送分阻塞和非阻塞,接收也分阻塞和非阻塞。常见的是非阻塞发送,阻塞接收。需要实现消息应答机制。
非阻塞发送还要考虑,错误会导致重复的消息发送。

5.5.5互斥

利用消息传递和信箱的方式,可以做到进程间互斥。进程阻塞接受box中的消息,得到消息后,执行临界区代码。
完成后,将邮件还给邮箱。
p(int i)
message msg;
while(true){
receive(box, msg)
临界区
send(box, msg)
}
parbegin(p(1), p(2), p(3), p(4), p(5), p(6), p(7))
有1条消息,只会发送给一个进程,其他进程被阻塞。

5.6 读写者问题

有一块共享的数据区域,需要满足如下条件:
1、任意进程都可以读取,相互之间不需要互斥
2、只能允许一个进程写入,写入时,其他进程不能读取内容。
读写者问题和一般互斥问题,生产者和消费的区别:

一般互斥问题,读取者效率低,读取和写入者都需要做互斥。
生产者和消费者都需要对队列做读写,无法像读取者那样,不修改共享区域内容

读优先的方式,会导致将数据的权限一直保持给读取者,结果会引发写入者饿死的情况。

Page165
void read(){
    semWait(x)
    readcount++
    if(readcount == 1)
        semWait(wsem)
    semSignal(x)
	READ_UINT()
	
	semWait(x)
    readcount--
    if(radcount == 1)
    	semWait(wsem)
    semSignal(x)

}

void write(){
	semWait(wsem)
	WRITE_UINT()
	semSignal(wsem)
}

另外一种解决办法,当写进程尝试写入时,就不会让新的读进程进入。

  1. 增加一个rsem,用于锁住读进程,writecount,和对应保护writecount的信号量y。
  2. 对于读进程,用一个sem z来避免太多读进程在rsem上排队。(不然rsem就挡不住这些读进程了)
    sem z是保证读进程排队串行去检查rsem。
void reader(){
    semWait(z)
    	semWait(rsem)
    		semWait(x)
    			read_counter++
    			if(counter==1)
    				semWait(wsem)
    		semSignal(x)
    	semSignal(rsem)
    semSignal(z)
	READ_UINT()
	semWait(x)
		read_counter--
		if(counter==0)
			semSignal(wsem)
	semSignal(x)
}

void writer(){
	semWait(y)
		write_counter++
		if(write_count==1)
			semWait(rsem)
	semSignal(y)
	
	semWait(wsem)
	WRITE_UINT()
	semSignal(wsem)
	
	semWait(y)
		write_counter--
		if(write_count==0)
			semSignal(rsem)
	semSignal(y)
}

更好的方式,是采用消息传递的方式来达到写入者优先。由控制者决定写入和控制的权限。

5.7 小结

进程互斥和同步可以通过信号量,信箱的方式去实现。

猜你喜欢

转载自blog.csdn.net/gongjun12345/article/details/83621387