并发进程
顺序程序设计
顺序性不但指一个程序模块内部,也指两个程序模块之间
- 内部顺序性
一个程序在处理器上的执行是严格按序的,只有前一个操作结束后,才能开始后继操作
- 外部顺序性
一个计算任务需要若干不同程序完成,这些程序也按照调用次序严格有序执行
顺序程序设计特点
- 程序执行的顺序性
在处理器上是严格按序执行的
- 程序环境的封闭性
运行程序独占全机资源,资源状态只能由此程序本身决定和改变,也不受外界因素影响
- 执行结果的确定性
执行过程中允许出现中断,但中断不会对程序最终结果产生影响,程序执行结果与执行速度无关
- 计算过程的可再现性
重复执行程序会获得相同的执行过程和计算结果
- 并发打破了程序执行的顺序性
进程的并发性
- 进程执行的并发性
一组进程的执行在时间上是重叠的,即多个程序处于都已开始执行但都未执行完成状态
- 可交替进行
从宏观上看,并发性反映一个时间段中几个进程都在同一处理器上,处于运行还未运行结束状态
从微观上看,任一时刻仅有一个进程在处理器上运行
并发的本质
一个处理器在几个进程之间的多路复用,是对有限物理资源强制行使多用户共享,消除计算机部件之间的互等现象,以提高系统资源利用率
并发程序设计 (concurrent programming)
- 一个程序被设计成可与其他程序并发执行的程序设计方法
并发程序特点
- 对于单处理器系统,可让处理器和各I/O设备同时工作,发挥硬部件的并行能力
- 对于多处理器系统,可让各进程在不同处理器上物理地并行,加快计算速度
简化程序设计任务
并发进程的分类
- 无关的并发进程
并发进程分别在不同的变量集合上操作,一个进程的执行与其他并发进程的进展无关
一个进程不会改变另一个与其并发执行的进程的变量
- 交互的并发进程
并发进程共享某些变量,一个进程的执行可能会影响其他进程的执行结果
交互的并发进程之间具有制约关系,进程的交互必须是有控制的,否则会出现不正确的计算结果
Bernstein条件
并发进程的无关性是进程的执行与时间无关的一个充分条件
只要要满足Bernstein条件,并发执行的程序就可以保持封闭性和可再现性
与时间有关的错误
对于一组交往的并发进程,执行的相对速度无法相互控制,各种与时间有关的错误就可能出现,甚至无法再现
- 结果不唯一:飞机售票问题
- 永远等待:内存资源管理问题
进程的交互
竞争关系
- 原本不存在逻辑关系的多个进程因共享一套计算机系统中的资源而产生交互和制约关系
- 是一种间接制约关系,又称互斥关系(mutual exclusion)
- 进程互斥指若干个进程因相互争夺独占型资源时所产生的竞争制约关系
- 竞争资源的独立进程之间并不交换信息,一个进程的执行可能影响到竞争资源的其他进程
资源竞争的两个控制问题
- 死锁(Deadlock)问题:因争夺资源陷入永远等待的状态
- 饥饿(Starvation) 问题:由于其他进程总是优先于它,而被调度程序无限期地拖延而不能被执行
解决途径:分配策略
协作关系
- 是一种直接制约关系
- 进程为完成同一任务需要分工协作,由于每个进程都独立地以不可预知的速度推进,在执行的先后次序上就要有约束,需要相互协作的进程在某些关键点上协调各自的工作
进程同步(synchronization)指两个以上进程基于某个条件来协调它们的活动
- 一个进程的执行依赖于协作进程的消息或信号
- 当一个进程没有得到来自于协作进程的消息或信号时需等待,直到消息或信号到达才被唤醒
进程互斥关系是一种特殊的进程同步关系,即逐次使用互斥共享资源,是对进程使用资源次序上的一种协调
临界区管理
临界区(critical section)
临界资源
- 共享变量代表的资源,即一次仅能供一个进程使用的资源
竞争条件(race condition)
- 当多个并发进程访问临界资源时,结果依赖于它们执行的相对速度,便称出现了竞争条件
与同一变量有关的临界区分散在各进程的程序段中,而各进程的执行速度不可预知
- 如果保证进程在临界区执行时,不让另一个进程进入临界区,即各进程对共享变量的访问是互斥的,就不会造成与时间有关的错误
互斥与临界区
临界区调度原则(Dijkstra)
- 一次至多一个进程能够进入临界区内执行
- 如果已有进程在临界区,其他试图进入的进程应等待
- 进入临界区内的进程应在有限时间内退出,以便让等待进程中的一个进入
总结
- 互斥使用、有空让进
- 忙则等待、有限等待
- 择一而入、算法可行
实现临界区管理的几种尝试
软件方法实现临界区管理(并发进程在单处理器或共享内存的多处理器计算机系统执行)
硬件假设
- 通常假设具有存储器访问级的基本互斥性
- 对内存中的同一个单元同时访问时,必定由存储器进行仲裁使其串行化
硬件、操作系统或语言未提供任何支持
采用标志方式,即用标志来表示哪个进程可进入临界区
- 为每个进程设置相应的标志位
- 进程在临界区时设置其值为true,不在时为false
- 进入临界区前先测试其他进程的标志位
实现临界区管理的软件方法
Peterson算法
- 为每个进程设置标志,当标志值为true时表示此进程要求进入临界区
- 另外,再设置一个指示器turn以指示可以由哪个进程进入临界区
- 当turn=i时,则可由进程Pi进入临界区
本质:inside声明自己需要使用, turn谦让其他进程使用
turn作为共享变量,不可能同时为不同的数,所以一定可以有一方执行。
实现临界区管理的硬件设施
关中断
实现互斥的最简单方法
- 进入临界区时关中断,进程退出临界区时开中断,这样进程的执行再也不会被打断
Linux系统:
- 关中断内核函数:cli()(clear interrupt)
- 开中断内核函数:sti()(set interrupt)
关中断方法的缺点
- 不适合作为通用的互斥机制,关中断的时间过长会影响性能和系统效率
- 不适用于多处理器系统,一个处理器关中断,并不能防止进 程在其他处理器上执行相同的临界区代码
- 若将这项权力赋予用户也存在危险,如果用户未开中断,则系统可能因此而终止
测试并设置指令
测试并设置机器指令TS(Test and Set)
- 可把这条指令看做函数,它有布尔型参数x和返回条件码
- 当TS(&x)测到x值为true时,则置x为false,且根据所测试到的x值形成条件码
bool TS(bool &x) {
if(x) {
x=false;
return true;
}
else
return false;
}
TS指令实现进程互斥
- 把一个临界区与一个布尔型变量s相关联
- 变量s代表临界资源的状态,把它看成一把锁,s的初值置为true,表示没有进程在临界区内,资源可用
- 系统利用TS指令实现临界区的上锁和开锁原语操作
本质:TS基于原语单向修改成false,其他进程单项修改成true
对换指令
对换指令的功能是交换两个字的内容
void SWAP(bool &a, bool &b) {
bool temp=a;
a=b;
b=temp;
}
SWAP可简单而有效地实现互斥,方法是
- 为每个临界区设置布尔型锁变量,如lock
- 当其值为false时,表示无进程在临界区内
本质:任意时刻只让一个进程拥有false(其实建立在指令为不可分割的最小执行单元?也不太准确,单条指令是否一定会被完整执行和指令本身功能,处理器设计等有关系,所以更好的是把swap理解为原语?)
信号量与PV操作
同步和同步机制
生产者—消费者问题(producer-consumer problem)
- 是计算机操作系统中并发进程内在关系的一种抽象,是典型的进程同步问题
- 在操作系统中,生产者进程可以是计算进程、发送进程;而消费者进程可以是打印进程、接收进程等等
- 解决好生产者—消费者问题就解决好了一类并发进程的同步问题
生产者和消费者进程的交替执行会导致进程永远等待:
- 当consumer检测到counter = 0后,进程切换到producer生产产品,但是其wakeup signal在consumer没有睡眠时是无效的,当producer在缓冲区快速生产满产品后会sleep,此时切换回consumer,consumer执行sleep,造成所有进程睡眠等待。
问题分析
- 不正确结果并不是因为并发进程共享缓冲区,而是因为它们访问缓冲区的速度不匹配
解决途径
信号量与PV操作
前面方法解决临界区调度问题的缺点
- 软件算法:太过复杂,效率低下
- 硬件方法:采用忙式等待测试法,浪费CPU时间
- 将测试能否进入临界区的责任推给各个竞争的进程会削弱系统的可靠性,加重用户编程负担
信号量和P、V操作(E.W.Dijkstra,1965年)
信号量(semaphore) :一种软件资源
- 类似交通信号灯机制,让多个进程通过特殊变量展开交互
- 一个进程在某一关键点上被迫停止执行直到接收到一个对应的特殊变量值
- 信号量在操作系统中主要用于封锁临界区、进程同步及维护资源计数
- 操作系统中,信号量表示物理资源的实体,它是一个与队列有关的整型变量
- 实现时,信号量是一种记录型数据结构,有两个分量
信号量的值
信号量队列的队列指针
信号量只能由同步原语PV对其操作
- 原语:内核中执行时不可被中断的过程(通常由多条特定指令组成)
- P操作原语和V操作原语:用于发送和接收信号
- 若协作进程的相应信号仍未送到,则进程被挂起直至信号到达为止
- 利用信号量和PV 操作既可解决并发进程竞争问题,又可解决并发进程协作问题
信号量分类
按用途分类
- 公用信号量:联系一组并发进程,相关进程均可在此信号量上执行PV操作
初值置为1,用于实现进程互斥
- 私有信号量:联系一组并发进程,仅允许此信号量所拥有的进程执行P操作,而其他相关进程可在其上施行V操作
初值往往为0或正整数,多用于并发进程同步
按取值分类
- 二元信号量:主要用于解决进程互斥问题
- 一般信号量:又称计数信号量,允许取大于 1的整型值,主要用于解决进程同步问题
信号量实现互斥
- 信号量和PV操作可用来解决进程互斥问题
与TS指令相比较,PV操作也用测试信号量的方法来决定是否进入临界区
不同的是,PV操作只对信号量测试一次,而TS指令则必须反复测试,造成忙式等待
信号量解决五个哲学家就餐问题
哲学家就餐问题:死锁
解决办法1:
semaphore count=4;
semaphore fork[5];
for (int i=0;i<5;i++)
fork[i]=1;
Cobegin
process philosopher_i( ) {
while(true) {
think( );
P(count);
P(fork[i]);
P(fork[(i+1)%5]);
eat( );
V(fork[i]);
V(fork[(i+1)%5]);
V(count);
}
}
coend
解决办法二:
Semaphore mutex=1;
semaphore fork[5];
for (int i=0;i<5;i++)
fork[i]=1;
Cobegin
process philosopher_i( ) {
while(true) {
think( );
P(mutex);
P(fork[i]);
P(fork[(i+1)%5]);
V(mutex);
eat( );
V(fork[i]);
V(fork[(i+1)%5]);
}
}
coend
信号量解决生产者-消费者问题
生产者-消费者问题:同步问题
- 一个生产者、一个消费者共享一个缓冲区
- 多个生产者、多个消费者共享多个缓冲区
解决基本思路: 可设置两个信号量empty和full,其初值分别为1和0
- empty :生产者判断能否向缓冲区放入产品
- full:消费者判断能否从缓冲区取出产品
在对共享资源操作时,通过mutex的PV操作进出临界区
记录型信号量解决读者-写者问题
记录型信号量解决睡眠理发师问题
管程
信号量和PV操作缺陷
- 共享资源的管理分散在各个进程,不利于对临界资源管理
- 难以防止有意/无意的违法同步操作
管程(P. Hansen, C. Hoare)引入背景
- 在进程共享内存的前提下,集中和封装针对一个共享资源的所有访问
- 把相关的共享变量及其操作集中在一起统一控制和管理,可方便管理和使用共享资源
- 便于用高级语言来书写程序,也便于程序正确性验证
管程思想:
- 把分散在各进程中的临界区集中起来管理,并把共享资源用数据结构抽象地表示
- 由于临界区是访问共享资源的代码段,建立一个“秘书”程序(管程)管理到来的访问
管程属性
- 共享性:管程中的过程可被进程共享
- 安全性:管程的局部变量只能由该管程的过程访问,管程过程不可访问任何非局部于它的变量
- 互斥性:任一时刻,最多只能一个进程进入管程
管程Vs.进程
- 管程定义的数据结构是公用的,而进程定义的是私有的
- 管程把共享变量上的同步操作集中起来,而临界区却分散在每个进程中
- 管程是为管理共享资源而建立的,进程主要是为占有系统资源和实现系统并发性而引入
- 管程被欲使用共享资源的进程调用,管程和调用它的进程不能并行工作,而进程之间能并行工作,并发性是其固有特性
- 管程是语言或操作系统的成分,不必创建或撤销,而进程有生命周期,由创建而产生至撤销便消亡
管程Vs.信号量
信号量与管程的功能是等价的
死锁
如果在一个进程集合中的每个进程都在等待只能由该集合中的其他一个进程才能引发的事件,则称一组进程或系统此时发生死锁
死锁产生
- 进程推进顺序不当产生死锁
- PV操作使用不当产生死锁
- 资源分配不当引起死锁
- 临时性资源使用不加限制引起死锁
不仅与系统拥有的资源数量有关,而且与资源分配策略,进程对资源的使用要求以及并发进程的推进顺序有关
形成死锁的必要条件
- 互斥条件(mutual exclusion):进程互斥使用资源
- 占有和等待条件(hold and wait):进程在请求资源得不到满足而等待时,不释放已占有资源
- 不剥夺条件(no preemption):又称不可抢占,一个进程不能抢夺其他进程占有的资源
- 循环等待条件(circular wait) :又称环路条件,存在循环等待链,其中每个进程都在等待链中下一个进程所持资源
必要条件分析
- 前3个条件是死锁存在的必要条件,但不是充分条件
- 第4个条件是前3个条件同时存在时所产生的结果,故条件并不完全独立
死锁防止(deadlock prevention)
只要能破坏4个必要条件之一,就可以防止死锁
- 破坏第一个条件
使资源可同时访问而不是互斥使用
- 破坏第二个条件
静态分配
- 破坏第三个条件
采用剥夺式调度方法:申请资源未果时主动释放资源,然后去等待
- 破坏第四个条件
采用层次分配策略,将系统中所有资源排列到不同层次中
一个进程得到某层的一个资源后,只能再申请较高一层的资源
当进程释放某层的一个资源时,必须先释放占用的较高层资源
当进程获得某层的一个资源后,如果想申请同层的另一个资源,必须先释放同层中的已占用资源
按序分配策略:层次策略的变种
- 把系统的所有资源排一个顺序:系统若共有n个进程,共有m个资源,用ri表示第i个资源,于是这m个资源是r1, r2, ……, rm
- 规定如果进程不得在占用资源ri(1≤i≤m)后再申请rj(j
- 按这种策略分配资源时系统不会发生死锁
死锁避免(deadlock avoidance)
允许系统中同时存在前三个必要条件,通过合适的资源分配算法确保不会出现进程循环等待链,从而避免死锁
用银行家算法避免死锁:
- 操作系统(银行家)
- 操作系统管理的资源(周转资金)
- 进程(要求贷款的客户)
银行家算法——进程启动拒绝法:
- 系统中若要启动一个新进程工作,其对资源Ri的需求当前系统中所有进程对资源Ri的最大资源需求数加上启动的新进程的最大资源需求数不超过系统拥有的最大数
- 这个算法考虑最坏的情况,即所有进程同时要使用它们声明的资源最大需求数,因此很难是最优的
系统性安全的定义:在时刻T0系统是安全的,仅当存在一个进程序列P1, …, Pn,对进程Pk满足公式
Need[k,i] ≤Available [i]+ ∑Allocation[j,i],k=1, …, n,i=1, …, m
- 公式左边表示进程Pk尚缺少的资源
- Available[i](即Vi)是T0时刻系统尚可用于分配且为Pk所想要的那类资源数
- ∑Allocation[j,i]表示排在进程Pk之前的所有进程所占用的Pk所需要的资源总数
- 进程Pk所需资源若不能立即被满足,但在所有Pj(j=1,2,…,k-1)运行完成后可以满足
- 当Pk释放全部资源后,Pk+1也能获得资源以完成任务
银行家算法
基本思想
系统中的所有进程进入进程集合
- 在安全状态下系统收到进程的资源请求后,先把资源试探性分配给它
- 系统用剩下的可用资源和进程集合中其他进程还要的资源数作比较
在进程集合中找到剩余资源能满足最大需求量的进程,从而保证这个进程运行完毕并归还全部资源
把这个进程从集合中去掉, 系统的剩余资源将更多,反复执行上述步骤
- 最后检查进程集合
若为空,表明本次申请可行,系统处于安全状态,可实施本次分配
否则,有进程执行不完,系统处于不安全状态,本次资源分配暂不实施,让申请进程等待
死锁检测和解除(deadlock detection and recovery)
解决死锁问题的一条途径是死锁检测和解除
- 这种方法对资源分配不加任何限制,也不采取死锁避免措施
- 系统定时地运行死锁检测程序,判断系统内是否已出现死锁
- 如果检测到系统已发生死锁,再采取措施解除它:基于进程—资源分配图
进程-资源分配图
- Pi→Rj为请求边,表示进程Pi申请资源类Rj中的一个资源得不到满足而处于等待Rj类资源的状态
- Rj→Pi为分配边,表示Rj类中的一个资源已被进程Pi占用
确定死锁的方法:
- 如果进程-资源分配图中无环路,则此时系统没有发生死锁
- 如果进程-资源分配图中有环路,且每个资源类中仅有一个资源,则系统中发生死锁。此时,环路是系统发生死锁的充要条件,环路中的进程便为死锁进程
- 如果进程-资源分配图中有环路,且涉及的资源类中有多个资源,则
环路的存在只是产生死锁的必要条件而不是充分条件
进程-资源分配图简化:
- 如果能在进程-资源分配图中消去此进程的所有请求边和分配边,成为孤立结点
- 经一系列简化,使所有进程成为孤立结点,则该图是可完全简化的;否则则称该图是不可完全简化的
死锁定理
- 系统为死锁状态的充分条件是当且仅当该状态的进程-资源分配图是不可完全简化的
死锁检测vs死锁避免
- 死锁避免算法考虑检查每个进程还需要的所有资源能否满足
- 死锁检测算法仅要根据进程的当前申请资源量来判断系统是否进入不安全状态
死锁解除
- 结束所有进程的执行,重新启动操作系统
方法简单,但以前工作全部作废,损失很大
- 撤销所有死锁进程,解除死锁继续运行
逐个撤销陷于死锁的进程,回收其资源重新分派,直至死锁解除
- 剥夺死锁进程占用资源,但并不撤销它,直至死锁解除
可仿照撤销陷于死锁进程的条件来选择剥夺资源的进程
根据系统保存的检查点,让所有进程回退,直到足以解除死锁
这种措施要求系统建立保存检查点、回退及重启机制
检测到死锁时,如果存在未卷入死锁的进程,随着这些进程执行结束后有可能释放足够的资源来解除死锁
进程通信
并发进程之间的交互必须满足两个基本要求:同步和通信
进程通信IPC(InterProcess Communication):进程之间互相交换信息的工作
Unix基本通信机制
- 信号(signal)通信机制
- 管道(pipeline)通信机制
System V IPC通信机制
- 消息传递(message passing)通信机制
- 信号量(semaphore)通信机制
- 共享内存(shared memory)通信机制
进程通信的发展
UNAT&T的Bell与加大伯克利的BSD是两大主力
- Bell致力于改进传统的进程IPC,形成了SYSTEM Ⅴ IPC机制
- BSD在改进IPC的同时,把网络通信规程(TCP/IP)实现到UNIX内核中,出现了socket网络通信机制
信号通信机制
软中断 :
- 中断与异常要通过硬件设施来产生中断请求,称为硬中断
- 不必由硬件产生中断源而引发的中断称为软中断
- 软中断是利用硬中断的概念,采用软件方法对中断机制进行模拟,实现宏观上的异步执行
- “信号”是一种软中断机制
中断机制vs.信号机制
相同点
- 两者在概念上是“一致”的
- 两者都是“异步”的
- 两者均采用“向量表”实现
- 两者均有“屏蔽”设施
不同点
- 前者由硬件和软件相结合来实现,后者则完全由软件实现
- 中断向量表和中断处理程序(全部由系统提供)均位于系统空间
- 信号向量表虽然处于系统空间,但信号处理程序由应用程序提供,并在用户空间执行
管道通信机制
管道(pipeline)是连接读写进程的一个特殊文件
- 允许进程按先进先出方式传送数据,也能使进程同步执行操作
- 发送进程以字符流形式把大量数据送入管道,接收进程从管道中接收数据,所以叫管道通信
- 管道的实质是一个共享文件,基本上可借助于文件系统的机制实现,包括
(管道)文件的创建、打开、关闭和读写
匿名管道
匿名管道是半双工的
- 数据只能向一个方向流动
- 要求双向通信时,需要建立两个匿名管道
只能用于具有亲缘关系的进程通信
- 亲缘关系指具有共同祖先,如父子进程或者兄弟进程之间
匿名管道对于管道两端的进程而言,是一个特殊文件
- 写入的内容每次都添加在管道缓冲区的末尾,每次都是从缓冲区的头部读出数据
有名管道
又称FIFO,克服只能用于具有亲缘关系的进程之间通信的限制
- FIFO提供一个与路径名的关联,以FIFO的文件形式存在于文件系统中,通过FIFO不相关的进程也能交换数据
- FIFO遵循先进先出,对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾
共享内存通信机制
指两个或多个进程共同拥有一块内存区,该区中的内容可被进程访问
消息传递通信机制
在不同进程之间传递数据的机制
- 消息传递的复杂性
进程地址空间隔离,发送进程无法将消息直接复制到接收进程的地址空间中,只能由操作系统完成
- 消息传递基本原语(阻塞/同步问题)
发送消息:向一个给定目标进程发送一条消
接收消息:从一个给定源进程接收一条消息
- 消息传递特点
正在执行的进程间可在任何时刻发送/请求消息
消息传递机制与进程的阻塞和释放紧密联系
一个进程在某一时刻的执行依赖于另一进程的消息或等待其他进程对发出消息的回答
消息传递提供了进程同步能力,进一步扩充了并发进程间对数据的共享
间接通信
- 支持消息传递异步通信的形式
- 进程之间发送或接收消息通过共享数据结构信箱进行
- 间接通信基本原语
send(A,信件):把一封信件(消息)传送到信箱A
receive(A,信件):从信箱A接收一封信件(消息)
基于消息传递的进程通信同步
send()操作
- 同步/阻塞:发送进程执行send()后,等待接收进程回答消息后才继续进行
- 异步/非阻塞:发送进程执行send()后,将消息传送到接收进程的信箱后继续运行,直到某个时刻需要接收进程送来回答消息时,才查询和处理
receive()
- 同步/阻塞:接收进程执行receive()后直到消息交付完成,都处于等待消息的状态
- 异步/非阻塞:执行receive()后不要求接收进程等待,当它需要消息时,再接收并处理消息
通信进程常用同步组合方式
- 阻塞型send和阻塞型receive
同步通信、可靠通信
- 非阻塞型send和非阻塞型receive
设置消息队列容纳消息数
- 非阻塞型send和阻塞型receive
发送进程可把一条或多条消息发给目标进程
接收进程通常是服务器进程,平时处于阻塞状态,直到发送进程发来消息才被唤醒
System V IPC
IPC资源
具有相同类型的接口函数
- 信号量实现进程同步
- 消息队列以异步方式为通信频繁、但数据量少的进程通信提供服务
- 共享主存为数据量大的进程间通信提供服务
共同点
- 通过 System V IPC对象通信时,需传递该对象的唯一IPC标识符
- 访问System V IPC对象时必须经过许可检验
- System V IPC对象的访问权限由对象创建者设置
- IPC通信机制将IPC标识符作为对系统资源表的索引
条件变量
利用线程间共享的全局变量实现同步
条件变量使线程睡眠等待特定条件出现(无需轮询)
使用方法
- 通常条件变量和互斥锁同时使用
- 一个线程因等待“条件变量的条件成立”而挂起
- 另一个线程使"条件成立"(给出条件成立信号)
Linux同步和通信机制
- 原子操作:原子操作执行时不被打断,防止竞争条件的发生
- 等待队列:Linux内核信号量采用非忙式等待来实现
进程执行down()操作而等待时,将被放入等待信号量队列
等待队列也是临界资源,对其进行修改时应加锁
内核为每个事件都设立一个等待队列
- 内核信号量:一般信号量(使用等待队列机制);读者/写者信号量,写进程每次唤醒一个,读进程每次唤醒多个
- 自旋锁:原子操作能保证对变量操作的原子性,在单步内完成,但临界区可以跨越多个函数,原子操作对此无能为力 ,例如:
先从一个数据结构中读取数据,对其进行格式转换和解析,再写到另一个数据结构中
整个执行过程必须是原子的,数据更新完毕前,不能有其他代码读取这些数据
自旋锁分为:一般自旋锁、读者-写者自旋锁
- 关中断:关中断是把内核态执行的程序段作为一个临界区来保护
,主要保护中断处理程序也要访问的数据结构。
关中断还能禁止内核抢占
为防止死锁,关中断期间内核不能执行阻塞操作
在SMP环境中,关中断只能防止来自本机其他中断处理程序的并发访问,需要引入自旋锁在禁止本地中断的同时,防止来自其他机器的并发访问
一般自旋锁:
- 最多只能被一个可执行线程持有
- 自旋锁采用忙式等待,直至资源被解锁 ;二值信号量不必循环等待信号量,而是进入等待队列
读者写者自旋锁:
多进程可以并发地持有读锁,写锁仅有一个进程能持有
自旋锁作用:在SMP系统中,自旋锁最重要的特点是内核例程在等待锁被释放时一直占据着某个 CPU,用来保护那些只需简短访问数据结构的操作,如
从双向队列链表中增加或删除一个元素
因此,在内核需要进程同步的位置,自旋锁使用得比较频
Linux操作系统内核执行过程中,会造成并发执行,应提供多种内核同步机制
- 中断及异步信号:随时可能打断正在执行的内核代码
- 可抢占:如果内核具有抢占性,运行的内核任务会被另一个任务抢占
- SMP系统:两个或多个CPU同时执行内核代码,访问同一共享数据结构
操作系统并发问题解决方案小结