在配合开发调试过程中,发现进程因使用基于FIFO的实时优先级,而另一个与之交互的进程是使用普通调度的优先级,两个进程是互相通信的,它们在工作一段时间后都不能正常工作。初步分析为进程优先级的设定问题,分析问题过程中查找了一些资料,以加强自己的理解。

Linux进程调度的三种策略
(1)、SCHED_OTHER,分时调度策略
(2)、SCHED_FIFO,实时调度策略,先到先服务
(3)、SCHED_RR,实时调度策略,时间片轮转

由于涉及相关内容比较多,很难讲的清楚,关于进程调度更详细的内容建议读Linux内核相关的书籍。

前面遇到问题的进程就是使用SCHED_FIFO调度策略的,而另进程没有经过设置,默认是SCHED_OTHER。

通常意义上来讲,我们平时说优先级也正是指的上面字面意义上的优先级,代表对 cpu 的优先使用级别,高优先级的进程先使用,低优先级的进程后使用。对于没有时间片限制的一些调度算法来说,只有最高优先级的进程主动让出 cpu(完成它自己的工作),次低一点优先级的进程才可以获取 cpu 使用权。而对于有时间片限制的调度算法,优先级高的进程则代表它优先、较多地享有 cpu 使用权,注意:优先、较多。它意味着可能不等到高优先级任务主动出让 cpu,就会被强制切换到更低一点优先级的进程,但是总体来说,高优先级的进程会有比其它进程更多的 cpu 使用时间。

Linux 系统的调度类

实时调度类
Linux 上面的实时调度不是硬实时的,而是尽可能的实时,比如 SCHED_RR、SCHED_FIFO 两种,采用 RT 调度算法。但是估计是这种实时调度策略只能够在某些特定的场景下发挥作用,而有一些对周期要求比较严格的场景,比如多媒体数据流,则无法满足。于是后续出台了更硬的实时调度策略,它就是 SCHED_DEADLINE,采用 EDF 调度算法。

非实时调度类
就是大名鼎鼎的完全公平调度 CFS(Completely fair schedule)。在内核定义中是 SCHED_OTHER 或者 SCHED_NORMAL,这两个代表同样的含义。还有 SCHED_BATCH 与 SCHED_IDLE 这两种,不过不是很常用,不过他们同属于 CFS 调度算法,也就是说它们的优先级概念是一样的。

Linux 上面的调度类主要就是这几个了,还有一类叫做 SCHED_ISO,不过在内核里面作为保留字段,并没有实现。其中实时调度类使用 RT 调度算法,不过实时类的 SCHED_DEADLINE 调度类是 EDF 调度算法,非实时的调度类就是 CFS 调度算法了,可以推知,在 Linux 系统上面有三种优先级的概念,它们分别归属于不同的调度算法上,如下图所示:



在Linux中,调度器是基于线程的调度策略(scheduling policy)和静态调度优先级(static scheduling priority)来决定那个线程来运行。对于下面三种调度策略SCHED_OTHER, SCHED_IDLE, SCHED_BATCH,其调度优先级sched_priority是不起作用的,即可以看成其调度优先级为0;调度策略SCHED_FIFO和SCHED_RR是实时策略,他们的调度值范围是1到99,数值越大优先级越高,另外实时调度策略的线程总是比前面三种通常的调度策略优先级更高。通常,调度器会为每个可能的调度优先级(sched_priority value)维护一个可运行的线程列表,并且是以最高静态优先级列表头部的线程作为下次调度的线程。所有的调度都是抢占式的:如果一个具有更高静态优先级的线程转换为可以运行了,那么当前运行的线程会被强制进入其等待的队列中。下面介绍几种常见的调度策略:

SCHED_OTHER:该策略是是默认的Linux分时调度(time-sharing scheduling)策略,它是Linux线程默认的调度策略。SCHED_OTHER策略的静态优先级总是为0,对于该策略列表上的线程,调度器是基于动态优先级(dynamic priority)来调度的,动态优先级是跟nice中相关(nice值可以由接口nice,setpriority,sched_setattr来设置),该值会随着线程的运行时间而动态改变,以确保所有具有SCHED_OTHER策略的线程公平运行。在Linux上,nice值的范围是-20到+19,默认值为0;nice值越大则优先级越低,相比高nice值(低优先级)的进程,低nice值(高优先级)的进程可以获得更多的处理器时间。使用命令ps -el查看系统的进程列表,其中NI列就是进程对应的nice值;使用top命令,看到的NI列也是nice值。运行命令的时候可用nice –n xx cmd来调整cmd任务的nice值,xx的范围是-20~19之间。

SCHED_FIFO:先入先出调度策略(First in-first out scheduling)。该策略简单的说就是一旦线程占用cpu则一直运行,一直运行直到有更高优先级任务到达或自己放弃。

SCHED_RR:时间片轮转调度(Round-robin scheduling)。该策略是SCHED_FIFO基础上改进来的,他给每个线程增加了一个时间片限制,当时间片用完后,系统将把该线程置于队列末尾。放在队列尾保证了所有具有相同优先级的RR任务的调度公平。使用top命令,如果PR列的值为RT,则说明该进程采用的是实时策略,即调度策略是SCHED_FIFO或者为SCHED_RR,而对于非实时调度策略(比如SCHED_OTHER)的进程,该列的值是NI+20,以供Linux内核使用。可以通过命令:
ps -eo state,uid,pid,ppid,rtprio,time,comm

