Linux进程调度
1.调度方式
Linux系统的调度方式基本上采用“ 抢占式优先级 ”方式,当进程在用户模式下运行时,不管它是否自愿,核心在一定条件下(如该进程的时间片用完或等待I/O)可以暂时中止其运行,而调度其他进程运行。一旦进程切换到内核模式下运行时,就不受以上限制,而一直运行下去,仅在重新回到用户模式之前才会发生进程调度。
Linux系统中的调度基本上继承了UNIX系统的 以优先级为基础 的调度。也就是说,核心为系统中每个进程计算出一个优先级,该优先级反映了一个进程获得CPU使用权的资格,即高优先级的进程优先得到运行。核心从进程就绪队列中挑选一个优先级最高的进程,为其分配一个CPU时间片,令其投入运行。在运行过程中,当前进程的优先级随时间递减,这样就实现了“负反馈”作用,即经过一段时间之后,原来级别较低的进程就相对“提升”了级别,从而有机会得到运行。当所有进程的优先级都变为0(最低)时,就重新计算一次所有进程的优先级。
2.调度策略
Linux系统针对不同类别的进程提供了3种不同的调度策略,即SCHED_FIFO、SCHED_RR及SCHED_OTHER。其中,SCHED_FIFO适合于 短实时进程 ,它们对时间性要求比较强,而每次运行所需的时间比较短。一旦这种进程被调度且开始运行,就一直运行到自愿让出CPU或被优先级更高的进程抢占其执行权为止。
SCHED_RR对应“时间片轮转法”,适合于每次运行需要 较长时间的实时进程 。一个运行进程分配一个时间片(200 ms),当时间片用完后,CPU被另外进程抢占,而该进程被送回相同优先级队列的末尾,核心动态调整用户态进程的优先级。这样,一个进程从创建到完成任务后终止,需要经历多次反馈循环。当进程再次被调度运行时,它就从上次断点处开始继续执行。
SCHED_OTHER是传统的UNIX调度策略,适合于交互式的 分时进程 。这类进程的优先级取决于两个因素:一个是进程剩余时间配额,如果进程用完了配给的时间,则相应优先级降到0;另一个是进程的优先数nice,这是从UNIX系统沿袭下来的方法,优先数越小,其优先级越高。nice的取值范围是-20 19。用户可以利用nice命令设定进程的nice值。但一般用户只能设定正值,从而主动降低其优先级;只有特权用户才能把nice的值设置为负数。进程的优先级就是以上二者之和。
后台命令对应后台进程(又称后台作业)。后台进程的优先级低于任何交互(前台)进程的优先级。所以,只有当系统中当前不存在可运行的交互进程时,才调度后台进程运行。后台进程往往按批处理方式调度运行。
3.调度时机
核心进行进程调度的时机有以下5种情况:
(1)当前进程调用系统调用nanosleep( )或者pause( ),使自己进入睡眠状态,主动让出一段时间的CPU的使用权。
(2)进程终止,永久地放弃对CPU的使用。
(3)在时钟中断处理程序执行过程中,发现当前进程连续运行的时间过长。
(4)当唤醒一个睡眠进程时,发现被唤醒的进程比当前进程更有资格运行。
(5)一个进程通过执行系统调用来改变调度策略或者降低自身的优先级(如nice命令),从而引起立即调度。
4.调度算法
进程调度的算法应该比较简单,以便减少频繁调度时的系统开销。Linux执行进程调度时,首先查找所有在就绪队列中的进程,从中选出优先级最高且在内存的一个进程。如果队列中有实时进程,那么实时进程将优先运行。如果最需要运行的进程不是当前进程,那么当前进程就被挂起,并且保存它的现场—— 所涉及的一切机器状态,包括程序计数器和CPU寄存器等,然后为选中的进程恢复运行现场。
(二)Linux常用调度命令
· nohup命令
nohup命令的功能是以忽略挂起和退出的方式执行指定的命令。其命令格式是:
nohup command [arguments]
其中,command是所要执行的命令,arguments是指定命令的参数。
nohup命令告诉系统,command所代表的命令在执行过程中不受任何结束运行的信号(hangup和quit)的影响。例如,
$ nohup find / -name exam.txt -print>f1 &
find命令在后台运行。在用户注销后,它会继续运行:从根目录开始,查找名字是exam.txt的文件,结果被定向到文件f1中。
如果用户没有对输出进行重定向,则输出被附加到当前目录的nohup.out文件中。如果用户在当前目录中不具备写权限,则输出被定向到$HOME/nohup.out 中。
· at命令
at命令允许指定命令执行的时间。at命令的常用形式是:
at time command
其中,time是指定命令command在将来执行时的时间和日期。时间的指定方法有多种,用户可以使用绝对时间,也可以用相对时间。该指定命令将以作业形式在后台运行。例如:
$ at 15:00 Oct 20
回车后进入接收方式,接着键入以下命令:
mail -s "Happy Birthday!" liuzheny
按下D键,屏幕显示:
job 862960800.a at Wed Oct 20 15:00:00 CST 1999
$
表明建立了一个作业,其作业ID号是862960800.a,运行作业的时间是1999年10月20日下午3:00,给liuzheny发一条标题为“Happy Birthday!”(生日快乐)的空白邮件。
利用 at -l 可以列出当前at队列中所有的作业。
利用 at -r 可以删除指定的作业。这些作业以前由at或batch命令调度。例如,
at -r 862960797.a
将删除作业ID号是862960797.a的作业。其一般使用形式是:
at -r job_id
注意,结尾是.a的作业ID号,表示这个作业是由at命令提交的;结尾是.b的作业ID号,表示这个作业是由batch命令提交的。
· batch命令
batch命令不带任何参数,它提交的作业的优先级比at命令提交的作业的优先级低。batch无法指定作业运行的时间。实际运行时间要看系统中已经提交的作业数量。如果系统中优先级较高的作业比较多,那么,batch提交的作业则需要等待;如果系统空闲,则运行batch提交的作业。例如,
$ batch
回车后进入接收方式,接着键入命令:
find / -name exam.txt -print
按下D。退出接收方式,屏幕显示:
job 862961540.b at Thu Nov 18 14:30:00 CST 1999
表示find命令被batch作为一个作业提交给系统,作业ID号是862961540.b。如果系统当前空闲,这个作业被立即执行,其结果同样作为邮件发送给用户。
· jobs命令
jobs命令用来显示当前shell下正在运行哪些作业(即后台作业)。例如:
$ jobs
[2] + Running tar tv3 *&
[1] - Running find / -name README -print >logfile &
$
其中,第一列方括号中的数字表示作业序号,它是由当前运行的shell分配的,而不是由 *** 作系统统一分配的。在当前shell环境下,第一个后台作业的作业号为1,第二个作业的作业号为2,等等。
第二列中的“ ”号表示相应作业的优先级比“-”号对应作业的优先级高。
第三列表明作业状态,是否为运行、中断、等待输入或停止等。
最后列出的是创建当前这个作业所对应的命令行。
利用 jobs -l 形式,可以在作业号后显示出相应进程的PID。如果想只显示相应进程的PID,不显示其它信息,则使用 jobs -p 形式。
· fg命令
fg命令把指定的后台作业移到前台。其使用格式是:
fg [job…]
其中,参数job是一个或多个进程的PID,或者是命令名称或者作业号(前面要带有一个“%”号)。例如:
$ jobs
[2] + Running tar tv3 *&
[1] - Running find / -name README -print >logfile&
$ fg %find
find / -name README -print >logfile
注意,显示的命令行末尾没有“&”符号。下面命令能产生同样的效果:
$ fg %1
这样,find命令对应的进程就在前台执行。当后台只有一个作业时,键入不带参数的fg命令,就能使相应进程移到前台。当有两个或更多的后台作业时,键入不带参数的fg,就把最后进入后台的进程首先移到前台。
· bg命令
bg命令可以把前台进程换到后台执行。其使用格式是:
bg [job…]
其中,job是一个或多个进程的PID、命令名称或者作业号,在参数前要带“%”号。例如,在cc(C编译命令)命令执行过程中,按下Z键,使这个作业挂起。然后键入以下命令:
$ bg %cc
该挂起的作业在后台重新开始执行。
第一部分: 实时调度算法介绍
对于什么是实时系统,POSIX 1003.b作了这样的定义:指系统能够在限定的响应时间内提供所需水平的服务。而一个由Donald Gillies提出的更加为大家接受的定义是:一个实时系统是指计算的正确性不仅取决于程序的逻辑正确性,也取决于结果产生的时间,如果系统的时间约束条件得不到满足,将会发生系统出错。
实时系统根据其对于实时性要求的不同,可以分为软实时和硬实时两种类型。硬实时系统指系统要有确保的最坏情况下的服务时间,即对于事件的响应时间的截止期限是无论如何都必须得到满足。比如航天中的宇宙飞船的控制等就是现实中这样的系统。其他的所有有实时特性的系统都可以称之为软实时系统。如果明确地来说,软实时系统就是那些从统计的角度来说,一个任务(在下面的论述中,我们将对任务和进程不作区分)能够得到有确保的处理时间,到达系统的事件也能够在截止期限到来之前得到处理,但违反截止期限并不会带来致命的错误,像实时多媒体系统就是一种软实时系统。
一个计算机系统为了提供对于实时性的支持,它的 *** 作系统必须对于CPU和其他资源进行有效的调度和管理。在多任务实时系统中,资源的调度和管理更加复杂。本文下面将先从分类的角度对各种实时任务调度算法进行讨论,然后研究普通的 Linux *** 作系统的进程调度以及各种实时Linux系统为了支持实时特性对普通Linux系统所做的改进。最后分析了将Linux *** 作系统应用于实时领域中时所出现的一些问题,并总结了各种实时Linux是如何解决这些问题的。
1. 实时CPU调度算法分类
各种实时 *** 作系统的实时调度算法可以分为如下三种类别[Wang99][Gopalan01]:基于优先级的调度算法(Priority-driven scheduling-PD)、基于CPU使用比例的共享式的调度算法(Share-driven scheduling-SD)、以及基于时间的进程调度算法(Time-driven scheduling-TD),下面对这三种调度算法逐一进行介绍。
1.1. 基于优先级的调度算法
基于优先级的调度算法给每个进程分配一个优先级,在每次进程调度时,调度器总是调度那个具有最高优先级的任务来执行。根据不同的优先级分配方法,基于优先级的调度算法可以分为如下两种类型[Krishna01][Wang99]:
静态优先级调度算法:
这种调度算法给那些系统中得到运行的所有进程都静态地分配一个优先级。静态优先级的分配可以根据应用的属性来进行,比如任务的周期,用户优先级,或者其它的预先确定的策略。RM(Rate-Monotonic)调度算法是一种典型的静态优先级调度算法,它根据任务的执行周期的长短来决定调度优先级,那些具有小的执行周期的任务具有较高的优先级。
动态优先级调度算法:
这种调度算法根据任务的资源需求来动态地分配任务的优先级,其目的就是在资源分配和调度时有更大的灵活性。非实时系统中就有很多这种调度算法,比如短作业优先的调度算法。在实时调度算法中, EDF算法是使用最多的一种动态优先级调度算法,该算法给就绪队列中的各个任务根据它们的截止期限(Deadline)来分配优先级,具有最近的截止期限的任务具有最高的优先级。
1.2. 基于比例共享调度算法
虽然基于优先级的调度算法简单而有效,但这种调度算法提供的是一种硬实时的调度,在很多情况下并不适合使用这种调度算法:比如象实时多媒体会议系统这样的软实时应用。对于这种软实时应用,使用一种比例共享式的资源调度算法(SD算法)更为适合。
比例共享调度算法指基于CPU使用比例的共享式的调度算法,其基本思想就是按照一定的权重(比例)对一组需要调度的任务进行调度,让它们的执行时间与它们的权重完全成正比。
我们可以通过两种方法来实现比例共享调度算法[Nieh01]:第一种方法是调节各个就绪进程出现在调度队列队首的频率,并调度队首的进程执行;第二种做法就是逐次调度就绪队列中的各个进程投入运行,但根据分配的权重调节分配个每个进程的运行时间片。
比例共享调度算法可以分为以下几个类别:轮转法、公平共享、公平队列、彩票调度法(Lottery)等。
比例共享调度算法的一个问题就是它没有定义任何优先级的概念;所有的任务都根据它们申请的比例共享CPU资源,当系统处于过载状态时,所有的任务的执行都会按比例地变慢。所以为了保证系统中实时进程能够获得一定的CPU处理时间,一般采用一种动态调节进程权重的方法。
1.3. 基于时间的进程调度算法
对于那些具有稳定、已知输入的简单系统,可以使用时间驱动(Time-driven:TD)的调度算法,它能够为数据处理提供很好的预测性。这种调度算法本质上是一种设计时就确定下来的离线的静态调度方法。在系统的设计阶段,在明确系统中所有的处理情况下,对于各个任务的开始、切换、以及结束时间等就事先做出明确的安排和设计。这种调度算法适合于那些很小的嵌入式系统、自控系统、传感器等应用环境。
这种调度算法的优点是任务的执行有很好的可预测性,但最大的缺点是缺乏灵活性,并且会出现有任务需要被执行而CPU却保持空闲的情况。
2. 通用Linux系统中的CPU调度
通用Linux系统支持实时和非实时两种进程,实时进程相对于普通进程具有绝对的优先级。对应地,实时进程采用SCHED_FIFO或者SCHED_RR调度策略,普通的进程采用SCHED_OTHER调度策略。
在调度算法的实现上,Linux中的每个任务有四个与调度相关的参数,它们是rt_priority、policy、priority(nice)、counter。调度程序根据这四个参数进行进程调度。
在SCHED_OTHER 调度策略中,调度器总是选择那个priority+counter值最大的进程来调度执行。从逻辑上分析,SCHED_OTHER调度策略存在着调度周期(epoch),在每一个调度周期中,一个进程的priority和counter值的大小影响了当前时刻应该调度哪一个进程来执行,其中 priority是一个固定不变的值,在进程创建时就已经确定,它代表了该进程的优先级,也代表这该进程在每一个调度周期中能够得到的时间片的多少; counter是一个动态变化的值,它反映了一个进程在当前的调度周期中还剩下的时间片。在每一个调度周期的开始,priority的值被赋给 counter,然后每次该进程被调度执行时,counter值都减少。当counter值为零时,该进程用完自己在本调度周期中的时间片,不再参与本调度周期的进程调度。当所有进程的时间片都用完时,一个调度周期结束,然后周而复始。另外可以看出Linux系统中的调度周期不是静态的,它是一个动态变化的量,比如处于可运行状态的进程的多少和它们priority值都可以影响一个epoch的长短。值得注意的一点是,在2.4以上的内核中, priority被nice所取代,但二者作用类似。
可见SCHED_OTHER调度策略本质上是一种比例共享的调度策略,它的这种设计方法能够保证进程调度时的公平性--一个低优先级的进程在每一个epoch中也会得到自己应得的那些CPU执行时间,另外它也提供了不同进程的优先级区分,具有高priority值的进程能够获得更多的执行时间。
对于实时进程来说,它们使用的是基于实时优先级rt_priority的优先级调度策略,但根据不同的调度策略,同一实时优先级的进程之间的调度方法有所不同:
SCHED_FIFO:不同的进程根据静态优先级进行排队,然后在同一优先级的队列中,谁先准备好运行就先调度谁,并且正在运行的进程不会被终止直到以下情况发生:1.被有更高优先级的进程所强占CPU;2.自己因为资源请求而阻塞;3.自己主动放弃CPU(调用sched_yield);
SCHED_RR:这种调度策略跟上面的SCHED_FIFO一模一样,除了它给每个进程分配一个时间片,时间片到了正在执行的进程就放弃执行;时间片的长度可以通过sched_rr_get_interval调用得到;
由于Linux系统本身是一个面向桌面的系统,所以将它应用于实时应用中时存在如下的一些问题:
Linux系统中的调度单位为10ms,所以它不能够提供精确的定时;
当一个进程调用系统调用进入内核态运行时,它是不可被抢占的;
Linux内核实现中使用了大量的封中断 *** 作会造成中断的丢失;
由于使用虚拟内存技术,当发生页出错时,需要从硬盘中读取交换数据,但硬盘读写由于存储位置的随机性会导致随机的读写时间,这在某些情况下会影响一些实时任务的截止期限;
虽然Linux进程调度也支持实时优先级,但缺乏有效的实时任务的调度机制和调度算法;它的网络子系统的协议处理和其它设备的中断处理都没有与它对应的进程的调度关联起来,并且它们自身也没有明确的调度机制;
3. 各种实时Linux系统
3.1. RT-Linux和RTAI
RT -Linux是新墨西哥科技大学(New Mexico Institute of Technology)的研究成果[RTLinuxWeb][Barabanov97]。它的基本思想是,为了在Linux系统中提供对于硬实时的支持,它实现了一个微内核的小的实时 *** 作系统(我们也称之为RT-Linux的实时子系统),而将普通Linux系统作为一个该 *** 作系统中的一个低优先级的任务来运行。另外普通Linux系统中的任务可以通过FIFO和实时任务进行通信。RT-Linux的框架如图 1所示:
图 1 RT-Linux结构
RT -Linux的关键技术是通过软件来模拟硬件的中断控制器。当Linux系统要封锁CPU的中断时时,RT-Linux中的实时子系统会截取到这个请求,把它记录下来,而实际上并不真正封锁硬件中断,这样就避免了由于封中断所造成的系统在一段时间没有响应的情况,从而提高了实时性。当有硬件中断到来时, RT-Linux截取该中断,并判断是否有实时子系统中的中断例程来处理还是传递给普通的Linux内核进行处理。另外,普通Linux系统中的最小定时精度由系统中的实时时钟的频率决定,一般Linux系统将该时钟设置为每秒来100个时钟中断,所以Linux系统中一般的定时精度为 10ms,即时钟周期是10ms,而RT-Linux通过将系统的实时时钟设置为单次触发状态,可以提供十几个微秒级的调度粒度。
RT-Linux实时子系统中的任务调度可以采用RM、EDF等优先级驱动的算法,也可以采用其他调度算法。
RT -Linux对于那些在重负荷下工作的专有系统来说,确实是一个不错的选择,但他仅仅提供了对于CPU资源的调度;并且实时系统和普通Linux系统关系不是十分密切,这样的话,开发人员不能充分利用Linux系统中已经实现的功能,如协议栈等。所以RT-Linux适合与工业控制等实时任务功能简单,并且有硬实时要求的环境中,但如果要应用与多媒体处理中还需要做大量的工作。
意大利的RTAI( Real-Time Application Interface )源于RT-Linux,它在设计思想上和RT-Linux完全相同。它当初设计目的是为了解决RT-Linux难于在不同Linux版本之间难于移植的问题,为此,RTAI在 Linux 上定义了一个实时硬件抽象层,实时任务通过这个抽象层提供的接口和Linux系统进行交互,这样在给Linux内核中增加实时支持时可以尽可能少地修改 Linux的内核源代码。
3.2. Kurt-Linux
Kurt -Linux由Kansas大学开发,它可以提供微秒级的实时精度[KurtWeb] [Srinivasan]。不同于RT-Linux单独实现一个实时内核的做法,Kurt -Linux是在通用Linux系统的基础上实现的,它也是第一个可以使用普通Linux系统调用的基于Linux的实时系统。
Kurt-Linux将系统分为三种状态:正常态、实时态和混合态,在正常态时它采用普通的Linux的调度策略,在实时态只运行实时任务,在混合态实时和非实时任务都可以执行;实时态可以用于对于实时性要求比较严格的情况。
为了提高Linux系统的实时特性,必须提高系统所支持的时钟精度。但如果仅仅简单地提高时钟频率,会引起调度负载的增加,从而严重降低系统的性能。为了解决这个矛盾, Kurt-Linux采用UTIME所使用的提高Linux系统中的时钟精度的方法[UTIMEWeb]:它将时钟芯片设置为单次触发状态(One shot mode),即每次给时钟芯片设置一个超时时间,然后到该超时事件发生时在时钟中断处理程序中再次根据需要给时钟芯片设置一个超时时间。它的基本思想是一个精确的定时意味着我们需要时钟中断在我们需要的一个比较精确的时间发生,但并非一定需要系统时钟频率达到此精度。它利用CPU的时钟计数器TSC (Time Stamp Counter)来提供精度可达CPU主频的时间精度。
对于实时任务的调度,Kurt-Linux采用基于时间(TD)的静态的实时CPU调度算法。实时任务在设计阶段就需要明确地说明它们实时事件要发生的时间。这种调度算法对于那些循环执行的任务能够取得较好的调度效果。
Kurt -Linux相对于RT-Linux的一个优点就是可以使用Linux系统自身的系统调用,它本来被设计用于提供对硬实时的支持,但由于它在实现上只是简单的将Linux调度器用一个简单的时间驱动的调度器所取代,所以它的实时进程的调度很容易受到其它非实时任务的影响,从而在有的情况下会发生实时任务的截止期限不能满足的情况,所以也被称作严格实时系统(Firm Real-time)。目前基于Kurt-Linux的应用有:ARTS(ATM Reference Traffic System)、多媒体播放软件等。另外Kurt-Linux所采用的这种方法需要频繁地对时钟芯片进行编程设置。
3.3. RED-Linux
RED -Linux是加州大学Irvine分校开发的实时Linux系统[REDWeb][ Wang99],它将对实时调度的支持和Linux很好地实现在同一个 *** 作系统内核中。它同时支持三种类型的调度算法,即:Time-Driven、 Priority-Dirven、Share-Driven。
为了提高系统的调度粒度,RED-Linux从RT-Linux那儿借鉴了软件模拟中断管理器的机制,并且提高了时钟中断频率。当有硬件中断到来时,RED-Linux的中断模拟程序仅仅是简单地将到来的中断放到一个队列中进行排队,并不执行真正的中断处理程序。
另外为了解决Linux进程在内核态不能被抢占的问题, RED-Linux在Linux内核的很多函数中插入了抢占点原语,使得进程在内核态时,也可以在一定程度上被抢占。通过这种方法提高了内核的实时特性。
RED-Linux的设计目标就是提供一个可以支持各种调度算法的通用的调度框架,该系统给每个任务增加了如下几项属性,并将它们作为进程调度的依据:
Priority:作业的优先级;
Start-Time:作业的开始时间;
Finish-Time:作业的结束时间;
Budget:作业在运行期间所要使用的资源的多少;
通过调整这些属性的取值及调度程序按照什么样的优先顺序来使用这些属性值,几乎可以实现所有的调度算法。这样的话,可以将三种不同的调度算法无缝、统一地结合到了一起。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)