目录
1.4.1 允许发生进程上下文切换的4种情况(即进程调度时机)
1 进程及其实现
计算机工作起来后,其中存在各种活动,操作系统的重要任务之一就是对这些活动进行管理,进程是最基本的活动单位。
进程与程序是极其容易混淆的两个概念。两者虽然都包含程序,但是程序是静态的,进程是动态的,进程是程序执行时的动态过程。
同一个程序在一段时间内可以同时存在多个执行活动(进程),分别对不同的数据进行处理。这时,程序与进程之间存在一对多的关系。多个进程执行相同的程序,例如:用于计算阶乘的程序即可以计算15的阶乘,亦可以计算10的阶乘,两个计算工作(进程)可以同时进行,互不影响。虽然计算阶乘的具体数值不同,但是都执行同一个程序。这时,两个数值的计算活动不能称之为程序,因为“程序”这个名词无法将两者区分开。必须称这两个计算活动为进程,而且是不同的,相互独立的进程。
1.1 进程的定义和属性
1.1.1 进程的概念
进程:进程是一个可并发执行的、具有独立功能的程序关于某个数据集合的一次执行过程,也是操作系统进行资源分配和保护的基本单位。
该定义表明:进程至少包含程序和数据两部分。有些系统称进程为“任务”或“活动”。
1.1.2 进程的属性(特征)
- 结构性:进程包含了数据集合和运行在其上的程序。每个进程至少包含3个组成要素:程序块、数据块和进程控制块(PCB)。程序也具有结构性,外存上的程序文件包括文件头(也称为程序前缀控制块)、代码及数据结构部分。代码及数据结构部分定义了程序的功能,进程的程序块和数据块就来自与程序代码及数据结构部分。
- 共享性:同一程序运行于不同数据集合上构成不同的进程。多个不同的进程可以共享相同的程序,所以进程和程序不是一 一对应的。
- 动态性(进程最基本的特征):进程是程序的一次运行过程,由创建而产生,由撤销而消亡。进程运行时需要使用处理器、内存、外设、外存等资源。程序是静态的、不活动的,并不使用处理器、内存等执行资源。
- 独立性:进程是系统中资源分配和保护的基本单位,也是系统调度的独立单位。
- 制约性(异步性):并发进程之间存在制约关系,进程在执行的关键点上需要相互等待,互通消息。
- 并发性:在单处理器系统环境下,各个进程轮流占用处理器。
1.2 进程的状态与切换
1.2.1 三态模型
引起进程状态转换的具体原因:
运行态->等待态(阻塞态):等待使用资源或者某事件发生
等待态->就绪态:资源得到满足或事件发生
运行态->就绪态:运行时间片到或者出现更高优先级进程
就绪态->运行态:CPU空闲时选择一个就绪进程
1.2.2 五态模型
在三态模型上增加了新建态和终止态
进程状态转换的具体原因如下:
NULL->新建态:创建一个子进程
新建态->就绪态:系统完成进程创建操作,且当前系统性能和内存容量均允许接纳新进程
运行态->终止态:进程自然结束,或出现无法克服的错误,或被操作系统终结,或被其它有终止权的进程终结
终止态->NULL:完成善后工作,进程撤离系统
就绪态->终止态:父进程终结子进程,进程被强行终止
等待态->终止态:父进程终结子进程,进程被强行终止
1.2.3 具有挂起状态的七态模型
1.引入 “挂起” 状态的原因:
由于进程的不断创建,系统资源已不能满足进程运行的要求,必须把某些进程挂起,对换到磁盘镜像区中,暂时不参与进程调度,起到平滑系统负荷的目的。
2.引起进程挂起的主要原因
- 当系统中的进程均处于等待状态时,需要把一些阻塞进程对换出去,以腾出足够内存来装入就绪进程运行
- 进程竞争资源,导致系统资源不足,负荷过重,需要挂起部分进程以调整系统负荷,保证系统的实时性或使系统正常进行
- 将定期执行的进程(如审计、监控、记账程序)兑换出去,以减轻系统负荷
- 用户要求挂起自己的进程,以便进行某些调试,检查和改正
- 父进程要求挂起后代进程,以进行某些检查和改正
- 操作系统需要挂起某些进程,检查运行中资源的使用情况,以改善系统性能;或当系统出现故障或某些功能受到破坏时,需要挂起某些进程以排除故障
3.两个挂起状态
挂起就绪态:表明进程就绪但位于外存,待对换到内存后方可调度执行
挂起等待态:表明进程阻塞并位于外存
具有挂起状态的进程状态转换关系如下图:
4.引起进程状态转换的具体原因
- 等待态→挂起等待态:当前不存在就绪进程,至少一个等待态进程将被对换出去成为挂起等待态。
- 挂起等待态→挂起就绪态:引起进程等待的事件结束之后,相应的挂起等待态进程将转换为挂起就绪态。
- 挂起就绪态→就绪态:内存中没有就绪态进程,或挂起就绪态进程优先级高于就绪态进程,挂起就绪态进程将转换为就绪态。
- 就绪态→挂起就绪态:为了减轻系统负荷,满足运行进程的资源使用和性能要求,将就绪态进程对换出去成为挂起就绪态。
- 挂起等待态→等待态:当内存空间充足,某个挂起等待态进程优先级较高,并且导致该进程阻塞的事件即将结束时,该等待进程将被对换到内存中。
- 运行态→挂起就绪态:当一个高优先级挂起等待进程的等待事件结束后,它将抢占 CPU,而此时主存不够,从而可能导致正在运行的进程转化为挂起就绪态。
- 新建态→挂起就绪态:根据系统当前资源状况和性能要求,可以将新建进程对换出去成为挂起就绪态。
挂起的进程将不参与低级调度直到它们被对换到主存中。
5.挂起进程的特征
挂起进程不能立即被执行。
挂起进程所等待的事件独立于挂起条件,事件结束并不能导致进程具备执行条件。
进程进入挂起状态是由于操作系统、父进程或进程本身阻止它的运行。
结束进程挂起状态的命令只能通过操作系统或父进程发出。
1.3 进程描述
1.3.1 操作系统的控制结构
为了管理进程和资源,操作系统需要构造相应的数据结构来登记各个进程和资源信息, 同样类型的数据结构组成表,称为控制表。操作系统的控制表分为如下4类。
(1) 进程控制表:管理进程及其相关信息。
(2) 存储控制表:管理主存和外存,主要内容包括主存储器的分配信息(分配给进程 的内存),辅助存储器的分配信息(分配给进程的外存),存储保护和分区共享信息(哪些 进程可以访问共享内存区域),虚拟存储器管理信息。
(3) I/O控制表:管理计算机系统的I/O设备和通道,主要内容包括I/O设备和通道是 否可用,I/O设备和通道的分配信息,I/O操作的状态和进展,I/O操作传输数据所在的主 存区。
(4) 文件控制表:管理文件,主要内容包括被打开文件的信息,文件在主存储器和辅 助存储器中的位置信息,被打开文件的状态和其他属性信息。
以上4种表分别管理计算机系统中的硬件资源(如CPU、内存、I/O设备)和软件资 源(如文件),如下图所示:
进程是内存、I/O设备和文件资源的使用者”为了使操作系统能够跟踪进程对资源的 使用情况,内存表、I/O表和文件表需与进程表关联。主进程表的每项都分别包含一个指 向进程映像的指针。在执行期间,进程映像需要驻留内存。
1.3.2 进程实体(进程映像)的组成
进程实体包括:程序块、数据块、进程控制块和核心栈其中,程序块和数据块定义进程的行为和功能,进程控制块用于操作系统跟踪、管理进程,核心栈用于进程运行在核心 态下时跟踪过程调用和过程间参数传递信息。
1)进程控制块(PCB)
每一个进程都捆绑一个进程控制块,用来存储进程的标志信息、现场信息和控制信 息。进程创建时建立进程控制块,进程撤销时回收进程控制块,进程控制块与进程一一 对应。
2)程序块
程序块即被执行的程序,规定了进程一次运行应完成的功能。程序块通常是纯代码, 可被多个进程共享。
3)数据块
数据块是进程的私有地址空间,是程序运行时加工处理的对象,包括全局变量、局部 变量和常量、用户栈等的存放区,常常为一个进程专用。
4)核心栈
每一个进程都将捆绑一个核心栈,进程在核心态工作时使用,用来保存中断/异常现场, 保存函数调用的参数和返回地址。
进程实体的内容随着进程的执行不断发生变化,某时刻进程实体的内容及其状态集合 称为进程映像。
1.3.3 进程上下文
进程上下文:指进程物理实体和支持进程运行的环境。
UNIX进程上下文包括如图所示的3个组成部分。
- 用户级上下文:由正文(用户进程的程序块,只读,用于保存程序指令)、用户数据块、共享存储区和用户栈组成,它们占用进程的虚拟地址空间。用户栈用于保存用户态 下过程调用和返回地址、参数传递信息。共享存储区是与其他进程共享的数据区域,用于进程间的通信。
- 寄存器上下文:由程序状态字(PSW)寄存器、指令计数器、栈指针、控制寄存器、通用寄存器等组成。程序未运行时,上述信息保存在寄存器上下文中。
- 系统级上下文:由进程控制块、内存管理信息、核心栈等组成。核心栈用于进程在核心态执行时保存过程调用或中断返回时需恢复的信息。
1.3.4 进程控制块(PCB)
每个进程都有且只有一个进程控制块,进程控制块是操作系统用于记录和刻画进程状态及有关信息的数据结构,也是操作系统掌握进程的唯一结构,是操作系统控制和管理进程的主要依据,是进程存在的唯一标识。它包括了进程执行时的情况,以及进程让出处理 器后所处的状态、断点等信息。
进程控制块各部分的信息所在位置及其与进程实体其他部分的关系,如下图
1.3.5 进程队列及其管理
进程队列:处于同一状态的所有PCB链接在一起的数据结构称为进程队列(Process Queues )。
进程队列的排队原则如下。
- 同-状态下进程的PCB按先来先到、优先级或其他原则排成队列。
- 等待态进程队列按照等待原因细分为多个队列。
进程队列结构如图所示
在一个队列中,链接进程控制块的方法有单向链接和双向链接。
队列管理模块的操作有入队和出队。新提交进程进入就绪队列,操作系统进程调度程 序依次调度就绪队列的每个进程占用处理器并获得运行机会。获得处理器的进程进入运行 状态,如果在分得的时间片内进程执行完毕,则撤离系统,否则重返就绪队列。如果正在 运行的进程产生等待事件,则该进程释放处理器并加入该事件等待队列。待等待事件结束 时,进程离开事件等待队列并加入就绪队列,开始等待获得处理器的新一轮的机会。进程 在各个队列中入队、出队的情况如图所示。
1.4 进程切换
进程切换即中断一个进程的执行转而执行另一个进程,被中断进程上下文需要保存到进程控制块中,然后装入新进程上下文,使其从上次断点恢复执行,或者调度一个新的进程。进程切换意味着处理器、输入/输出设备等共享资源的切换,必须将其中的现场信息保存起来,以便进程重新获得调度时恢复现场信息。
1.4.1 允许发生进程上下文切换的4种情况(即进程调度时机)
(1)当进程进入等待态时:一个进程等待时处理器会空闲下来,一个就绪进程应该获得处理器。进程等待事件激活操作系统,并实施进程切换。
(2)当进程完成其系统调用返回用户态,但不是最有资格获得CPU时:如图下所示,当进程Pi完成其系统调用即将返回用户态时,操作系统调度程序从众多进程中挑选最 有资格获得CPU的进程,发现P2而不是Pi最有资格获得CPU,则P2获得CPU,进程切换发生。
(3) 当内核完成中断处理,进程返回用户态但不是最有资格获得CPU时:这种情况类 似于第二种情况,不同之处在于第二种情况中的系统调用是自愿性中断,由当前进程主动 引发,其发生时机可以预知。这里说的中断是强迫性中断,非当前进程主动引发,其发生 时机不可预知。其处理过程类似于第二种情况。
(4) 当进程执行结束时:当进程执行结束时,不再需要处理器,处理器理应分配给其 他进程使用,需要实施进程切换。通常,进程调用“结束进程,返回操作系统”系统调用 显式请求操作系统做结束处理,包括进程切换。
上述4种情况实际上可归纳为一种情况:中断发生时才有可能发生进程上下文切换。 上述4种情况是对中断事件的细分。只有中断发生时,操作系统才能接管处理器,才有机会把处理器由一个进程转交给另一个进程,进程切换才会发生。
1.4.2 Linux 进程调度时机
(1) 进程状态转换的时刻,如进程终止(结束)、进程睡眠(阻塞等待),进程调用sleep() 或exit()等函数进行状态切换,这些函数会主动调用调度程序实施进程切换。
(2) 当前进程的时间片用完时,时间片由时钟中断更新,时钟中断处理程序隶属于操作系统内核程序,该情况与第4种情况相同。
(3) 设备驱动程序执行时,设备驱动程序执行重复而耗时的任务时,会根据调度标志决定是否调用调度程序。
(4) 进程从中断、异常或系统调用将要返回用户态时,操作系统将调度和进程切换的时机安排在处理中断事件的时候,因为中断是激活操作系统的唯一方法。
1.4.3 进程切换的步骤
(1) 保存被中断进程的处理器现场信息。
(2) 修改被中断进程的进程控制块的有关信息,如进程状态等。
(3) 把被中断进程的进程控制块加入有关队列。
(4) 选择下一个占用处理器运行的进程。
(5) 修改被选中进程的进程控制块的有关信息。
(6) 根据被选中进程设置操作系统用到的地址转换和存储保护信息。
(7) 根据被选中进程的信息恢复处理器现场。
进程切换的步骤如下图:
1.5 模式切换
CPU模式切换即处理器管态(核心态)与目态(用户态)之间的切换。中断发生时, 处理器由执行用户进程代码的用户态切换为执行操作系统内核程序(中断处理程序)的核心态,这是由用户态到核心态的模式切换。内核中断处理程序执行结束后,通过执行程序状态字加载指令可以使处理器由核心态切换为用户态,这是核心态到用户态的模式切换。 被中断的进程可以是正在用户态下执行的,也可以是正在核心态下执行的(这属于中断嵌套或多重中断),内核都要保留足够信息以便以后恢复被中断了的进程。
(1)保存被中断进程的处理器现场过程
(2)处理器由用户状态切换到内核状态,准备执行中断处理程序。
(3)根据中断级别设置中断屏蔽位。
(4)根据系统调用号或中断号,从系统调用表或中断入口地址表中找到系统服务程序或中断处理程序的地址。
进程切换包含两次模式切换,一次是处理器由一个进程的用户态切换到核心态,另一 次是处理器由核心态切换到另一个进程的用户态。
如果两次模式切换仅仅发生在一个用户进程与操作系统内核程序之间,则进程切换并未发生。进程切换与模式切换的区别如下图所示。
1.6 进程控制与管理
进程控制是处理器管理的主要工作,包括进程创建、进程阻塞、进程唤醒、进程挂起、进程激活、进程终止和进程撤销等。这些控制和管理功能是由操作系统中的原语来实现的。
原语(Primitive):在管态下执行、完成系统特定功能的不可中断的过程,具有原子操作性。
根据定义,原语的执行是顺序的而不可能是并发的。
原语的实现方法:原语可以采用屏蔽中断的系统调用来实现,以保证原语操作不被打断。也就是说,原语和系统调用都使用访管指令实现,具有相同的调用形式。但普通系统调用可以被中断,原语不可中断。
1.6.1 进程的创建
1) 进程创建的事件来源
- 提交一个批处理作业。
- 交互式作业登录。
- 操作系统创建一个服务进程。
- 存在的进程创建(孵化)新的进程。
生成其他进程的进程称为父进程(Parent Process),被生成的进程称为子进程(Child Process),即一个父进程可以创建子进程,从而形成树形结构。
2) 进程的创建过程
- 在主进程表中增加一项,并从PCB池中取一个空白PCB,为新进程分配唯一的进程标识符。
- 为新进程的进程映像分配地址空间,装入程序和数据。
- 为新进程分配内存空间外的其他资源。
- 初始化进程控制块,如进程标识符、处理器初始状态、进程优先级等。
- 把进程状态置为就绪态并加入就绪进程队列。
- 通知操作系统的某些模块,如记账程序、性能监控程序。
1.6.2 进程的阻塞与唤醒
进程阻塞是指一个进程让出处理器,去等待一个事件。通常,进程调用阻塞原语阻塞自己,所以,阻塞是自主行为,是一个同步事件。当一个等待事件结束时会产生一个中断, 从而激活操作系统,将被阻塞的进程唤醒。进程的阻塞和唤醒是由进程切换来完成的。
1) 进程阻塞的步骤
- 停止进程执行,保存现场信息到PCB中。
- 修改PCB的有关内容,如进程状态由运行改为等待,并把修改状态后的PCB加入相应等待队列。
- 转入进程调度程序,调度其他进程并运行。
2) 进程唤醒的步骤
- 从相应等待队列中移出进程。
- 修改PCB的有关信息,如将进程状态改为就绪并把修改PCB后的进程加入就绪队列。
- 若被唤醒的进程优先级高于当前运行的进程,则重新设置调度标志。
1.6.3 进程的撤销
1) 进程撤销的主要原因
- 进程正常运行结束。
- 进程执行了非法指令,或在常态下执行了特权指令。
- 进程运行时间或等待时间超越了最大限定值。
- 进程申请的内存超过最大限定值。
- 越界错误、算术错误、严重的输入/输出错误。
- 操作员或操作系统干预。
- 父进程撤销其子进程、父进程撤销、操作系统终止。
2) 进程撤销步骤
- 根据撤销进程标识号,从相应队列找到它的PCBo
- 将该进程拥有的资源归还给父进程或操作系统。
- 若该进程拥有子进程,则先撤销它的所有子孙进程,以防它们脱离控制。
- 撤销进程出队,将它的PCB归还给PCB池。
1.6.4 进程的挂起和激活
挂起原语执行过程:检査要被挂起进程的状态,若处于活动就绪态,则修改为挂起就绪 态;若处于阻塞态,则修改为挂起阻塞态。被挂起PCB的非常驻部分要交换到磁盘对换区中。
激活原语主要处理过程:把PCB非常驻部分调入内存,修改它的状态,将挂起等待态 改为等待态,将挂起就绪态改为就绪态,加入相应队列。挂起原语既可由进程自己也可由 其他进程调用,但激活原语只能由其他进程调用。
2 进程通信
进程通信就是指进程之间的信息交换。进程通信就要说明交换数据用到的共享空间如何创建,如何访问,在进程空间相对封闭的情况下如何访问共享数据就是进程通信终点研究的问题。
进程是分配系统资源的单位(包括内存地址空间),因此各进程拥有的内存地址空间相互独立。
为了保证安全,一个进程不能直接访问另一个进程的地址空间。
但是进程之间的信息交换又是必须实现的。为了保证进程间的安全通信,操作系统提供了方法。
2.1 管道通信机制
“管道” 是指用于连接读写进程的一个共享文件,又名pipe文件。其实就是在内存中开辟一个大小固定的缓冲区
1. 管道只能采用半双工通信,某一时间段内只能实现单向的传输。如果要实现双向同时通信,则需要设置两个管道,如下:
2. 各进程要互斥的访问管道
3. 数据以字节流的形式写入管道,当管道写满时,写进程的write()系统调用将被阻塞,等待读进程将数据取走。当读进程将数据全部取走后,管道变空,此时读进程的read()系统调用将被阻塞。
4. 如果没写满,就不允许读。如果没读空,就不允许写。
5. 数据一旦被读出,就从管道中被抛弃,这就意味着读进程最多只能有一个,否则可能会有读错数据的情况。
2.2 共享内存通信机制
共享内存是允许两个或多个进程共同访问的物理内存区域,是实现进程通信的一种手段。共享内存会映射到各个进程独立的虚地址空间中。每个进程都有唯一的虚拟地址空间, 各个进程的虚拟地址空间是相互隔离、不能互相访问的,但是共享内存却是通信进程的公共地址空间。
共享内存区应映射到进程中未使用的虚地址区,以免与进程映像发生冲突。共享内存的页面在每个共享进程的页表中都有页表项引用,但无需在所有进程的虚地址段都有相同的地址。共享内存区属于临界资源,读写共享内存区的代码属于临界区。
两个进程对共享空间的访问必须是互斥的(互斥访问通过操作系统提供的工具实现)。
操作系统只负责提供共享空间和同步互斥工具(如P、V操作)
2.2.1 基于数据结构的共享
比如共享空间里只能放一个长度为10的数组。这种共享方式速度慢、限制多,是一种低级通信方式
2.2.2 基于存储区的共享
在内存中画出一块共享存储区,数据的形式、存放位置都由进程控制,而不是操作系统。相比之下,这种昂共享方式速度更快,是一种高级通信方式。
2.2.3 Linux 共享内存的实现
Linux内核为每个共享内存段维护一个数据结构shmid ds,其中描述了段的大小、操作权限、与该段有关系的进程标识等。共享内存的主要操作如下。
- 创建共享内存:使用共享内存通信的第一个进程创建共享内存,其他进程则通过创建操作获得共享内存标识符,并据此执行共享内存的读写操作。
- 共享内存绑定(映射共享内存区到调用进程地址空间):需要通信的进程将先前创建的共享内存映射到自己的虚拟地址空间,使共享内存成为进程地址空间的一部分,随后可以像访问本地空间一样访问共享内存。
- 共享内存解除绑定(断开共享内存连接):不再需要共享内存的进程可以解除共享内存到该进程虚地址空间的映射。
- 撤销共享内存:当所有进程不再需要共享内存时可删除共享内存。
2.3 消息传递通信机制
进程间的通信以格式化的消息(Message)为单位。进程通过操作系统提供的 “发送消息/接收消息”两个原语进行数据交换。
消息传递分为直接通信方式和间接通信方式
直接通信方式:
消息直接挂到接收进程的消息缓冲队列上。如:进程1先创建需要发送的消息,然后使用发送原语将消息挂到进程2的消息缓冲队列,进程2使用接收原语开始读取消息。
间接通信方式:
消息要先放送到中间实体(信箱)中,因此也称 “信箱通信方式” 。Eg:计网中的电子邮件系统
消息传递通信机制由信箱、发送原语(send)和接收原语(receive)组成。信箱是存 放信件的存储区域,每个信箱可分成信箱头和信箱体两部分。信箱头指出信箱容量、信件 格式、信件位置指针等;信箱体用来存放信件,可分成若干个区,每个区容纳一个信件。
原语send (A,信件):若信箱未满,则把一封信件(消息)发送到信箱A,同时唤醒信件等待者进程,否则发送者阻塞。
原语receive (A,信件):若信箱不空,则从信箱A中接收一封信件(消息),同时唤醒等待发送者进程;否则接收者阻塞。
发送原语和接收原语封装了同步细节,其中包含阻塞唤醒机制,程序员使用它们进行程序设计时不用再考虑同步操作。
信箱的所有者可以是操作系统,也可以是进程。如果信箱为进程所有,则当拥有信箱的进程执行结束时,信箱也随之消失。这时,拥有信箱的进程必须将这一情况通知此信箱的用户。操作系统拥有的信箱在显示删除之前一直存在,,可以供通信进程共享。
2.4 套接字通信机制
套接字(Socket)通信允许互连的、位于不同计算机上的进程实现通信功能。套接字用于标识和定位特定计算机上特定进程的地址,以便数据准确传输给目标进程。套接字包含3个参数:通信的目的IP地址、使用的传输层协议(TCP或UDP)和使用的端口号。 1P地址用于标识目的计算机,端口号用于标识目的计算机上的特定进程。
Socket是连接应用程序和网络驱动程序的桥梁,Socket在应用程序中创建,通过绑定与网络驱动建立关系。应用程序向Socket发送的数据被提交给网络驱动程序并在网络中发送出去。计算机从网络上收到与该Socket绑定IP地址和端口号相关的数据后,由网络驱动程序交给Socket,应用程序便可从该Socket中提取接收到的数据。
通过套接字通信的一对进程分为客户端进程和服务器端进程。套接字之间的连接过程分为3个步骤:服务器监听、客户端请求、连接确认。
服务器监听是指服务端套接字并不定位具体的客户端套接字,而处于等待连接的状态, 实时监控网络状态。
客户端请求是由客户端的套接字提出连接请求,要连接的目标是服务器端套接字。为此,客户端的套接字必须先描述它要连接的服务器端套接字,指出服务器套接字的地址和端口号,再向服务器端套接字提出连接请求。
连接确认是当服务器端套接字监听到客户端套接字的连接请求时,它响应客户端套接 字的请求,建立一个新的线程,把服务器端套接字的信息发送给客户端,客户端确认后连接即可建立。而服务器端继续处于监听状态,继续接收其他客户端的连接请求。
套接字通信有两种基本模式:同步和异步。同步模式的特点是客户机和服务器在接收到对方响应前会处于阻塞状态,一直等到收到对方请求后才继续执行后面的语句。异步模式的特点是客户机或服务器进程在调用发送或接收的方法后直接返回,不会处于阻塞方式, 因而可继续执行后面的程序。
2.5 信号通信机制
信号
信号是一种软中断信号,是对硬件中断机制的软件模拟。利用信号可以实现进程间的通信,一个进程可以向另一进程发送某一信号,并通知该进程某个异常事件的发生。接收信号的进程被中断,对该信号代表的事件进行处理。
信号的产生源
用户、内核和进程都能生成信号请求。
- 用户。用户通过按Ctrl+C组合键,或终端驱动程序分配给信号控制字符的其他任何键来请求内核产生信号。
- 内核。当进程执行出错时,内核检测到事件并给进程发送信号,如非法段存取、 浮点数溢出、非法操作码。
- 进程。进程可通过系统调用kill给另一个进程发送信号。
信号应用实例
用户发送信号杀死进程的过程如下;
- 用户按中断组合键Ctrl+C。
- 终端驱动程序收到输入字符,并调用信号系统。
- 信号系统发送SIGINT信号给Shell, Shell再把它发送给进程。
- 进程收到SIGINT信号。
- 进程撤销。
信号响应情况
进程收到信号后可能釆用如下响应方式。
- 执行默认操作。
- 执行预置的信号处理程序。
- 忽略此信号。
Linux信号机制的实现
信号有一个产生、传送、捕获和释放的过程。
1)数据结构
每个进程结构中signal域专门用于保存接收到的信号。进程接收到信号时,对应位置 1,相当于“中断请求寄存器”某位置位。task_struct的blocked是信号屏蔽标记,相当于 “中断屏蔽寄存器”,进程需要忽略某信号时可将对应位置1= task_struct的sigaction[]数组 用于存放信号处理程序入口。信号编号对应数组下标。
2)信号函数
sigaction(signo,act,oldact):为指定信号预置处理程序,sign。指出接收信号的类型,act 是信号处理函数的地址,oldact存放最近一次为信号signo定义的函数地址。
kill(pid,sig):向进程PID (进程标识号)发送信号sig。
3)信号的检测和响应过程
在中断或异常处理程序末尾,进程从核心态返回用户态之前,或时钟中断结束之前, 系统调用内核函数do_signal()检査该进程是否收到信号,若是,则执行handle_signal(),使 进程返回用户态并转入信号处理程序执行。信号处理结束后,执行系统调用sigreturn()陷 入内核,内核做好善后工作后返回用户态,回到应用程序断点执行。
例如,在图3-15中,进程P1调用kill(P2,5)向进程P2发送一个信号5,该信号被系统置于P2进程控制块的信号域。P1继续执行后续指令。由于P2并未获得处理器, 因此P2无法立即对信号5做出响应。待中断或系统调用再次发生时,系统接管处理器, 执行中断或系统调用处理程序,在末尾执行调度程序。当P2获得调度时,先返回用户态执行信号处理程序,再返回核心态执行信号终止处理,然后返回P2主程序断点处开始执行。