点我查看操作系统秘籍连载


Linux的进程调度和调整优先级

关于进程调度,应该时刻记住的是,对于我们用户来说,进程的调度完全可以看作是随机的,因为我们根本不知道下一个要调度到哪个进程。在分析多进程或多线程问题时,调度到哪个进程并不重要,重要的是进程调度的不确定性和随机性造成的影响。

在此前提下,再来了解下Linux中的进程调度器。

Linux系统中的调度器基于调度类实现,每个调度类都可以使用不同的调度算法。自Linux 2.6.23开始,标准Linux支持两种调度类:完全公平调度算法(Completely Fair Scheduler,CFS)和实时调度类(real-time scheduling class)。在Linux 2.6.23之前使用过多个调度算法,比如很长一段时间使用的是实时调度类,从2.6.23开始,已经使用CFS作为默认的调度算法。当然,Linux内核还支持其它调度算法。

无论使用什么调度算法,对使用Linux系统的用户来说,都无法直接让调度器去决定优先调度哪个进程,因为调度对用户来说是未知的。但是,用户可以通过修改优先级间接地影响Linux的调度器:优先级越高,越有可能先被调度到,优先级越低,越后被调度到。

此外,对于成熟的抢占式调度算法来说,改变进程优先级,都会影响为进程分配的时间片长度。根据之前对多级反馈队列调度算法的分析,进程的优先级越高,意味着该进程很可能是交互式或IO密集型进程,应当为其分配更短的时间片;而优先级越低,意味着该进程很可能是CPU密集型或后台服务类进程,应当为其分配更长的时间片。在Windows和Solaris系统中,确实是如此分配的,但是在Linux中正好相反,Linux为高优先级的任务分配更长的时间片,为低优先级的任务分配更短的时间片。孰优孰劣,这不是我们需要考虑的。

在Linux命令行下,可以通过nice命令或renice命令手动修改进程的nice属性值,从而改变进程的优先级。此外,还可以使用ionice命令来改变进程的IO调度优先级。它们的用法类似。

Linux的nice属性

nice和进程PID一样,是进程的一个属性,可以用来间接地影响该进程的优先级,从而改变调度程序对该进程的调度。

nice属性提供给用户的取值范围为-20~19,默认为0。需要注意的是,值越小,优先级越高,所以-20是最高优先级,19是最低优先级。另外,只有特权用户才能使用-20~0的优先级,普通用户只能使用正数优先级(这个限制可以使用ulimit命令修改或修改/etc/security/limit.conf文件)。如图:

此外,Linux支持的实时调度类(real-time scheduling class)采用两个范围的nice值,它将所有的进程分为140个优先级队列,其中nice值在0-99范围内的进程是实时进程,nice值在100-139范围内的进程是普通进程,同样的nice值越小优先级越高。其中100-139这个范围的nice值映射到全局nice值,即-20~19,-20对应的是100,19对应的139。如图所示。

所以,即使使用的是实时调度类,也可以使用同样的方式修改进程的优先级。而且,能修改的范围只有100-139

之所以0-99不能修改,是因为这个优先级范围的进程都是实时进程。所谓实时,即响应时间非常短,交互性非常好,这类进程必须以最高优先级去调度。例如交通导航系统,响应慢了可能会导致严重灾难,像一些军用反导弹系统会使用专门定制的调度算法,因为它们要保证响应时间足够短。

nice命令和renice命令

nice和renice命令都能修改进程的优先级。它们的区别是,nice只能设置将要运行的命令的优先级,而renice可以改变已有进程的优先级。

nice命令使用-n选项指定将要运行的命令的优先级数值,范围为-20~19(非特权用户只能使用0-19)。

例如,删除包含大量小文件的目录时,指定rm进程以低优先级运行。

1
$ nice -n 19 rm -rf /dir

