1.进程的并发执行
并发是所有问题产生的基础,也是OS设计的基础
并发:进程的执行是间断性的,进程的相对执行速度不可预测
2.进程互斥:
由于各进程要求使用共享资源(变量、文件),而这些资源需要排他性使用,各进程之间竞争使用这些资源
临界资源:系统中某些资源一次只允许一个进程使用(也叫临界资源、互斥资源、共享资源)
临界区(互斥区):各个进程中对某个临界资源(共享变量)试试操作的程序片段
记不记得OS的进程or线程通信有这个!!
竞争条件:两个或者多个进程读写某些共享数据时,最后的结果取决于进程运行的精确时序
3.临界区的使用原则:
没有进程在临界区时,想进入临界区的进程可进入
不允许两个进程同时处于临界区
临界区运行的进程不得阻塞 其他进程进入临界区
不得使用进程无限期等待进入临界区
4.实现进程的方案:
(1)软件方案:Dekker Peterson (编程技巧)
(2)硬件:屏蔽中断 TSL(XCHG)指令
忙等待 不断测试CPU,而不做其他事情(单处理器不推荐)
多处理器中自旋锁 Spin lock
5.中断屏蔽方法:
“开关中断”指令
执行“关中断”指令
临界区操作
执行“开中断”指令
特点:
简单,高效;
代价高,限制CPU并发能力(临界区大小);
不适合于多处理器;
适合于操作系统本身,不适合用于进程
6.TSL指令:测试并加锁 test and set lock
enter_region:
复制锁到寄存器并将锁置为1
判断寄存器内是否是0
若不是0 进入enter_region
返回调用者,进入临界区
leave_region:
锁中置0
返回调用者
7.XCHG指令 交换指令
寄存器与锁变量的内容交换
8.进程同步:
多个进程中发生的事件存在某种时序关系,需要互相合作,共同完成一项任务
具体的说,一个进程运行到某一个点时,要求另一个伙伴进程为他提供消息,在未获得消息之前,该进程进入阻塞态,获得消息后被环形进入就绪态
9.生产者/消费者问题:(有界缓冲区问题)
问题描述:
一个或者多个生产者生产某种类型的数据放置在缓冲区中,有消费者从缓冲区中取数据,只能有一个生产者或消费者对缓冲区进行操作
要解决的问题:
当缓冲区已满时,生产者不会继续向其中添加数据;
当缓冲区为空时,消费者不会从中移走数据。
避免忙等待。睡眠与唤醒操作
代码部分看linux的那本培训书吧
spooling技术中也可以认为,输入程序和输出程序是一对生产者、消费者
10.信号量及PV操作(一种经典的进程同步机制)又是一个之前无数遍听说过不知道的概念
信号量:一种特殊的变量,用于进程间传递信息的一个整数值 对信号量实施的操作:初始化、P操作、V操作
P(s)
{
s.count--;
if(s.count<0)
{
该进程状态置为阻塞状态;将该进程插入相应的等待队列s.queue的末尾;重新调度
}
}
V(s)
{
s.count++;
if(s.count<=0)
{
唤醒相应等待队列s.queue中等待的一个进程;改变其状态为就绪态,并将其插入就绪队列
}
}
PV都是原子操作;
在信号量上定义了三个操作:初始化(非负数)、p、v
最初提出的是二元信号量,推广到一般信号量(计数信号量)
11、PV操作解决进程间互斥问题的过程:
分析并发进程的关键活动,划定临界区
设置信号量mutex,初值为1
在临界区前实施P
在临界区之后实施V
之前一直在想 为啥生产者/消费者这个这么眼熟……好像是设计模式里有吧
(空行的地方加 P(mutex) V(mutex)操作)
两个P操作不能颠倒,会死锁
两个V操作可以颠倒,但是临界区会扩大(虽然不会出错)
12.信号量解决读者/写者问题
问题描述:多个进程共享一个数据区,分为两组:只读的进程;只写的进程
要求的条件:
允许多个读者同时执行读操作;不允许多个写者同时操作;不允许读者、写者同时操作
(1)读者优先
如果读者执行:
无其他读者、写者,该读者可以读
若已经有写者,但有其他读者正在读,则该读者可以读
若写者正在写,该读者必须等
如果写者执行:
无其他读者、写者,该写者可以写
若有读者正在读,该写者等待
若有其他写者正在写,该写者等待
第一个读者需要做P(w)后续的可以不做P操作;最后一个读者做V(w)
linux中的读写锁:
应用场景:
如果每个执行实体对临界区的访问或者读写是写共享变量,但他们不会读+写,读写锁是最好的选择
e.g.维护路由表ipx路由代码
13.管程:
提出原因:信号量机制不足:程序编写困难、易出错
方案:在程序设计语言中引入一种高级维护机制
定义:
是一个特殊的模块;
有一个名字;
由关于共享资源的数据结构及在其上操作上的一组过程组成。
进程只能通过调用管程中的过程间接访问管程中的数据结构
14.需要解决问题
(1)互斥:管程是互斥进入的 为了保证数据结构的数据完整性
管程的互斥由编译器负责保证的,是一种语言机制
(2)同步:设置条件变量及等待唤醒操作以解决同步问题
可以让一个进程或者线程在条件变量上等待(先释放管程的管理权),也可以通过发送信号将等待在条件变量上的进程线程唤醒
15.应用管程遇到的问题:
当一个进入管程的进程执行等待操作时,它应当释放管程的互斥权。当后面进入管程的进程执行唤醒操作时(例如P唤醒Q),管程中便存在两个同时处于活动状态的进程
解决方法:
P等待Q (Hoare)
Q等待P (MESA)
规定唤醒操作为管程中最后一个可执行的操作(Hansen 并发pascal)
16.HOARE管程:
17.管程的实现:
(1)直接构造 效率高
(2)间接构造 用某种已经实现的同步机制去构造
18.MESA管理:
提出由于Hoare管程的缺点:两次额外的进程切换
P进程唤醒Q,P限制性
解决:signal->notify
当一个正在管程中的进程执行notify(x)时,它使得x条件队列得到通知,发信号的进程继续执行
使用notify要注意的问题:
notify的结果:位于条件队列头的进程在将来合适的时候且当处理器可用时回复执行
由于不能保证在它之前没有其他进程进入管程,因而这个进程必须重新检查条件 用while循环取代if语句
导致对条件变量至少多一次额外的检测,且对等待进程在notify之后合适运行没有任何限制
19.notify的改进:
给每个条件原语关联一个监视计时器,无论是否被通知,一个等待时间超过的进程被设为就绪态
当改进程被调度执行,会再检查相关条件,如果条件满足则继续执行
超时可以预防:当某些进程在产生相关信号前失效
20.broadcast:使所有在该条件上等待的进程都被释放并进入就绪队列
当一个进程不知道有多少进程将被**时,这种方式是非常方便的
当一个进程不能准确判断**哪个进程时,也可以使用广播
21.hoare&mesa比较
M管程优于H管程之处在于M管程错误比较少
在M中,优于每个过程中在收到信号后都重新检查管程变量,并且由于使用了while结构,一个进程不正确的broadcast广播或者发信号notify不会导致收到信号的程序出错
收到信号的程序将检查相关的变量,如果期望的条件没有满足,它会重新继续等待
22.pthread中的同步机制:
通过互斥量保护临界区 (记不记得互斥量好像也是通信的一种方式)
通过条件变量解决同步问题(用的MESA管程)
pthread_cond_wait三个主要动作:
(1)解锁
(2)等待:
当收到一个解除等待的信号(pthread_cond_signal 或者 pthread_cond_broad_cast)之后,pthread_cond_wait马上执行:
(3)上锁
23.通信机制:
信号量和管程不能传递大量信息,不适合多处理器的情况
引入“消息传递”机制 send recieve
适用于:分布式系统、基于共享内存的多处理器机系统……
消息传递;共享内存;管道;套接字;远程调用
24.消息传递:
消息缓冲区结构:消息头(消息类型,接受进程ID,发送进程ID,消息长度,控制信息);消息体(消息内容)
25.共享内存:
在物理内存中建立共享内存,映射到两个进程的地址空间
26.管道 PIPE
利用一个缓冲传输介质--内存活文件连接两个互相通信的进程
字符流方式写入读出
先进先出顺序
管道通信进制必须提供协调能力:互斥、同步;判断对方进程是否存在
27。不同OS中进程通信(尼玛……终于学到这里了,去年这会背的是一脸懵逼啊)
UNIX:管道、信号队列、共享内存、信号量、信号、套接字
Linux:管道、消息队列、共享内存、信号量、信号、套接字
内核同步机制:原子操作、自旋锁、读写锁、信号量(读取信号量,互斥体)、屏蔽、BKL
windows:
同步对象:互斥对象、事件对象、信号量对象、临界区对象、互锁变量
套接字、文件映射、管道、有名管道、邮件槽、剪贴板、动态数据交换、对象连接与嵌入、动态链接库(这居然也算)、远程过程调用
28.Linux的屏障:
一种同步机制;用于对一组线程进行协调;应用场景:一组线程协同完成一项任务,需要所有线程都到达一个汇合点后再一起前进推进