来查看进程对应的实时优先级(位于RTPRIO列下),如果有进程对应的列显示'-',则说明它不是实时进程。注意任何实时策略进程的优先级都高于普通的进程,也就说实时优先级和nice优先级处于互不相交的两个范畴。

调度类的优先级
在内核里面,调度类的优先级顺序如下所示:

SCHED_DEADLINE > SCHED_FIFO/SCHEDRR > SCHED_NORMAl/SCHED_OTHER/SCHED_BATCH > SCHED_IDLE
 

调度类内核归一化优先级范围user 可设置优先级/NICE范围设置函数调度算法
SCHED_OTHER/NORMAL100~139-20~19sched_setpriorityCFS
SCHED_IDLE100~139-20~19sched_setpriorityCFS
SCHED_BATCH100~139-20~19sched_setpriorityCFS
SCHED_RR0~9899~1sched_setparamRT
SCHED_FIFO0~9899~1sched_setparamRT
SCHED_DEADLINE-1不可设置sched_setparamEDF



如何设置为实时进程

查找资料的时候发现有个链接,为什么设置FIFO策略,但和预想的不一致。可以通过top命令查看进程是否成功,如果为"rt"表示是实时进程了。如果不成功,可能是权限问题,需要roo权限。如果不调整调度策略,也可以提升进程优先级,使得进程得到更多的CPU,特别是交互式程序,用户体检更好。代码很简单,只需要调用nice(int n)函数即可。n的有效范围是-20~19,数值越小表示优先级越高。

用户空间的 NI,PRI

除了内核空间里面繁杂、错综的优先级概念,在用户空间也有一些容易混淆的概念,它们之间有着某种联系,这里主要就说用户空间使用 top 命令看到的 NI 与 PRI 。

NI 就是 nice 值,顾名思义,就是这个进程的友好程度,越友好的程序 NI 值越大(取值范围 -20~19),也就越慷慨,它们会使用相对较少的 cpu 时间,所谓优先级低;而不那么友好的进程则使用相对较多的 cpu 时间,所谓优先级高。由此可见,nice 值代表了可以使用的 cpu 资源的比例。

PRI 就是优先级,这个优先级不对应内核里面任何一个优先级的概念,可以看到它的值跟随 NI 值不断变化,在 CFS 调度时关系看起来就像是:PRI = 20+NI。实际上:PRI(new)=PRI(old)+nice,用户空间的进程刚初始化是默认都是 PRI=20,NI=0,当 NI 的值改变的时候,PRI 的值就显而易见。

在用户空间,我们想要设置某一个进程的 nice 值,那么这个进程一定是 CFS 调度算法下面的调度类,否则无意义,如果想让这个程序尽可能优先的运行,那么通过 nice 值修改可能并不会得到想象中的效果,更好的办法是提升进程的调度类,比如从 SCHED_OTHER 升级到 SCHED_RR。如果想让自己的进程尽可能多地使用 cpu 时间,那么减少 nice 值就是一个很好的选择。


用top或者ps -l查看进程会发现有PR(PRI) NI两个字段:
NI 是优先值,是用户层面的概念, PR是进程的实际优先级, 是给内核(kernel)看(用)的。
一般情况下,PR=NI+20, 如果一个进程的优先级PR是20, 那么它的NI(nice)值就是20-20=0。

进程调度优先级是从-20到19,一共40个级别,数字越大,表示进程的优先级越低。默认时候,进程的优先级是0。查看进程优先级有两个办法:ps和top。top命令显示的NI列的值,或者可以使用ps -efl来查看,也是在ni列表示了进程的优先级。

进程的优先级可以在程序运行的时候设置,也可以在程序运行过程中动态的修改。运行的时候设置进程的优先级可以使用nice命令,比如要使得top命令运行时候的优先级是5而不是默认的0,则可以使用nice -n 5 top,来使得top命令运行在5的优先级别。如果top命令已经在运行,则有两个办法可以动态的调整进程的级别。可以在top中输入r命令,然后按照提示输入top命令对应的进程号,再按照提示输入要调整到哪个级别。另一个方法是使用renice命令,帮助如下:
$ renice --help
usage: renice priority [ [ -p ] pids ] [ [ -g ] pgrps ] [ [ -u ] users ]

此命令使用也很简单,可以调整单个进程,一个用户或者一个组的所有进程的优先级。示例如下:
#renice +10 -u oracle,此命令把oracle用户的所有进程的优先级全部调为10,包括新创建的和已经在运行的oracle用户的所有进程。此处的+10并不是表 示在现有级别上再往上调整10个级别,而是调整到正10的级别,所以多次运行此命令,进程的优先级不会再发生变化,将一直停留在+10级别。

#renice 10 1825  将PID为1825的进程优先级调整为10

注意:如果不是root权限,则只能降调度优先级而不能提高,即使是自己用户的进程,自己把它调高了后,优先级也不能再被调会原来的值了,除非使用root用户来调回去。系统重启后,对进程优先级的调整全部失效,所有进程的调度回到默认的初始级别。

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