并发:互斥与同步
并发术语
一、并发的原理
1.在单处理器的情况下,问题源于多道程序设计系+统的一个基本特性:进程的相对执行速度不可预测,它取决于其他进程的活动,操作系统处理中断的方式以及操作系统的调度策略。带来以下困难:
(1)全局资源的共享危险
(2)操作系统很难对资源进行最优化分配
(3)定位程序设计错误困难
2.回显程序
void echo()
{
chin = getchar();
chout = chin;
putchar(chout);
}
由于每一个应用程序都需要使用过程echo,所以它被视为一个共享过程,载入到所有应用程序公用的全局存储区中。因此,只需要使用echo过程的一个副本,从而节省空间。
同时我么也要保护共享的全区变量(以及其他全局资源),控制访问该变量的代码。定义一条规则:一次只允许一个进程进入echo,控制对共享资源的访问。
3.竞争条件
竞争条件发生在多个进程或者线程读写数据时,其最终结果依赖于多个进程的指令顺序。(用信号量解决)
4.操作系统关注的并发问题
(1)操作系统必须能够跟踪不同的进程(进程控制块)。
(2)操作系统必须能为每一个活跃进程分配和释放各种资源。
(3)操作系统必须保护每个进程的数据和物力资源,避免其他进程的无意干涉。
(4)一个进程的功能和输出结果必须与执行速度无关。(进程之间的交互)
5.进程的交互
5.1进程间的资源竞争
当并发进程竞争同一个资源时,它们之间会发生冲突:两个或者更多的进程在它们执行过程中需要访问同一个资源,每个进程并不知道其他进程的存在,并且每个进程也不受其他进程的影响。每个进程都不影响它所使用的资源的状态。竞争进程间没有任何信息交换,但是,一个进程的执行可能会影响到竞争进程的行为。
5.2竞争进程面临的三个控制问题:
(1)互斥:
假设两个或以上的进程需要访问一个不可共享的资源(打印机)。执行过程中,每个进程都给该I/O设备发送命令,接受状态信息,发送接收数据。这类资源叫做临界资源,使用临界资源的那一部分程序称为程序的临界区。一次只允许一个程序在临界区中。
(2)死锁
(3)饥饿
5.3进程间通过共享的合作
通过共享进行合作的情况,包括进程间在互相并不确切知道对方的情况下进行交互。Eg:多个进程可能访问一个共享变量、共享文件或数据库,进程可能使用并修改共享变量而并不涉及其他进程,但却知道其他进程也可能访问同一个数据。因此,这些进程必须合作,以确保它们共享的数据得到正确管理。控制机制必须确保共享数据的完整性。只有写操作必须保证互斥。除此之外还有一个新要求:数据的一致性。
5.4进程间通过通信的合作
传递消息的过程中,进程间未共享任何对象,因而不需要互斥,但是存在死锁和饥饿。
6.互斥的要求
1.必须强制实施互斥;在与相同资源或者共享对象的临界区有关的所有进程中,一次只允许一个进程进入临界区。
(2)一个在非临界区停止的进程不能干涉其他进程。
(3)绝不允许出现需要访问临界区的进程被无线延迟的情况,即不会出现死锁和饥饿。
(4)当没有进程处于临界区时,任何需要进入临界区的进程必须能够立即进入。
(5)对相关进程的执行速度和处理器的数目没有任何要求和限制。
(6)一个进程驻留在临界区中的时间必须是有限的。
二、信号量
1.常用并发机制
2.信号量
(1)一个信号量可以初始化为非负数。
(2)semWait(接受信号)使信号量-1.若值为负数,则执行semWait的进程被阻塞。否则进程继续执行。
(3)semSignal(发送信号)使信号+1,若值小于或者等于0,则被semWait阻塞的进程被解除阻塞。
【解释】开始时,信号量的值为0或者是正数。如果是正数,则该值等于发出semWait后立即继续执行的进程的数量。如果为0,则发出semWait的下一个进程会被阻塞,此时信号量为负数,之后每一个semWait都会使信号量的负值变大。该负值等于正在等待解除阻塞的进程的数量。在信号量为负值的情况下,每一个semSignal操作都会将等待进程中的一个进程解除阻塞
3.二元信号量
(1)一个二元信号量被初始化0或者1。
(2)semWaitB会检查信号的值,如果为0,那么进程执行semWaitB就会受阻。如果值为1,就变为0,并且继续执行。
(3)semSignalB检查是否有任何进程在该信号上受阻,如果有就通过semWaitB操作,受阻的进程就会被唤醒,如果没有进程受阻,值就被设置为1。
无论计数信号量还是二元信号量,都需要使用队列来保存在信号量上等待的进程,最公平的策略是先进先出,采用这个策略定义的信号量称为强信号量,没有规定进程从队列中移出顺序的信号量称为弱信号量。
4.互斥
信号量一般初始化为1,这样第一个执行semWait的进程就可以立即进入临界区,并且把s的值置为0.接着任何试图进入临界区的其他进程,都发现第一个进程忙,因此被阻塞,把s的值置为-1.可以任意数目的进程试图进入,每个不成功的尝试都会使s的值减1,当最初进入临界区的进程离开时,s+1,一个被阻塞的进程被移出等待队列,置于就绪态。这样,当操作系统下次调度时,他就可以进入临界区。
三、消息传递
进程交互时,必须满足两个基本要求:同步和通信。为实施互斥,需要同步;为了合作,进程之间需要交换信息。send() ;seceive();
1.同步
2.寻址
2.1直接寻址
Send原语包含目标进程的标识号,而receive原语有两种处理方式:第一种是要显式指定源进程,另一种是不可能指定所期望的源进程,此时receive原语的source参数保存了接受操作执行后的返回值。
2.2间接寻址
消息先是发送到一个共享数据结构,该结构是由临时保存消息的队列组成,这些队列通常称为信箱。
3.信息格式
4.排队原则
先进先出/优先级原则
5.互斥
(1)如果有一条信息,它仅仅被传递给一个进程,其他进程被阻塞。
(2)如果消息队列为空,那么所有进程被阻塞;当一条消息可用时,只有一个阻塞进程被**,并得到这条消息。
四、死锁原理
可以把死锁定义成一组互相竞争系统资源或者进行通信的进程间的“永久阻塞”。当一组进程中的每一个进程都在等待某一事件(等待请求的资源被释放),而只有在这组进程中的其他被阻塞的进程才可以触发该事件,这时就称这组进程发生死锁。
1.可重用资源
资源分为两类:可重用资源、可消耗资源。
可重用资源指一次只能提供一个进程安全的使用,并且不会由于使用而耗尽资源。进程得到资源单元,后来释放这些单元,供其它进程再次使用。
可消耗资源是指可以被创建和销毁的资源。通常对某种类型可消耗资源的数目没有限制,一个无阻塞的生产进程可以创建任意数目这类资源。当消费进程得到一个资源时,该资源就不存在了。(中断、信号、消息等)
死锁检测、预防和避免的方式
原则 |
资源分配策略 |
不同的方案 |
主要优点 |
主要缺点 |
预防 |
保守的:预提交资源 |
一次性请求所有资源 |
(1)对执行一连串的进程非常有效 (2)不需要抢占 |
(1)低效 (2)延迟进程的初始化 (3)必须知道将来的资源请求 |
抢占 |
用于状态易于保存和恢复的资源时非常方便 |
过于经常的没有必要的抢占 |
||
资源排序 |
(1)通过编译时检测是可以实施的 (2)既然问题已经在系统设计时解决了,不需要再运行时间计算 |
禁止增加的资源请求 |
||
避免 |
处于检测和预防之间 |
操作以发现至少一条安全路径 |
不需要抢占 |
(1)必须知道将来的资源请求 (2)进程不能长时间阻塞 |
检测 |
非常自由:只要可能,请求的资源都允许 |
周期性地调用以测试死锁 |
(1)不会延迟进程的初始化 (2)易于在线处理 |
固有的抢占被丢失 |
2.资源分配图
3.死锁的条件
死锁的可能性 |
死锁的存在性 |
1.互斥 2.不可抢占 3.占有且等待 |
1.互斥 2.不可抢占 3.占有且等待 4.循环等待 |
五.死锁预防
通过约束资源请求,使4个死锁条件至少有一个破坏。
1.互斥
第一个条件:互斥不可能禁止
2.占有且等待
为预防占有且等待的条件,可以要求进程一次性的请求所有需要的资源,并且阻塞这个进程直到所有请求同时满足。这个方法是低效的。首先,这个进程可能被阻塞很长的时间,而实际上,只要有一部分的资源就可以继续执行。其次,分配给一个进程的资源可能有相当长的一段时间不会被使用,且在此时间他们不能被其他进程使用。第三,一个进程可能实现不会之塔他所需要的所有资源。
3.不可抢占
预防这个条件,首先,如果占有某些资源的一个进程进一步申请资源被拒,则该进程必须释放他最初占有的资源,如果有必要,可再次申请这些资源和另外的资源。另一个方法是,如果一个进程请求当前进程被另一个进程占有的一个资源,则操作系统可以抢占另一个进程,要求他释放资源。只有在任意两个进程的优先级都不相同的条件下,后一种方法才可以预防死锁。
此外,只有在只有状态可以很容易保存和恢复的情况下,这种方法才是实用的。
4.循环等待
循环等待的预防方法是低效的,它会使进程执行速度变慢,并且可能在没有必要的情况下拒绝只有访问。
六、死锁避免
死锁避免允许3个必要条件,通过明智的选择。确保永远不会到达死锁点,它允许更多的并发,死锁避免需要知道将来的进程资源请求的情况。
1.如果一个进程的请求会导致死锁,则不启动此进程。
2.如果一个进程增加的资源请求会导致死锁,则不允许此分配。
六、UNIX并发机制
1.管道
管道是一个环形的缓冲区,允许两个进程以“生产者/消费者”模式进行通信。因此只是一个FIFO队列,由一个进程写,一个进程读。
管道在创建时获得一个固定的大小字节数。当一个进程试图往管道中写时,如果有足够的空间,则写请求被立即执行;否则被阻塞。如果一个读进程在试图读取的字节数多于当前管道中的字节数时,他也被阻塞;否则读操作立即执行。操作系统实施强制互斥,一次只能有一个进程可以访问管道。
有两类管道:有名管道、无名管道。只有有血缘关系的进程才可以共享匿名管道,而无关的进程只能共享有名管道。
2.消息
每个进程都有一个与之相关联的消息队列。消息的发送者指定每一个信息的类型。接受者可以按照先进先出的顺序接受信息,或者按照类型接受。当进程试图给一个满队列发送信息时,会阻塞;当进程从一个空队列中读取信息时也会被阻塞;如果一个进程试图读取某一特定类型的信息,但由于现在还没有这种类型而失败时,该进程不会阻塞住。
3.共享内存
速度最快的通信手段。这是虚存中由于多个进程共享一块公共内存块。进程读写共享内存所使用的机器指令与读写虚拟内存空间的其他部分多实用的指令相同。每个进程只有一个只读或读写权限。互斥约束不属于共享内存机制的一部分,但是必须由共享内存的进程提供。
4.信号量
5.信号
七、LINUX内核并发机制
1.原子操作
这些操作用来避免简单的竞争条件。原子操作执行时不会被打断或者被干涉。
2.自旋锁
3.信号量
4.屏障