Linux进程调度策略与研究Word格式文档下载.docx
- 文档编号:19775560
- 上传时间:2023-01-10
- 格式:DOCX
- 页数:19
- 大小:273.86KB
Linux进程调度策略与研究Word格式文档下载.docx
《Linux进程调度策略与研究Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《Linux进程调度策略与研究Word格式文档下载.docx(19页珍藏版)》请在冰豆网上搜索。
1.1课题研究背景及意义
随着技术的发展,计算机科学也在不断前进,而CPU作为计算机最昂贵的资源,发展的速度很快,体积骤减和频率剧增,但是单核处理器的架构越来越繁琐、复杂,不仅给设计带来了相当大的困难,而且还使得CPU得不到最大化的使用,再加上体积变小,功耗却没有降下来,散热也成了问题,很难使得处理器的设计与实现以及频率的提升能够顺利的进行下去[1]。
一来处理器的频率提升出现了天花板效应,很难再提升。
微处理器性能的提升很大程度是依赖于工业设计的发展,制造CPU所需的电阻电容等的体积在急剧的变小,这就使得在同一个处理器内集合很多的设计,然而它们之间的连接线路却成了它们的瓶颈,导致了频率不易于提升。
所以一些大公司在研发了3.8G赫兹这样的高频率CPU后,就不在这个方向上继续投入开发力度。
二来处理器的功耗也不断飙升。
CPU的制造工艺一直在不停的改善,晶体管的体积革命性的变小,这就便于集成,再加上CPU的频率提升了,单位面积内功耗比以前更多了,发热量也更大了,这不利于CPU的稳定工作。
所以转向多核处理器是发展的需要。
片上多核处理器CMP(ChipMulti-Processor)[2]就是在那个关键的时刻被提出的,它在一个处理器芯片上封装了多个核心,各个核心能并行地运行各自的任务,非常接近传统的对称多处理器系统(SymmetricMultiprocessors,SMP)[3]。
多核处理器中的每个核都可视为一个独立的单元,并且它的实现起来相对容易,对以后的研发也便于扩展,并且发热也没有比单核处理器少。
多核处理器在频率不变的情况下通过并行运行多个任务来实现性能的提升,也使得CPU得到了充分的利用。
多核处理器的发展,对操作系统的相关研究既提供了机遇,又提出了更高的要求。
第一,与单核相比,如何在多核环境下各个CPU间合理地均匀地调派任务才能充分发挥计算机系统结构优势,提高系统整体的性能?
其次,如何降低这种底层核心架构的改变对上层计算机系统用户的影响,保持对外接口的稳定?
对计算机系统各种使用者而言,最好是实现计算机体系结构尽可能平滑地过渡。
一方面,用户界面最好不要发生改变,另一方面,最好能保持对以前开发的软件及程序兼容,节省重复开发的成本。
这些都是在设计和开发多核体系结构下系统软件所必须面对和解决的问题。
但归结为一点,如何高效率的调派任务以便将多核体系结构的性能发挥到极限是多核平台下研发操作系统的核心命题,这是人们对多核系统软件的最大要求。
针对新的多核平台的相关的操作系统方面的研究从起步至今还比较短暂,许多方面需要完善,特别是缺乏比较完备的进程调度策略。
因此,多核平台下操作系统的进程调度问题是当今比较前沿的一个研究热点。
在本文,我们基于Linux内核研究其在多核环境下的进程调度问题。
尽管相对于其他操作系统的漫长历史来说,Linux的历史非常短暂,但Linux在从其问世到现在短短的时间之内得到了非常迅猛的发展,已成为主流的多用户多任务的操作系统之一,而且具有良好的特性,特别是其开放性、可靠的安全性及良好的可移植性使其获得了广泛的应用[5]。
Linux与Unix完全兼容并且开放源代码,也使其成为操作系统的研究人员的不二选择。
从二十世纪六十年代进程的概念由J.H.Sallexer等人提出以后,人们对进程和任务的组织与调度问题的研究一直是一个热点。
Linux操作系统之所以受到好评,是因为它的高效率很大程度上要归功于其内核进程调度系统的超凡设计。
同时,我们又可以借助其开源特性,将最新的操作系统方面相关的思想、研究和技术融合于Linux操作系统中,通过修改其内核来个性化定制并进一步完善、优化它。
近年来,基于Linux的进程调度研究比较活跃。
文献[8]分析了Linux内核的任务调度流程,指出Linux内核的调度策略综合了时间片轮转和可剥夺式优先级两种调度策略。
高珍等人[9]分析了Linux内核对SMP的实现方式。
安智平、张德运等[10]设计了进程调度的Master/Slave模型,并考虑了该模型在Linux环境下的实现。
1.3本文研究内容及方法
3.在ubuntu系统下实现上面的进程调度程序
第2章进程管理及其调度
2.1基本概念
2.1.1进程
进程(Process)的概念是在上世纪六十年代被提出的,最初是由MIT的Multics和IBM的TSS/360系统引用。
至今人们从各方面对进程做出过许多种定义。
主要考虑了:
(1)进程的并发执行性(S.E.Madnick,J.T.Donovan);
(2)进程作为独立的被系统调度的单位(E.Cohen,D.Jofferson);
(3)进程的抽象性以及任务调度时作为系统分配和释放各种资源的单位(P.Denning);
(4)进程与程序的区别。
程序是行为规则的集合,程序的运行即体现为进程(E.W.Dijkstra);
(5)进程是具体操作的序列(BrinchHansen)。
以上关于进程的描述,尽管角度不同,但它们在实质上是相通的,即进程的动态执行性。
因此,进程可被定义为可并发执行的程序对相关数据的一次具体的执行过程,是系统调配资源的单位。
进程的基本特征有:
并发性;
动态性;
独立性;
异步性以及结构特征。
进程在并发执行过程中总是相互制约的。
进程在其活动期间至少具备三种基本状态,它们是:
执行状态、就绪状态和等待状态。
进程的状态反映进程执行过程的变化。
这些状态随着进程的执行和外界条件的变化而转换。
进程在执行期间,可以在三个基本状态之间进行多次转换。
2.1.2进程在Linux内核中的实现
进程由操作系统创建。
具体到Linux环境下,系统可以通过调用fork()函数复制一个现有进程来生成新的进程[13]。
调用fork()函数的进程是父进程,复制出的进程是子进程。
fork()函数返回后,父进程从返回点继续执行,子进程从返回点独立运行。
fork()函数在父进程和子进程的返回值不同。
另外,fork()函数又是通过clone()函数实现的。
为了使子进程执行新的任务,可以通过exec()相关函数装载新的任务。
最后通过exit()函数退出。
exit()函数的作用是结束进程及释放分配给该进程的各种资源。
父进程和子进程可以通过相关函数实现同步。
如wait4()函数可用来查询子进程是否结束。
进程结束后即成为僵尸进程,由其父进程通过wait()或waitpid()函数进行进一步处理。
可以发现,Linux进程之间存在一个明显的继承关系。
所有的进程都是PID(进程标识值)为1的init进程的后代。
内核在系统启动的最后阶段启动init进程。
值得说明的是,Linux内核通常把进程称做任务(task)。
并使用类型为task_struct、称为进程描述符(processdescriptor)的结构来描述进程[14]。
该结构定义在<
linux/sched.h>
文件中。
进程描述符包含一个具体进程的所有信息,主要有:
1、进程的状态(ProcessState)
进程从产生到结束期间会经历几种状态的改变。
进程的状态是操作系统决定如何对其调度的重要属性。
Linux环境下进程状态如表2-1所示。
进程的状态在不同条件下可以相互转换,如图2-1所示:
2、和进程调度相关的信息
这些信息一般反映了系统对进程调度的组织方式,比较重要的如进程的优先级,进程是普通进程还是实时进程。
相关字段参见表2-2说明。
当need_resched字段被设置时,在“下一次的调度机会”就调用调度程序schedule()。
counter代表进程剩余的时间片,是进程调度的主要依据,也可以说是进程的动态优先级,因为这个值在不断地减少;
nice值代表静态优先级,反映进程拥有的时间片,影响counter值,可以用nice()函数改变nice值;
policy代表了操作系统该进程的调度方式,如该进程是按照实时进程的方式被调度还是按照普通进程的方式被调度;
rt_priority是操作系统对实时进程进行调度时的重要依据。
表2-3说明了进程的调度策略类型。
只有root用户能通过sched_setscheduler()系统调用来改变调度策略。
3、标识符(Identifiers)信息
最基本的如进程标识符(ProcessIdentifier),其他如用户标识符(UserIdentifier)、组标识符(GroupIdentifier)。
4、和进程通信相关的一些信息(IPC)
主要为了使进程在执行期间能够与其他进程进行信息交换。
5、进程链接信息(Links)
相关信息如表2-4说明。
进程通过这些指针可以组织成一颗进程树。
6、和时间以及定时器相关的信息(TimesandTimers)
进程的生存期(lifetime)是指该进程从产生到结束的这段时间。
在此期间,
内核要详细统计并更新进程使用CPU的时间,一般包括用户态执行时间和系统态执行时间。
有了“时间”的概念,可以实现进程的“定时”操作,即判断系统时间是否到达某个时刻,是否应该执行相关的操作。
Linux提供了许多种定时方式,用户可以灵活使用这些方式来为自己的程序定时。
7、和文件系统相关的信息()
用来存储进程对文件操作的信息,如访问文件的文件描述符等等。
8、虚拟内存相关信息(VirtualMemory)
进程通过自己的mm_struct数据结构描述自己独立的地址空间。
9、内存页面管理相关信息
当系统内实际内存分配不足时,Linux内核内存管理模块会把某些页面搬移到硬盘等辅助存储器。
10、对称多处理器(SMP)信息
Linux内核对SMP进行了全面的支持。
11、和处理器相关的环境(上下文)信息(ProcessorSpecificContext)
进程调度过程中需要保存上下文切换时的处理器现场信息。
12、与进程相关的其他信息
2.2线程及其实现
在传统的操作系统中,进程是系统进行调度和资源分配的基本单位,在任一时刻只执行一个控制流程,这就是单线程(结构)进程(SingleThreadedProcess)。
然而随着并行技术、网络技术和软件设计技术的发展,研究人员提出了多线程(结构)进程(MultipleThreadedProcess)的概念[15],其思想是把“分配资源”与“被调度”这两项功能独立。
进程仍然是操作系统分配资源的独立单位,可以适当避免由于进程被频繁调度而在进程间切换;
线程来作为操作系统新的调度单位。
可以说,进程实现了程序的并发执行,提高了系统效率;
那么线程则可以有效减少系统开销,使多任务系统的并发性能更好。
线程作为可被调度执行的独立实体是进程的组成要素,若某个进程内包含有多个线程,那么该进程就是多线程进程。
该进程中的线程共享操作系统分配给该进程的各种资源。
多线程进程的内存布局如图2-2所示。
由于线程具有许多传统进程所具有的特征,所以,也把线程称为轻量进程
LWP(Light-WeightProcess)。
我们期望通过线程在操作系统和程序设计中来改善系统和应用程序的性能。
然而,在Linux环境下,我们并没有线程这样的结构。
Linux内核并没有对线程做什么特殊的对待,它把线程当做进程处理,也没有特殊的数据结构和调度策略专门为线程服务。
线程同样用task_struct结构来描述并按照进程的管理和调度策略来对待。
因此,线程在Linux环境下可认为是普通的进程。
Linux系统的这种处理方式区别于MicrosoftWindows、SunSolaris等系统的处理方式。
对Linux操作系统而言,线程并不是什么“轻量级进程”,而仅仅是共享系统内各种资源的一种手段。
这一方面简化了线程的设计同时在调度方面,可以不用区分进程和线程,关于线程的调度实际上就是对进程的调度。
第3章Linux内核任务调度系统研究
进程调度是操作系统的核心功能。
从Linux2.4到Linux2.6,内核进程调度程序历经数次改进(Linux内核的修订与改进是相当频繁的),每一次内核调度程序的改善都使内核的性能上升到更高层面。
3.1O(n)调度器
O(n)调度器是指Linux2.4版本内核所采用进程调度程序,基于静态优先级实现。
该调度器为系统内所有CPU维护一个全局运行队列,调度时每次为该队列中优先级最高的那个进程分配CPU执行。
进程在产生时会得到一个时间片。
当进程占有CPU后,其时间片随着进程的运行逐渐减少至零。
当该进程的时间片已消耗完,该进程就必须让出系统分配给它的CPU。
当所有在运行队列中的进程的时间片都消耗完后,由内核重新分派时间片。
O(n)调度器的性能受全局运行队列中的任务数量约束。
就绪进程越多,查找下一个要运行的进程耗时越长。
系统的就绪任务越多,O(n)调度器的效率就越低。
而且,给进程分配多长的时间片才合适也很难判断。
同时,由于系统内只有一个全局的运行队列,该调度器不适合在多核体系结构下应用。
任意一个处理器调度时都需要访问这个全局唯一的运行队列,这样就必须加锁以控制各处理器因访问队列而产生的竞争,而各处理器对锁的争用又会产生新的系统性能瓶颈
3.2Linux内核O
(1)调度器
为了改进O(n)调度器,使系统性能在就绪队列拥有大量进程的情况下有所提升,Linux内核调度器的设计者IngoMolnar提出并实现了O
(1)调度器。
O
(1)调度器在进程调度上所需要的时间是恒定的,和就绪队列的任务数没有关系。
该调度器效率很高,得到了广泛应用。
Linux2.6.23内核版本之前各版本均采用O
(1)调度器。
该调度器还被集成到Linux2.4内核中以改进调度性能。
运行队列结构structrunqueue是O
(1)调度器中一个关键的的数据结构,它主要用于存储每个CPU的就绪队列信息。
限于篇幅,本文在此只说明structrunquene最重要的数据成员:
prio_array_t*active,*expired。
这几个成员是runqueue中最重要的内容。
在Linux内核中,每个CPU只维护一个和它一一对应的就绪队列rq,但rq又根据时间片的使用区分为active和expired两个队列。
active队列是就绪队列中时间片尚未消耗完的就绪进程组成的队列;
expired队列是就绪队列中时间片已消耗完的就绪进程组成的队列。
active和expired指针都是prio_array_t类型。
由typedefstructprio_arrayprio_array_t;
知prio_array_t类型实际上是prio_array类型。
数据结构prio_array的定义如下:
structprio_array{
unsignedintnr_active;
unsignedlongbitmap[BITMAP_SIZE];
structlist_headqueue[MAX_PRIO];
};
其中:
BITMAP_SIZE=5,MAX_PRIO=140。
MAX_PRIO定义系统拥有的优先级个数,其默认值为140。
每一个优先级在内核中都拥有与自己对应的运行队列。
同时内核还维护一个优先级位图bitmap,其作用是提高查找当前系统中拥有最高优先级的可执行进程时的效率。
优先级位图各个位的初值为零,只有当某个优先级的运行队列不为空时,响应的标志位才为1。
优先级数组的示意图如图3-1所示。
第4章Linux内核任务调度系统研究
4.1时间片和优先级的计算
4.1.1时间片的计算方法
在O
(1)调度器中,进程刚被创建时,它的初始时间片从父进程那里继承得来,为了避免进程通过反复fork()来窃取时间片,子进程被创建时并不是分配时间片,而是与父进程平分父进程的剩余时间片,即
同时父进程的时间片也减少一半。
这一过程是在sched_fork()函数(sched.c1145)中实现的。
内核以HZ的频率发生时钟中断,在其处理函数中调用scheduler_tick()函数将进程的时间片减去一毫秒。
一旦普通进程消耗完运行时间片time_slice后,调度器将调用task_timeslice()函数重新计算它的运行时间片,然后把它从CPU上切换下来,将其重新插入到就绪队列,等待再次被调度。
和Linux2.4不同,2.6调度系统中,当进程时间片用完时,会及时地重算。
时间片的递减和重算工作都是在scheduler_tick()函数中实现的。
在O
(1)调度器中,进程的时间片完全由静态优先级static_prio决定。
由此可见,不同的用户执行相同的程序,在O
(1)调度器中,他们创建的进程将获得相同的静态优先级static_prio,由于时间片完全由static_prio决定,
所以,不同用户执行相同的程序,他们创建的进程将获得相同的运行时间片。
这样对高级别的用户是不公平的。
4.1.2优先级的计算过程
O
(1)调度器在进行进程调度时是完全根据进程的动态优先级的大小来确定候选进程的,可见优先级是决定进程能否被尽快调度到的关键因素。
因此,优先级的计算至关重要。
动态优先级的计算主要由函数effective_prio()完成,该函数实现相当简单,总的来说,它是static_prio和sleep_avg的函数
onus是根据进程的sleep_avg计算出来的对其动态优先级prio的奖励。
普通进程的优先级则复杂的多。
Linux2.6中,优先级prio的计算不再集中在调度器选择next进程时,而是分散在进程状态state改变的任何时候。
这些时机有:
①进程被创建时应用程序调用do_fork()创建新进程,在完成拷贝父进程task_t结构、初始化某些成员后,do_fork()调用wake_up_new_task()函数初始化新进程的sleep_avg、interactive_credit等,然后调用effective_prio()计算其prio,根据prio将其插入到就绪队列中等待被CPU调度。
②休眠进程被唤醒时当休眠进程等待的条件满足时,该进程将被唤醒。
此时activate_task()函数将调用recalc_task_prio()函数根据休眠时间来更新进程的prio。
③从TASK_INTERRUPTIBLE状态中被唤醒的进程被调度时调度器在选择了候选进程后,如果该进程是从休眠状态中被唤醒的,调度器将给该进程一定的sleeg_avg奖励,调用recalc_task_prio()函数
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 进程 调度 策略 研究