这是一个比较有用的命令,当一个目录下包含了大量小文件,直接rm删除将会消耗大量CPU资源,这时可以指定它以最低优先级运行。当然相对应的,低优先级的rm命令可能会花费大量时间才能删除完成。如果想要快速删除,有多种技巧,比如关闭ext文件系统的日志功能、使用find+xargs+rm多进程并行删除、使用rsync删除、使用编程语言的遍历+unlink等,总之不要直接rm删除目录,因为rm直接删除目录时不够聪明,资源消耗可能会比较重(并非是因为速度慢,其实删整个目录的速度并不慢)。

renice命令可以指定已运行命令的优先级。

1
2
3
renice [-n] <priority> [-p|--pid] <pid>...
renice [-n] <priority> -g|--pgrp <pgid>...
renice [-n] <priority> -u|--user <user>...

-n指定优先级数值,-p选项可以指定一个或多个pid的优先级,使用-g选项可以指定进程组内所有进程的优先级,使用-u选项可以指定该用户的所有进程优先级。

例如,修改sleep进程的优先级

1
2
3
4
5
6
7
8
$ sleep 30&           
[1] 64121

$ renice +10 64121
64121 (process ID) old priority 0, new priority 10

$ renice +10 64121
64121 (process ID) old priority 10, new priority 10

再例如,修改PID为987和32的进程以及daemon和root用户的进程的优先级为1。

1
$ renice +1 987 -u daemon root -p 32

ionice命令

ionice命令可以修改已有进程的IO调度优先级,也可以让某命令以指定的IO调度优先级运行。IO调度优先级越高,意味着获取磁盘的时间越长。

1
2
3
4
5
6
7
ionice [OPTION] -p PID [PID...]
ionice [OPTION] COMMAND

选项说明:
-c, --class <class>:指定调度类,支持0: none, 1: realtime, 2: best-effort, 3: idle
-n, --classdata <num>:指定调度类的优先级数值,支持0-7共8个优先级值
-p, --pid=PID:指定要查看或修改的进程的pid值

一般renice只会使用idle(3)和best-effort(2)这两种调度类,none调度类从Linux 2.6.26开始默认使用CFQ I/O调度算法后不再使用,在此之前它代表的是那些没有发出IO请求的进程。而realtime的IO调度类表现不佳,所以现在也不用,renice时也不建议使用。

idle调度类没有参数,它直接表示最低IO调度的优先级。

best-effort是默认的IO调度类,其参数值为0-7,0优先级最高,7优先级最低。

例如,修改sleep进程,使其优先级最低。

1
2
3
4
5
$ sleep 30 &               
[1] 64244

$ ionice -c 3 -p 64244
$ ionice -n 7 -p 64244

再例如,rm删除大文件时,指定其以低IO优先级运行。

1
$ ionice -n 7 rm -rf huge-file.log

和删除包含大量文件的目录一样,这个命令也是比较有用的。当删除一个大文件,将会占用大量磁盘IO(和具体的文件系统有关),耗时也会较长(也和具体的文件系统有关),有时候会想让它以低优先级的方式运行,以便将磁盘资源交给其它重要的进程,那么就可以这样做。删除大文件更好的方法,是使用truncate或重定向截断大文件,或者使用/dev/null来清空文件。

此外,ionice和nice可以结合起来使用。例如:

1
2
$ nice -n 19 ionice -n 3 COMMAND
$ ionice -n 3 nice -n 19 COMMAND

提示:高效删除大量小文件

如果一个目录中包含了大量小文件。

如果是删除目录中部分文件:
(1).find /tmp/temp/ -type f -name “*.jpg” -print0 | xargs -0 -P4 -n20000 rm -rf
(2).cd /tmp/temp/; perl -e ‘unlink for (<”*.jpg”>)’

如果是删除目录中所有文件:
(1).rm -rf /tmp/temp;mkdir -p /tmp/temp
(2).mkdir /tmp/empty;rsync -r –delete /tmp/empty/ /tmp/temp/
(3).cd /tmp/temp/; perl -e ‘unlink for (<”*“>)’