1、任务的管理与调度一、进程和线程这一节我们要解决三个问题:什么是进程?什么是线程?进程和线程之间的区别?1、进程的定义提问:什么是进程,什么是程序,进程和程序之间的区别?程序是存放在磁盘上的一系列代码和数据的可执行映像,是一个静止的实体。也就是说存储到文件系统中的一个文件。进程:正在执行的一个文件,是动态的。程序是静止的,程序是动态的。2、进程四要素我们学习操作系统的时候,有的同学学过操作系统,对进程有一定的了解,有没有学过操作系统?操作系统原理当中描述的进程是什么?进程是资源分配的最小单位,线程是调度的基本单位,这是很抽象的,操作系统中很多概念都是很抽象的。那么怎么去衡量,到底什么是进程,实际
2、上在 linux 操作系统当中,我得到进程的四要素,我们从内核情景分析摘录出来,很经典,讲怎么衡量一个进程。第一、 作为一个进程,要有一段程序供其执行。 这段程序不一定是某个进程所专有,可以与其他进程公用。这是最基本的条件。第二、 有进程专用的内核空间堆栈。 程序在执行的过程当中都需要有堆栈的。在参数传递的时候要用到堆栈,局部变量分配的时候要用到堆栈,用户空间用到用户空间堆栈,进程在内核空间运行的时候要有内核空间堆栈,因此要有内核空间堆栈。第三、 在内核中有一个 task_struct 数据结构 , 即通常所说的 “进程控制块 ”。 有没有同学听说过 PCB?进程控制块有什么作用?保存进程的状
3、态以及调度信息,这是操作系统原理所提出来的 PCB 这样的概念,在 Linux 系统当中, PCB 这个数据结构实际上对应了task_struct 数据结构,也就是说 task_struct 数据结构就充当了 PCB,保存了进程状态信息,要想成为一个进程,还有 PCB。有了这个数据结构,进程才能够成为内核调度的基本单位接收内核的调度。第四、 有独立的用户空间, 非常关键。如果有独立的用户空间,则肯定是进程;如果不满足,还要看,如果没有独立的用户空间,但是还有用户空间,那分为两种:一种是完全没有用户空间,一种是和别人共享的用户空间。如果完全没有用户控件,就是内核线程;如果有共享的用户空间,就是用
4、户线程。在Linux 系统当中存在:进程,用户线程,内核线程这三个概念,你以后经常会遇到这三个概念,怎么去区分它们?仅仅这两个条件就可以区分。你以后去面试,面试操作系统相关的开发,尤其是嵌入式开发,经常会问到进程和线程的区别,怎么回答这个问题?首先,说一下操作系统原理中怎么说进程和线程,进程是资源分配的最小单位,线程是调度的最小单位;第二,在 Linux 系统当中怎么去划分的,把这个图给他画出来,非常清晰。如果说你把这个图画出来,那对这个内核真正的理解,不是说仅仅是看过几页书而已。要背下来的。3、线程的引入很多同学没有学习过操作系统,对线程及相应的概念并不是很清楚。线程技术在上个世纪 60 年
5、代被提出来的。提问:为什么有了进程,还要引入线程?使用线程究竟有哪些好处?第一,线程非常“节俭” 。创建一个子进程,代码段和父进程是共享的,数据段是共享的么?要拷贝,需要重新建立的,堆栈是共享的么?也不是,也是需要重新建立的。如果我们使用线程呢,所有这些都是共享的,这样,就少了很多额外的工作,这就是节俭的一个表现之一。第二,有多个线程,同属于同一个进程的,使用同样的数据段,还需要管道,消息队列实现两个进程间的通信么?不需要,只需要简单定义一个变量,比如说定义一个全局变量,属于进程的数据段,可以让属于这个进程的所有线程都可以看到这个变量,这可以使线程的通信变得简单。优点:多线程程序作为一种多任务
6、、并发执行与多进程具备同样的优点。引入线程的概念后,可以把进程和线程的使用分为以下几种模型:单进程 /单线程模型(如 MS-DOS) :整个系统只有一个进程、一个线程。单进程 /多线程模型: 在单进程/多线程模型中,整个系统有一个进程、多个线程 多进程 /单线程模型(如传统的 UNIX) :在多进程/单线程模型中,整个系统有多个进程,每个进程只有一个线程 多进程 /多线程模型(如 Windows NT、 Solaris、 Mach 等): 在多进程/ 多线程模型中,系统有多个进程,每个进程又可包含多个线程大多数实时内核都把整个应用当做一个没有定义的进程来对待,应用则被划分为多个任务的形式来进行
7、处理,即单进程/多线程模型,简称任务模型。二、任务1、 任务的定义:任务是一个具有独立功能的无限循环的程序段的一次运行活动,是实时内核调度的单位。2、 任务的特性:动态性:任务状态是不断变化的。任务状态一般分为就绪态、运行态和等待态。在多任务系统中,任务的状态将随着系统的需要不断进行变化。并行性:系统中同时存在多个任务,这些任务在宏观上是同时运行的。异步独立性:每个任务各自按照相互独立的不可预知的速度运行,走走停停3、 任务的内容 任务包含:代码:一段可执行的程序。数据:程序所需要的相关数据 (变量、工作空间、缓冲区 )堆栈程序执行的上下文环境 任务与程序的区别任务能真实地描述工作内容的并发性
8、,而程序不能;程序是任务的组成部分;除程序外,任务还包括数据、堆栈及其上下文环境等内容;程序是静态的,任务是动态的;任务有生命周期,有诞生、有消亡,是短暂的;而程序是相对长久的;一个程序可对应多个任务,反之亦然;任务具有创建其他任务的功能,而程序没有。4、 任务的分类 按照到达情况的可预测性,任务可以划分为:周期任务( periodic task) ,非周期任务 按照重要程度,可分为:关键任务( critical task) ,非关键任务( noncritical task)5、 任务的参数优先级( priority):表示任务对应工作内容在处理上的优先程度。任务的优先级分为静态优先级和动态优
9、先级。周期( period):周期任务所具有的参数,表示任务周期性执行的间隔时间。计算时间( computation time):任务在特定硬件环境下被完整执行所需要的时间,也被称为是任务的执行时间( execution time) 。就绪时间( ready time):任务具备了在处理器上被执行所需要条件时的时间截止时间( deadline):任务需要在该时间到来之前被执行完成。截止时间可以分为强截止时间( hard deadline)和弱截止时间( soft deadline)两种情况:具有强截止时间的任务即为关键任务,如果截止时间不能得到满足,就会出现严重的后果。拥有关键任务的实时系统又
10、被称为强实时( hard real-time)系统,否则称为弱实时( soft real-time)系统。三、任务的管理1、任务的状态与变迁等待:任务在等待某个事件的发生就绪:任务等待获得处理器资源执行:任务获得处理器资源,所包含的代码内容正在被执行。在一定条件下,任务会在不同的状态之间进行转换,称为任务状态的变迁 。这里给出图。2、任务控制块任务管理是通过对 任务控制块( Task Control Block, TCB) 的操作来实现的。任务控制块是包含任务相关信息的 task_struct 数据结构,包含了任务执行过程中所需要的所有信息。任务控制块大都包括以下信息: 任务的名字、任务执行的
11、起始地址、任务的优先级、任务的状态、任务的上下文(堆栈指针、 PC 和寄存器、状态寄存器等 )、任务的队列指针 等内容。 3、任务切换任务切换( context switching):保存当前任务的上下文,并恢复需要执行的任务的上下文的过程。当发生任务切换时:当前正在运行的任务的上下文就需要通过该任务的任务控制块保存起来;把需要投入运行的任务的上下文从对应的任务控制块中恢复出来。这里给出图任务切换将导致任务状态发生变化:当前正在运行的任务将由运行状态变为就绪或是等待状态;需要投入运行的任务则由就绪状态变为运行状态。任务切换的步骤:1) 保存任务上下文环境2) 更新当前运行任务的控制块内容,将其
12、状态改为就绪或等待状态3) 将任务控制块移到相应队列(就绪队列或等待队列)4) 选择另一个任务进行执行 (调度 )5) 改变需投入运行任务的控制块内容,将其状态变为运行状态6) 恢复需投入运行任务的上下文环境任务切换的时机:1)中断、自陷2)运行任务因缺乏资源而被阻塞3)采用时间片轮转调度时4)一个高优先级任务处于就绪时,如果采用基于优先级的抢占式调度算法,将导致任务切换。任务的上下文切换时间:这里给出图,任务上下文切换的时间与调度(即选择下一个运行任务)的过程有关。强实时内核要求调度过程所花费的时间是确定的,即不能随系统中就绪任务的数目而变化。与具体实现调度算法时所采用的数据结构有关。4、任
13、务队列任务队列通过 任务控制块 实现对系统中所有任务的管理。如果想从就绪队列的任务中获取最高优先级的就绪任务,可用以下方式处理:方式一: 任务就绪时,把就绪任务的任务控制块放在就绪队列的末尾。调度程序需要从就绪队列的头部到尾部进行一次遍历,才能获得就绪队列中具有最高优先级的任务;方式二: 就绪队列按照优先级从高到低的顺序排列 。新的就绪任务到达时,需要插入到就绪队列的合适位置,确保就绪队列保持优先级从高到低排列的顺序性。以上两种方式,所花费的时间与任务数量密切相关,具有不确定性,为了提高实时内核的确定性,可采用一种被称为优先级位图的就绪任务处理算法:1)设置两个变量priorityReadyG
14、roup: 优先级就绪组priorityReadyTable: 优先级就绪表char priorityReadyGroup;char priorityReadyTable 8;2)任务优先级与 priorityReadyGrou 和 priorityReadyTable 之间的关系。5、任务的管理机制任务管理用来实现对任务状态的直接控制和访问。 内核的任务管理是通过系统调用来体现,主要包括任务创建、任务删除、任务挂起、任务唤醒、设置任务属性等内容 。创建任务任务创建过程即为任务分配任务控制块的过程。任务成功创建后,通常会为用户返回一个标识该任务的 ID,以实现对任务的引用管理。任务创建时通常需
15、要使用如下信息:任务的名字 :用户在创建任务时指定,也可以由系统自动分配。任务的初始优先级任务栈: 任务栈大小的设定是一个反复修正的过程。任务属性: 任务是否被抢占,是否采用时间片轮转调度,是否响应异步信号,任务开放的中断级别等任务对应的函数入口地址 :表示创建任务起始执行的入口任务删除时的回调函数: 任务可以在该任务被删除时,对其所用资源进行回收或删除。任务创建通常需要完成以下工作:获得任务控制块 TCB根据实时内核用户提供的信息初始化 TCB为任务分配一个可以唯一标识任务的 ID使任务处于就绪状态,把任务放置到就绪队列进行任务调度处理任务删除实时内核根据任务创建时获得的 ID 删除指定的任
16、务。在删除一个任务时,需要释放该任务所拥有的资源。释放任务所拥有的资源通常由 内核和任务 共同完成。 内核通常只释放那些由内核为任务分配的资源,如任务名字和 TCB 等内容所占用的空间 。 由任务自己分配的资源则通常由任务自身进行释放如任务的堆栈空间,以及其他一些任务申请的资源,信号量、 timer、文件系统资源、 I/O 设备和使用 malloc 等函数动态获得的内存空间等。任务删除进行一下工作:根据指定的 ID,获得对应任务的 TCB。把任务的 TCB 从队列中取出来,挂入空闲 TCB 队列。释放任务所占用的资源。任务挂起任务挂起指的是挂起指定任务 ID,把任务挂起,直到通过唤醒任务对任务
17、进行解挂。一个任务可以把自己挂起,当任务把自己挂起后,会引起任务的调度,实时内核将选取另外一个合适的任务进行执行。任务被挂起后,该任务将处于等待状态。挂起任务通常需要进行以下工作:根据指定的 ID,获得对应任务的 TCB把任务的状态变为等待状态,并把 TCB 放置到等待队列如果任务自己挂起自己,进行任务调度任务唤醒任务唤醒指的是,根据任务 ID 解挂指定的任务。解挂任务通常需要进行以下工作:根据指定的 ID,获得对应任务的 TCB如果任务在等待其他资源,任务将仍然处于等待状态;否则,把任务的状态变为就绪状态,并把 TCB 放置到就绪队列进行任务调度任务睡眠任务睡眠指的是使当前任务睡眠一段指定的
18、时间,时间到后,任务又重新回到就绪状态。任务睡眠通常需要进行以下工作:修改任务状态,把任务状态变为等待状态把任务 TCB 放置到时间等待链进行任务调度四、任务的调度1、实时内核的关键设计问题对于通用的,大型的计算机操作系统来说,嵌入式操作系统更为精巧,但这并不是说嵌入式操作系统是由简单地剪裁通用操作系统而来的。嵌入式应用领域对于它提出了很多特别的要求,尤其对于嵌入式实时内核,在设计时通常要考虑一下要求:实时性可移植性可剪裁可配置性可靠性应用编程接口这里仅讨论实时性,实时性是实时内核最重要的特性之一,从整体上考虑,一个系统的实时性与硬件、操作系统及应用程序三方面都有关系,提高硬件能力可以在一定程
19、度上提高系统实时性,但是当硬件条件确定之后,嵌入式系统的性能主要由操作系统来决定,实时内核起到关键作用。实时性:实时内核应该保证系统尽可能快地对外部事件产生响应。确定性:系统对外部事件响应的最坏时间是可预知的。响应性:确定性关心的是系统在识别一个外部事件 (通常以中断的形式到达 )之前有多长的延迟,而响应性关心的是在识别外部事件后,系统要花多长时间来服务该事件。响应时间:确定性和响应性结合在一起构成了系统对外部事件的响应时间。对于强实时内核来讲,相应时间应该在 us 级。调度算法、可抢占内核、内核关中断时间、数据结构(优先级位图,双向链表,差分时间链)、存储管理机制、资源的有限等待时间、优先级
20、反转、中断处理等因素都是影响实时性的重要指标。2、任务的调度操作系统通过一个调度程序来实现调度功能。调度程序以函数的形式存在 ,本身并不是一个任务,而是一个函数调用,可在内核的各个部分进行调用。 调用调度程序的具体位置又被称为一个调度点 ,调度点通常处于一下位置:中断服务程序的结束位置任务因等待资源而处于等待状态任务处于就绪状态。调度本身需要一定的开销,需要花费时间计算下一个可被执行的任务。调度算法在复杂性与实时性之间要做出权衡,调度问题仍然是 NP-hard 问题。设计调度程序时,通常需要综合考虑如下因素:CPU 的使用率(CPU utilization) 、输入/输出设备的吞吐率、响应时间
21、(responsive time ) 、公平性、截止时间调度算法:是在一个特定时刻用来确定将要运行的任务的一组规则。对于大量的实时调度方法而言,存在着以下几类主要的划分方法:离线( off-line)和在线( on-line)调度抢占( preemptive)和非抢占( non-preemptive)调度静态( static)和动态( dynamic)调度最佳( optimal)和试探性( heuristic)调度根据获得调度信息的时机来划分,分为离线和在线:调度信息在系统运行之前就确定了,优点是有确定性,缺点是缺乏灵活性。在线调度则在系统运行过程中进行,具有较大的灵活性。根据任务在运行过程当
22、中是否能被其他任务所打断:抢占和非抢占。抢占式指的是正在运行的任务可能被其他任务打断;非抢占式指的是一旦任务开始运行,该任务只有在运行完成而主动放弃 CPU 资源,或因为等待其他资源被阻塞的情况下才会停止运行。实时内核大多采用抢占式,使关键任务能打断非关键任务,确保关键任务的截止时间能够得到满足。内核的可抢占性:与可抢占式调度是不同的概念,体现在任务在执行内核提供的系统调用的过程中,是否可以被中断打断的不同处理上。 可抢占式内核: 即使正在执行的是内河服务函数,也能响应中断。 不可抢占式内核:有两种情况,第一种, 内核服务函数不能被中断,系统在执行内核服务函数时处于关中断状态。第二种,能被中断
23、但是不能进行任务重新调度。系统在执行内核服务函数时可以响应中断,不会延迟中断响应时间;但是在中断退出时不进行任务重新调度。内核服务函数能被中断但是不能进行任务重新调度这里给出图根据任务优先级的确定时机,调度算法分为静态和动态:静态,所有任务的优先级在设计时就确定下来,运行过程当中不会发生变化;动态,任务的优先级在运行过程当中确定,并能发生变化。3、基于优先级的可抢占式调度基于优先级的可抢占调度方式:如果出现具有更高优先级的任务处于就绪状态时,当前任务将停止运行,把 CPU 的控制权交给具有更高优先级的任务,使更高优先级的任务得到执行。这里给出图4、时间片轮转调度当有两个或多个就绪任务具有相同的
24、优先级,且它们是就绪任务中优先级最高的任务时,任务调度程序按照这组任务就绪的先后次序调度第一个任务,让第一个任务运行一段时间,然后又调度第二个任务,让第二个任务又运行一段时间,依次类推,到该组最后一个任务也得以运行一段时间后,接下来又让第一个任务运行。任务运行的这段时间称为时间片( time slicing)这里给出图五、优先级反转理想情况下,高优先级任务就绪后,能够立即抢占低优先级任务而得到执行。但在有多个任务需要使用共享资源的情况下,可能会出现高优先级任务被低优先级任务阻塞,并等待低优先级任务执行的现象。优先级反转( priority inversion):高优先级任务需要等待低优先级任务
25、释放资源,而低优先级任务又正在等待中等优先级任务的现象。这里给出图假定 T1 和 T3 通过信号量 S 共享一个数据结构。在时刻 t1,任务 T3 获得信号量 S,开始执行临界区代码。在 T3 执行临界区代码的过程中,高优先级任务 T1 就绪,抢占任务 T3,并在随后试图使用共享数据,但该共享数据已被 T1 通过信号量 S 加锁。在这种情况下,会期望具有最高优先级的任务 T1 被阻塞的时间不超过任务 T3 执行完整个临界区的时间。但事实上,这种阻塞时间的长度是无法预知的。这主要是由于任务 T3 还可能被具有中等优先级的任务 T2 所阻塞,使得 T1 也需要等待 T2 和其他中等优先级的任务释放
26、 CPU 资源。1、优先级继承优先级继承协议的基本思想是:当一个任务阻塞了一个或多个高优先级任务时,该任务将不使用其原来的优先级,而使用被该任务所阻塞的所有任务的最高优先级作为其执行临界区的优先级。当该任务退出临界区时,又恢复到其最初的优先级。这里给出图优先级继承协议工作过程:如果任务 T 是具有最高优先级的就绪任务,则任务 T 将获得 CPU 资源,任务 T 得以运行。如果任务 T 除了 CPU,还要获得其他共享资源,才能继续运行,要看当前共享资源是否被其他任务占用:如果被其他任务占用,则任务 T 被阻塞;如果空闲,则任务 T 使用该共享资源。如果任务 T 不是最高优先级任务,任务 T 将保
27、持原有优先级不变,除非任务 T 使用共享资源的同时,有高优先级任务到来要使用共享资源,进而任务 T 阻塞了高优先级任务,此时发生优先级继承,任务 T 继承被阻塞高优先级任务的优先级。当任务 T 使用完共享资源后,将恢复原有优先级。优先级继承具有传递性。比如,假设 T1,T2,T3 为优先级顺序降低的三个任务,如果任务 T3 阻塞了任务 T2,此前任务 T2 又阻塞了任务 T1,则任务 T3 将通过任务 T2 继承任务 T1 的优先级。注意:只有在高优先级任务与低优先级任务共享临界资源,且低优先级任务已经进入临界区后,高优先级任务才可能被低优先级任务所阻塞;高优先级任务被低优先级任务阻塞的最长时
28、间为:高优先级任务中可能被所有低优先级任务阻塞的具有最长执行时间的临界区的执行时间;如果有 m 个信号量可能阻塞任务 T,则任务 T 最多被阻塞 m 次。2、优先级天花板使用优先级天花板协议的目的在于解决优先级继承协议中存在的死锁和阻塞链问题。信号量的优先级天花板为所有使用该信号量的任务的最高优先级。在优先级天花板协议中,如果任务获得信号量,则在任务执行临界区的过程中,任务的优先级将被抬升到所获得信号量的优先级天花板。在优先级天花板协议中,主要包含如下处理内容: 设置申请共享资源的任务的优先级天花板为可能申请该共享资源的所有任务中具有最高优先级任务的优先级;如果任务成功获得该共享资源,任务的优
29、先级将被抬升为优先级天花板;任务执行完毕,释放共享资源后,其优先级恢复到其最初的优先级;如果任务不能获得共享资源,任务将被阻塞这里给出图,对图的解释假设 T1,T2,T3 的优先级分别为 p1、p2、p3,并且 T1 和 T3 通过信号量 S 共享一个临界资源。根据优先级天花板协议,信号量 S 的优先级天花板为 p1。假定在时刻 t1,T3 获得信号量 S,按照优先级天花板协议,T3 的优先级将被抬升为信号量 S 的优先级天花板 p1,直到 T3 退出临界区。这样,T3 在执行临界区的过程中,T1 和T2 都不能抢占 T3,确保 T3 能尽快完成临界区的执行,并释放信号量 S。当 T3 退出临
30、界区后,T3 的优先级又回落为 p3。如果在 T3 执行临界区的过程中,任务T1 或 T2 已经就绪,则此时 T1 或 T2 将抢占 T3 的执行。3、优先级继承和优先级天花板协议的比较优先级继承协议和优先级天花板协议都能解决优先级反转问题,但在处理效率和对程序运行流程的影响程度上有所不同。执行效率优先级继承协议可能多次改变占有某临界资源的任务的优先级,而优先级天花板协议只需改变一次。从这个角度看,优先级天花板协议的效率高,因为若干次改变占有资源的任务的优先级会引入更多的额外开销,导致任务执行临界区的时间增加。执行流程优先级天花板协议的特点是一旦任务获得某临界资源,其优先级就被抬升到可能的最高程度,不管此后在它使用该资源的时间内是否真的有高优先级任务申请该资源,这样就有可能影响某些中间优先级任务的完成时间。但在优先级继承协议中,只有当高优先级任务申请已被低优先级任务占有的临界资源这一事实发生时,才抬升低优先级任务的优先级,因此优先级继承协议对任务执行流程的影响相对要较小。