注本课件摘抄自吉林大学多核课程课件
1. 进程定义:进程是具有一定独立功能的程序关于一个数据集合的一次运行活动。可表示成四元组(P, C, D, S)
P:程序代码
C:进程的控制状态
D:进程的数据
S:进程的执行状态。
2. 进程的组成
进程控制块:由于进程控制块中包含程序的地址信息,通过它可以找到程序在内存或外存的存放地址,也就找到了整个进程. PCB存于系统空间,只有操作系统能够对其存取,用户程序不能访问. 实际上用户甚至感觉不到PCB的存在
程序:进程的“躯体”,其中包括代码和数据两个部分. 现代操作系统都支持程序共享的功能,这就要求代码是“纯”的,即在运行期间不修改自身。数据一般包括静态变量、动态堆和动态栈。
3. 进程控制块(Process Control Block,PCB):标志进程存在的数据结构,其中包含系统对进程管理需要的全部信息。
进程标识
用户标识
进程状态
调度参数
现场信息
家族联系
程序地址
当前打开文件
消息队列指针
资源使用情况
进程队列指针
4. 进程的表示
5. 进程的状态
运行态(Run):进程占有处理机资源,正在运行;
就绪态(Ready):进程本身具备运行条件,但由于处理机的个数少于可运行进程的个数,暂未投入运行;
等待态(Wait):进程本身不具备运行条件,即使分给它处理机也不能运行,正在等待某一个事件的发生,如等待某一资源被释放,等待与该进程相关的I/O传输的完成信号等。
6. 进程间状态的转换
当一个就绪进程获得处理机时, 其状态由就绪变为运行;
当一个运行进程被剥夺处理机时, 其状态由运行变为就绪;
当一个运行进程因某事件受阻时, 如所申请资源被占用, 启动I/O传输未完成, 其状态由运行变为等待;
当所等待事件发生时,如得到申请资源, I/O传输完成, 其状态由等待变为就绪。
7. 进程的队列
为实现对进程的管理,系统需要按照某种策略将进程排成若干队列,由于PCB是进程的代表,因而进程队列实际上是由进程PCB构成的队列。因为该队列通常由链的形式实现的,所以也称PCB链 。系统中的进程队列分为如下三类:就绪队列 、等待队列、运行队列。
就绪队列
整个系统一个。所有处于就绪状态的进程按照某种组织方式排在这一队列中。
等待队列
每个等待事件一个,当进程等待某一事件时,进入与该事件相关的等待队列中;当某事件发生时,与该事件相关的一个或多个进程离开相应的等待队列,进入就绪队列。
运行队列
在单CPU系统中只有一个,在多CPU系统中每个CPU各有一个,每个队列中只有一个进程,指向运行队列头部的指针被称作运行指示字。
8. 进程的创建与撤销
进程创建
建立一个PCB,并对其内容进行初始化;
为该进程分配必要的存储空间,并加载所要执行的程序;
将PCB送入就绪队列。
进程撤销
完成使命的进程需要终止自己并告知操作系统,系统将对进程进行善后处理(收集进程状态信息、通知其父进程等),之后将收回进程所占有的所有资源(打开文件、内存等),最后撤销其PCB 。非正常终止也将进入操作系统进行善后处理。
9. 进程间的相互联系与作用
多道系统中同时运行的并发进程一般有多个,在逻辑上,这些进程之间可能存在某种联系,也可能相对独立
相关进程:在逻辑上具有某种联系的进程称作相关进程;
无关进程:在逻辑上没有任何联系的进程称作无关进程;
并发进程之间存在相互制约的关系,这种相互制约的关系称作进程间的相互作用. 进程间相互作用的方式有两种:即直接相互作用和间接相互作用
直接相互作用:进程之间不需要通过中间媒介而发生的相互作用,这种相互作用通常是有意识的;
间接相互作用:进程之间需要通过某种中间媒介而发生的相互作用,这种相互作用通常是无意识的。
10. 多线程概念
线程为轻量级进程(lightweight process, LWP),也是CPU调度和分派的基本单元。进程也被称为重量级进程(heavyweight process, HWP)只拥有一个线程的进程。如果进程有多个控制线程,那么它就能同时执行多个任务。
- 进程和线程的区别
进程 – 是指程序在一个数据集合上运行的过程,是系统进行资源分配和调度运行的一个独立单位,有时也称为活动、路径或任务。进程是资源的分配单位。
线程 – 是进程中的一个执行流,是处理机调度的基本单元。每个程序至少包含一个线程,那就是主线程。线程自己只拥有很少的系统资源(如程序计数器、一组寄存器和栈),但它可与同属一个进程的其他线程共享所属进程所拥有的全部资源,同一进程中的多个线程之间可以并发执行,从而更好地改善了系统资源的利用率。
如果说在操作系统中引入进程的目的,是为了在多道程序系统中正确的描述程序并发的执行过程;那么,在操作系统中再引入线程则是为了减少程序并发执行时所付出的时空开销,使操作系统具有更好的并发性
线程是“进程中的一条执行路径或线索”或“进程中的一个可调度实体”。
- 线程的优点:
上下文切换速度快:由同一进程中的一个线程切换到另一个线程只需改变寄存器和栈,包括程序和数据在内的地址空间不变;
系统开销小:创建线程比创建进程所需完成的工作少,因而对于客户请求,服务器动态创建线程比动态创建进程具有更高的响应速度;
通讯容易:由于同一进程中的多个线程地址空间共享,一个线程写到数据空间的信息可以直接被该进程中的另一线程读取,方便快捷 ;
终止一个线程比终止一个进程的代价要小。
调度:
在传统的操作系统中,CPU调度和分派的基本单位是进程。而在引入线程的操作系统中,则把线程作为CPU调度和分派的基本单位,进程则作为资源拥有的基本单位,从而使传统进程的两个属性分开,线程便能轻装运行,这样可以显著地提高系统的并发性。同一进程中线程的切换不会引起进程切换,从而避免了昂贵的系统调用。但是在由一个进程中的线程切换到另一进程中的线程时,依然会引起进程切换。
并发性:
在引入线程的操作系统中,不仅进程之间可以并发执行,而且在一个进程中的多个线程之间也可以并发执行,因而使操作系统具有更好的并发性,从而能更有效地使用系统资源和提高系统的吞吐量。例如,在一个未引入线程的单CPU操作系统中,若仅设置一个文件服务进程,当它由于某种原因被封锁时,便没有其他的文件服务进程来提供服务。在引入了线程的操作系统中,可以在一个文件服务进程中设置多个服务线程。当第一个线程等待时,文件服务进程中的第二个线程可以继续运行;当第二个线程封锁时,第三个线程可以继续执行,从而显著地提高了文件服务的质量以及系统的吞吐量。
系统开销:
不论是引入了线程的操作系统,还是传统的操作系统,进程都是拥有系统资源的一个独立单位,它可以拥有自己的资源。
一般地说,线程自己不拥有系统资源(也有一点必不可少的资源),但它可以访问其隶属进程的资源。即一个进程的代码段、数据段以及系统资源(如已打开的文件、I/O设备等),可供同一进程的其他所有线程共享。
拥有资源:
由于在创建或撤消进程时,系统都要为之分配或回收资源,如内存空间、I/O设备等。因此,操作系统所付出的开销将显著地大于在创建或撤消线程时的开销。类似地,在进行进程切换时,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置。而线程切换只需保存和设置少量寄存器的内容,并不涉及存储器管理方面的操作。
可见,进程切换的开销远大于线程切换的开销。此外,由于同一进程中的多个线程具有相同的地址空间,致使它们之间的同步和通信的实现也变得比较容易。在有的系统中,线程的切换、同步和通信都无需操作系统内核的干预。
线程的结构:
线程的组成
线程控制块(Thread Control Block,TCB)
线程控制块是标志线程存在的数据结构,其中包含系统对于线程进行管理所需要的全部信息
线程的应用:
许多任务在逻辑上涉及多个控制流,控制流具有内在的并发性,当其中一些控制流被阻塞时,另外一些控制流仍可继续. 在没有线程支持的条件下,只能采用单进程或多进程模式,单进程不能表达多控制流,多进程开销大而且在无共享存储空间的条件下进程间交往困难. 采用多线程一方面可以提高应用程序的并行性,另一方面也使程序设计简洁明晰。
例如:Word文字编辑工具、Web服务器等。
某些应用需要处理几个相似或者并行的几个任务,创建进程需要给每个进程分配资源,用多进程来实现上述工作很不经济,响应时间长。
如在C/S模式下,web server为每个用户连接运行一个线程;
RPC服务器中,RPC服务进程会开启多个线程服务于每个RPC request
某些应用可以分成若干相对独立的部分,每一部分用一个线程来实现
在多处理器系统上,如果一个应用具有如下特征,就可以利用多线程技术达到目标:
前台后台操作;
异步处理;
需要加速执行;
模块化程序结构
多线程的优点:
创建一个线程比创建一个进程的代价要小
如 solaris中创建线程比进程快30倍,
线程的切换比进程间的切换代价小
由同一进程中的一个线程切换到另一个线程只需改变寄存器和栈,包括程序和数据在内的地址空间不变;
如 solaris线程切换比进程切换快5倍
充分利用多处理器
提高了程序执行并行度,降低了并行粒度
数据共享
由于同一进程中的多个线程地址空间共享,一个线程写到数据空间的信息可以直接被该进程中的另一线程读取,方便快捷
快速响应特性
多核处理器的基本结构是共享存储的,多线程程序设计技术被认为是能够充分挖掘共享存储系统性能潜力的最有效的技术。用户级线程和内核级线程
用户级线程
用户空间实现
有关线程的所有管理工作都由在用户级实现的线程库来支持
优点:
用户级线程的创建和管理等操作无须内核参与,操作更快
对于那些内核本身并不支持多线程的操作系统来说,通过用户级线程库可以使用户获得多线程编程的好处
缺点:
同一进程中的多个线程不能真正并行;
由于线程对操作系统不可见,调度在进程级别,某进程中的一个线程通过系统调用进入操作系统受阻,该进程的其它线程也不能运行 。
内核级线程
内核级线程的所有管理操作都是由操作系统内核完成的
特点
并行性高,
多个线程可被同时调度
充分利用多处理器
创建和管理代价高
核心级别线程的优点是并发性好,在多CPU环境中同一进程中的多个线程可以真正并行执行
核心级别线程的缺点是线程控制和状态转换需要进入操作系统完成,系统开销比较大.
多线程的映射模型
对于实现了用户级线程和内核级线程的操作系统,用户级线程和内核级线程之间的可以有不同的映射方式
多对一模型
把多个用户级线程映射到一个内核级线程。
用户级线程的管理在用户空间实现,所以效率高
当一个线程因调用系统调用被阻塞时,整个进程被阻塞。另外,用户级线程不能在多处理器上并发执行,不支持内核级线程的操作系统使用多对一模型。
多对一模型
一对一模型
一对一模型把每个用户级线程映射到一个内核级线程(纯核心级线程)。
当一个线程阻塞时,其他线程仍然可以运行。
内核资源会限制系统中线程的数目。
例如:Windows 95/98/NT/2000,OS/2
多对多模型
多对多模型将m个用户级线程映射到n个内核级线程,m≥n。
用户可以创建所需要的用户级线程,通过分配适当数目的内核级线程获得并发执行的优势并节省系统资源。
线程池
多线程编程的问题
重复创建和销毁线程,如:网络服务器,长时间重复该过程要耗费大量的处理器时间
为每个请求创建一个线程,大量的创建线程可能会耗尽系统资源
解决方式:线程池
一组被创建的线程的集合,需要线程时从池中取出,使用结束后归还线程池。当线程池中没有可用线程时,创建线程的请求必须等待,保护系统资源不被耗尽。
线程池中的线程数目可以依据系统的内存、处理器数目、待处理的任务数目来确定,或者动态地依据系统当前资源和系统负载进行调整。
线程的生命周期
线程的标识
通常用一个整数来标识一个线程
在Pthread线程库中,以pthread_t类型来标识一个线程
线程的创建(c程序)
首先运行的是从main函数开始的主线程或初始线程
在pthread函数库中调用pthread_create创建一个新的线程
线程的终止
线程的启动函数执行完毕,或者调用了pthread_exit 导致线程终止
主线程退出导致整个进程会终止 ,主线程一般会在其它线程退出后再结束。
线程的状态
线程的状态
就绪(ready):线程等待可用的处理器。
运行(running):线程正在被执行。
阻塞(blocked):线程正在等待某个事件的发生(比如I/O的完成,试图加锁一个被上锁的互斥量)。
终止(terminated):线程从起始函数中返回或者调用pthread_exit。
线程状态的转换
线程的同步
由于线程共享同一进程的内存空间,多个线程可能需要同时访问同一个数据。对共享数据的并发访问如果没有正确的保护措施,可能导致数据的不一致性和错误。为避免不确定的错误结果出现,在两个线程访问共享数据的时候需要同步机制。
竞争条件
若干进程并发地访问并且操纵共享数据的情况。
共享数据的值取决于哪个进程最后完成
防止竞争条件,并发进程必须被同步
常用的同步机制
临界区
信号量
互斥量
管程
线程Producer: 线程Consumer:
counter– ; counter++;
register2=counter; register1=counter;
register2=register2-1; register1=register1+1;
counter=register2; counter=register1;
假定counter的原始值是5,则可能的执行结果:
Producer: register1=counter {register1=5}
Producer: register1=register1+1 {register1=6}
Consumer: register2=counter {register1=5}
Consumer: register2=register2-1 {register1=4}
Producer: counter=register1 {counter=6}
Consumer: counter=register2 {counter=4}
顺序程序
顺序程序,程序的顺序性包括内部顺序性和外部顺序性。
内部顺序性:对于一个进程来说, 它的所有指令是按序执行的;
外部顺序性, 对于多个进程来说, 所有进程是依次执行的。
P1活动: a1 a2 a3 a4,P2活动: b1 b2 b3 b4
顺序执行时, 有如下两种情形:
情形1: a1 a2 a3 a4 b1 b2 b3 b4
情形2: b1 b2 b3 b4 a1 a2 a3 a4
顺序程序的特性:
顺序性:处理机严格按照指令次序依次执行,即仅当一条指令执行完后才开始执行下一条指令;
封闭性:程序在执行过程中独占系统中的全部资源,该程序的运行环境只与其自身动作有关,不受其它程序及外界因素影响;
可再现性:程序的执行结果与执行速度无关,而只与初始条件有关,给定相同的初始条件,程序的任意多次执行一定得到相同的执行结果.
程序的并发性含义:
内部并发性, 对于一个进程来说, 它的所有指令可能按序执行,也可能不按次序执行;
外部并发性: 对于多个进程来说, 所有进程是交叉(interleave)执行的. 例如, 对于上面P1和P2两个进程来说, 只考虑外部并发性,具有许多情形 :
情形1: a1 b1 b2 a2 a3 b3 a4 b4
情形2: b1 b2 a1 a2 a3 b3 b4 a4
并发进程在其执行过程中, 出现哪种交叉情形是不可预知的, 这就是并发程序带来的不确定性.
并发程序特性:
交叉性:程序并发执行对应某一种交叉,不同的交叉可能导致不同的计算结果,操作系统应当保证只产生导致正确结果的交叉,去除那些可能导致不正确结果的交叉;
非封闭性:一个进程的运行环境可能被其它进程所改变,从而相互影响;
不可再现性:由于交叉的随机性,并发程序的多次执行可能对应不同的交叉,因而不能期望重新运行的程序能够再现上次运行的结果.
例:一个图书馆管理系统, 连有两个终端, 用户可通过终端借书. 为简化问题, 假设所有用户借阅的图书是相同的. 设x代表图书的剩余数量, 为两个终端用户服务的程序
竞争条件
竞争条件(Race Conditions):多个线程间可能会共享一些彼此都能够读写的公用存储区,它可能在内存中,也可能是一个共享文件,当两个或多个进程试图在同一时刻访问共享内存,或读写某些共享数据,而最后的结果取决于线程之行的顺序。
例:“抢椅子”游戏,三个人伴随着音乐围着两把椅子转,当音乐暂停时,便立即找空的椅子坐下。没有抢到椅子的人被淘汰,这时,三个人就对应着三个不同的线程,椅子就是他们共享的区域。最后区域的值是多少完全取决于人被淘汰的顺序。
临界区域
多个进程均需要访问的变量称为公共变量(shared variable) ;
访问共享变量的程序段称作临界区域(critical region), 也称为临界段(critical section)
共享变量可能属于操作系统空间,也可能属于用户进程空间。对于前者, 其临界区域亦属于操作系统空间,而对于后者, 其临界区域则属于用户进程空间。
所有n 个进程竞争使用一些共享的数据,每个进程有一个代码段, 称为临界区, 在那儿共享数据被访问。
问题:保证当一个进程正在临界区执行时,没有另外的进程进入临界区执行
解决临界区问题需满足
如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入。
任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待。
进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区。
如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。
一个典型进程 Pi 的结构
process Pi (other process Pj)
do {
entry section 进入
critical section 临界区
exit section 退出
reminder section 其他
} while (1);
进程为了同步彼此间的行为会共享一些变量。
临界区与进程互斥
两个或两个以上的进程不能同时进入关于同一组共享变量的临界区域, 其中有两层含意:
不容许多个进程同时进入关于同一组共享变量的相同的临界区域;
不容许多个进程同时进入关于同一组共享变量的不同的临界区域. 当然, 如果容许, 则有可能发生错误, 亦有可能不发生错误, 这与各个进程并发执行时的推进速度有关.
这是保证正确性的要求, 但这件事的实现应当由操作系统以及并发程序的设计者在编写程序时来保证, 而这需要有必要的互斥机制.
临界区域可以嵌套
具体实现
应当满足下面三个管理原则:
正确性原则(correctness):任意时刻至多只能有一个进程处于关于同一组共享变量的临界区域之中;
公平性原则(fairness):一个请求进入临界区的进程应当在有限等待时间内获得进入该临界区的机会;
进展性原则:当临界区空闲时,竞争进入临界区的多个进程在有限时间之内确定下一个进入临界区的进程.
临界区域的管理应当满足如下调度原则:
当关于某一组共享变量的所有临界区域均为空闲时, 一个要求进入该组共享变量某一临界区域的进程应当能够立即进入;
当关于某一组共享变量的某一临界区域被占用时, 一个要求进入该组共享变量某一临界区域的进程应当等待;
当一个进程离开关于某一组共享变量的某一临界区域时, 应当容许某一个等待进入关于该组共享变量某一临界区域的进程进入.
使用临界区的原则
每次只允许一个进程处于它的临界区(CS)中
若有多个进程同时想进入CS,应在有限时间内让其中一个进程进入CS,以免阻塞
进程在CS内只能逗留有限时间
不应使要进入CS的进程无限期地等待在CS之外
在CS之外的进程不可以阻止其他进程进入CS
不要预期和假定进程进展的相对速度以及可用的处理器数目,因为这是不可预期的。
信号量Semaphores
信号量被定义为一个整型变量,用两个原子的操作wait和signal来操作信号量
wait:
当执行wait时,如果信号量的值大于0,wait操作把信号量的值减1并返回;如果当前信号量的值为非正数则执行wait操作的线程会等待。
signal
signal操作对信号量的值加1,并唤醒哪些等待的进程
Multual-exclusion with semaphores
Shared data:
semaphore mutex; //initially
mutex = 1
Process Pi: do { wait(mutex);
critical section
signal(mutex);
remainder section
} while (1);
对于初始值为1的信号量,成对的wait和signal操作与互斥量的加锁、解锁的功能相似,可以实现多个线程对共享数据的安全访问。
和互斥量相比,信号量更加灵活,当信号量的初值大于1时,允许多个线程通过wait操作;信号量的值可以代表某类资源的数目,创建资源的线程调用signal,消耗资源的线程调用wait。
要等待某个条件成立(如x>y),当其他线程改变了共享变量的值时,线程应重新测试该条件 条件变量提供了阻塞线程和唤醒其他线程的方法
改变共享变量值的线程,给阻塞在条件变量上的线程发信号,唤醒该线程。
条件变量
条件变量的用法是先测试谓词,若不成立则调用pthread_cond_wait阻塞在该条件变量上。
Pthread定义的条件变量类型为pthread_cond_t,在某个条件变量上等待的操作是:
int pthread_cond_wait
(pthread_cond_t*cond,pthread_mutex_t*mutex);
int pthread_cond_signal (pthread_cond_t*cond);
等待谓词x>y成立的线程:
1 pthread_mutex_lock(&mut);
2 while(x<=y){
3 pthread_cond_wait(&cond, &mut);}
4 pthread_mutex_unlock(&mut);
更改x、y值线程:
1 pthread_mutex_lock(&mut);
2 /modify x,y/
3 if (x>y) pthread_cond_signal(&cond)
4 pthread_mutex_unlock(&mut);
进程互斥
定义:两个或两个以上的进程,不能同时进入关于同一组共享变量的临界区域, 否则可能发生与时间有关的错误, 这种现象被称作进程互斥.
进程互斥是进程之间所发生的一种间接性相互作用, 这种相互作用是进程本身不希望的, 也是运行进程感觉不到的. 进程互斥可能发生在相关进程之间, 也可能发生在不相关进程之间.
互斥量
互斥量作为一种互斥设备,有两个状态:上锁和空闲
对于一个已经被加锁的互斥量,当一个线程试图对它加锁时,该线程会被阻塞,直到该互斥量被释放。
互斥量在Pthread线程库中被定义为pthread_mutex_t类型。
互斥量作为一种互斥设备,有两个状态:上锁和空闲
对于一个已经被加锁的互斥量,当一个线程试图对它加锁时,该线程会被阻塞,直到该互斥量被释放。
互斥量在Pthread线程库中被定义为pthread_mutex_t类型。
Pthread线程库对一个互斥量的加锁操作是:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
producer对counter变量的操作要先加锁互斥量,完成对counter的操作之后释放互斥量
pthread_mutex_lock(&counter_mutex);
counter++;
pthread_mutex_unlock(&counter_mutex);
这两个函数都可以对一个互斥量加锁,如果互斥量当前是空闲的则被加锁,调用线程持有该互斥量并立即返回。
如果该互斥量已经被其他线程加锁,调用 pthread_mutex_lock的线程被阻塞,直到持有该互斥量的线程释放互斥量。
释放互斥量:pthread_mutex_unlock(pthread_mutex_t *mutex)
锁
类似信号量,同一时刻使用一个锁,对应两个原子操作:
Acquire():获取操作,将锁据为己有,状态-〉已加锁,如果锁被占,等待之;
Release():释放操作,将锁状态-〉未加锁。
互斥量是一种锁,线程对共享资源进行访问之前必须先获得锁,否则,线程保持等待状态,直到锁可用,只有其他线程都不占有它时,一个线程才可以占有它。占有锁的过程叫做锁定或者获得互斥量。
A Hello one
B Hello one
B Hello two
A Hello two
Mute mutex;
A Hello one B Hello one
A Hello two B Hello two
B Hello one A Hello one
B Hello two A Hello two
锁的粒度
锁的粒度是上锁后保护的共享数据的多少,当一个线程加锁并访问这些数据期间,其它线程无法访问。
减小锁的粒度可以提高对共享数据访问的并行性,可以更加充分利用并行处理能力。
假设多个线程需要存取一棵树,如果设定一个互斥量以保证对树的互斥访问,当一个线程加锁并访问该树期间,则其他线程无法访问。另一种方法是对树的每个节点设置一个互斥量,当访问不同的节点时,加锁不同的互斥量,除非两个线程试图访问同一个节点,多个线程可以同时访问树的不同节点,在多CPU或多核系统中可以充分地利用并行处理能力
同步
同步:一组进程(线程), 为了协调其推进速度, 在某些点处需要相互等待与相互唤醒, 进程之间这种相互制约的关系称作进程同步,简称同步(synchronization).进程同步是进程之间直接的相互作用形式, 是合作进程之间有意识的行为, 这种相互作用只发生在相关的进程之间。
进程合作(cooperation) :一组进程,如果它们单独不能正常进行, 但并发可以正常进行, 称这种现象为进程合作,参与合作的进程称作合作进程(cooperating process)。
同步的例子
要求:
(1)关车门后方能启动车辆;
(2)到站停车后方能开车门.
进程的同步机制
同步机制:用于实现进程间同步的工具称作同步机制,亦称同步设施(synchronization mechanism)
同步机制应当满足如下几个基本要求:
描述能力够用: 即用此种同步机制应当能够描述并发程序设计中所遇到的各种同步问题;
可以实现;
效率高;
使用方便.
进程通讯的模式
进程通讯主要有两种模式: 共享内存模式和消息模式。
共享内存模式
相互通讯的进程之间需要有公共内存, 一组进程向该公共内存中写, 另一组进程由该公共内存中读, 如此便实现了进程之间的信息传递。
需要解决两个问题:
为相互通讯的进程之间提供公共内存;
为访问公共内存提供必要的同步机制。
信息传递模式(通讯通过两个基本的系统调用命令, 即发送命令和接收命令)
直接方式
间接方式
直接方式:是指相互通讯的进程之间在通讯时直呼其名, 发送者在发送时要指定接收者的名字, 接收者在接收时要指定发送者的名字
两种系统调用形式:
对称形式——通讯形式的特点是一对一的,调用命令:
send(R,M):将消息M发给进程R;
receive(S,N):由进程S处接收消息至N。
非对称形式——通讯形式的特点是多对一的,调用命令:
send(R,M):将消息M发给进程R;
receive(pid,N):接受消息至N,返回pid为发送进程标识。
信息传递两种途径:
有缓冲途径
无缓冲途径
间接方式:是指相互通讯的进程之间在通讯时不是直呼对方名字, 而是指明一个中间媒体, 即信箱, 进程之间通过信箱来实现相互间的通讯. 此时, 系统所提供的高级通讯原语以信箱取代进程.发送和接收原语如下:
send(MB,M):将消息M发送到信箱MB;
receive(MB,N):由信箱MB中接收消息至N
死锁
如果两个线程都各自加锁了一个互斥量,然后申请加锁对方的互斥量,这样会陷入阻塞,这样的话,这两个线程永远不会从阻塞中恢复,此时,这两个线程处于死锁状态。
定义:一组进程(线程)中的每个进程(线程)均等待此组进程(线程)中某一其它进程(线程)所占有的, 因而永远无法得到的资源, 这种现象称作进程死锁。
参与死锁的进程个数至少为二;
参与死锁的所有进程均等待资源;
参与死锁的进程至少有两个占有资源;
参与死锁的进程是系统中当前正在运行进程之集合的一个子集。
死锁
两个线程分别拥有锁,并等待对方的锁
死锁避免
线程以相同的次序进行加锁
使用非阻塞的加锁操作
饿死
饥饿 – 由于分配策略不公平造成线程长期期地等待
进程可能永远无法从它等待的信号量队列中移去
当等待时间给进程推进和响应带来明显影响
当饥饿到一定程度的进程所赋予的任务即使完成也不再具有实际意义时称该进程被饿死。
活锁:在忙式等待条件下发生的饥饿
死锁的类型
竞争资源引起的死锁——由于进程争夺使用系统中有限的资源而引起;
进程通讯引起的死锁
其它原因引起的死锁
死锁的条件
资源独占;
不可剥夺;
保持申请;
循环等待
预先分配策略
思想:进程在运行前 一次性地向系统申请它所需要的全部资源,系统能满足呢,则一次性地将所请求的全部资源分配给申请进程;若系统当前不能满足进程的全部资源请求,则就不分配资源,此进程暂不投入运行。
分析:由于运行前已经获得所需的全部资源,因而在运行期间不会再发出新的资源请求,所以不会发生占有资源又申请资源的现象,破坏了保持申请这一死锁必要条件,故死锁不发生。
缺点:
资源利用率低 ;
有些资源有可能根本就不用 ;
有可能发生饥饿 。
有序资源分配
将每个资源类按一定原则编号,进程对资源的申请应严格按照资源编号由小到大的次序来进行,即,当进程不占有任何资源时,它可以申请任何资源类ri中的任意多个资源实例,但此后它可以申请另外一个资源类rj中的若干资源实例的充要条件是:f(ri)