
在共享服务器上运行 CPU 密集型任务时,一次漫长的编译或后台备份都可能会拖慢其它所有工作。Linux 允许你给这类进程设置较低的优先级,使其在与其它任务争用 CPU 时获得更少的 CPU 时间。启动命令时用 nice 调整优先级,对已经在运行的进程则用 renice 改变其优先级。
本指南讲解 Linux 中 nice 值的工作原理,以及如何使用 nice 和 renice 以不同的优先级启动并调整进程。
nice 值的工作原理
对于使用普通分时调度或批处理调度的进程,内核会用 nice 值来决定它们相对竞争进程应当获得多少 CPU 时间。取值范围是 -20(最高优先级)到 19(最低优先级)。新进程会继承父进程的 nice 值,通常为 0。
nice 值越大,意味着该进程对其它工作更“友好”,在争用时获得的 CPU 份额越小;nice 值越小,份额越大。即便没有争用,nice 值为 19 的进程也能用完一整个 CPU 核心。nice 值并不设定 CPU 使用上限。
在多数系统上,普通用户只能调高自己进程的 nice 值(即降低其优先级)。调低该值通常需要 root 权限,即便只是从 10 改回 0 也是如此。管理员可以通过 RLIMIT_NICE 资源限制来允许例外情况。
你可以在 ps 或 top 的 NI 列中查看进程当前的 nice 值:
ps -eo pid,ni,comm
例如,输出可能如下:
PID NI COMMAND
1 0 systemd
842 0 sshd
1024 10 rsync
上面例子中的 rsync 进程以 nice 值 10 在运行,其优先级低于默认值。
nice 命令语法
nice 命令会以一个对继承 nice 值的调整量来运行命令:
nice [OPTION] [COMMAND [ARGS]...]
不带命令时,nice 会打印它从 shell 继承而来的当前 nice 值:
nice
在 nice 值为 0 的常规 shell 中,输出为:
0
这并不会改变 shell 的优先级。当你传入一条命令时,nice 会向继承的 nice 值加 10,除非你用 -n 选择其它调整量。
下面的示例假设起始 nice 值为 0,并使用来自 coreutils 的 GNU nice 和来自 util-linux 的 renice,它们都是 Linux 上常见的实现。
以较低优先级运行命令
想在不创建文件、也不运行繁重负载的情况下尝试默认调整量,可以用 nice 再运行一份它自己:
nice nice
第二个 nice 会打印出调整之后的值:
10
外层命令向继承值 0 加上 10。内层命令只打印该值然后退出。
一个实用例子是,你可以以较低的 CPU 优先级运行归档任务。选一个未被占用的归档文件名,并把 /var/www 换成你能读取的目录:
nice tar -czf backup.tar.gz /var/www
tar 进程会以 nice 值 10 而非 0 运行。当交互式程序与归档任务争用 CPU 时间时,这有助于它们保持响应。
你可以用 -n 选项选择不同的调整量。例如,运行一个已有的构建脚本时加上 15:
nice -n
15
./build.sh
从起始值 0 开始,构建脚本会以 nice 15 运行。如果你的 shell 已经有了 nice 值 10,结果会是允许的最大值 19,而不是 15。
以较高优先级运行命令
要调高命令的优先级,可使用负的调整量。这通常需要 root 权限。例如,没有合适 nice 资源限制的普通用户可以尝试:
nice -n -5 nice
从起始值 0 开始,GNU nice 会报告调整失败,而内层命令打印出未改变的值:
nice: cannot set niceness: Permission denied
0
即便出现这个权限错误,GNU nice 仍会运行该命令,所以这条警告并不代表任务被中止。
借助 sudo,通常就能应用这个调整量:
sudo nice -n -5 nice
当起始值为 0 时,内层命令打印:
-5
该命令以 root 身份运行。要调高你已经以自己的用户身份启动的作业的优先级,请使用下面所示的 sudo renice。改变其 nice 值并不会改变它的所有者。
谨慎使用负值。一个以 nice -20 运行的 CPU 密集型进程,可能让竞争中的其它工作几乎没有 CPU 时间。
renice 命令语法
renice 命令会改变一个已经在运行的进程的 nice 值:
renice PRIORITY [-p|-g|-u] IDENTIFIER...
默认情况下,renice 把标识符解释为一个进程 ID。-p 选项让这一点更明确,-g 针对进程组,-u 针对某个指定用户拥有的进程。
与 nice -n 不同,这种形式里的优先级是一个绝对的 nice 值。例如,renice 10 -p PID 会把值设为 10,而不是加上 10。
在 util-linux 中,renice -n 10 默认也设为绝对值,但一旦设置了 POSIXLY_CORRECT 环境变量,它就会变成相对调整量。为了让示例含义明确,我们采用不带 -n 的写法。详情请参阅 renice 手册。
更改正在运行的进程的优先级
要降低一个正在运行的进程的优先级,先用 ps 或 pgrep 找到它的 PID,再把它传给 renice。把 4821 换成你自己进程的 PID:
renice
10
-p
4821
如果进程起始于 nice 0,输出为:
4821 (process ID) old priority 0, new priority 10
输出确认了值从 0 变为 10。
想在一个无害的进程上试用,可以在后台启动 sleep,捕获它的 PID,然后改变它的 nice 值:
sleep
60
&
job_pid
=
$!
renice
10
-p
"
$job_pid
"
ps -p
"
$job_pid
"
-o pid,ni,comm
$! 保存着上一个后台任务的 PID。ps 的 NI 列应当显示 10。该进程会在 60 秒后自行退出,所以请在它结束之前运行后面的示例。
要再次改变该值,传入新的绝对值:
renice
15
-p
"
$job_pid
"
这会将该 nice 值从 10 改为 15,而不是加上 15。
把值恢复为 0 会调高进程的优先级,通常也需要 sudo:
sudo renice
0
-p
"
$job_pid
"
在 Linux 上,nice 值是按线程计的。对某个 PID 执行 renice 并不会自动更新每一个工作线程或已有的子进程。新创建的子进程会继承调用线程的 nice 值。
调整某用户的所有进程
要调整某用户当前拥有的进程,用 -u 传入用户名。例如,如果你的备份任务在一个名为 backup 的账户下运行:
sudo renice
15
-u backup
这会把该账户当前的进程设为 nice 15。它并不会为该账户配置一个永久默认值,也不会改变之后独立启动的任务。
调整进程组
要针对进程组,把 -g 与进程组 ID(PGID)一起使用。首先,列出进程 ID 及其所属的组:
ps -eo pid,pgid,ni,comm
如果你想要调整的作业属于组 1820,就把它们的 nice 值设为 5:
sudo renice
5
-g
1820
把 1820 换成你找到的 PGID。一个进程组可以包含同一作业中的多条命令,例如一条 shell 管道的各个阶段。
快速参考
| 任务 | 命令 |
|---|---|
| 查看从 shell 继承的 nice 值 | nice |
| 启动命令时加 10 | nice command |
| 启动命令时加 15 | nice -n 15 command |
| 以 root 身份启动命令时减 5 | sudo nice -n -5 command |
| 把运行中进程设为 nice 10 | renice 10 -p PID |
| 把运行中进程恢复为 nice 0 | sudo renice 0 -p PID |
| 把用户当前进程设为 nice 15 | sudo renice 15 -u USER |
| 把组内进程设为 nice 5 | sudo renice 5 -g PGID |
故障排查
nice: cannot set niceness: Permission denied(无法设置 nice 值:权限被拒绝)——你试图应用负的调整量,却没有提升优先级的权限。请在合适时改用 sudo,或者选择一个非负的调整量。GNU nice 在给出这条警告后,仍会以继承的 nice 值运行该命令。
renice 报告 permission denied 或 operation not permitted(权限被拒绝 / 不允许的操作)——你试图更改一个不属于你的进程,或者在缺少所需权限的情况下调低 nice 值。在多数系统上,即便把自己进程从 nice 10 恢复到 0 也需要 sudo。
renice 报告 no such process(找不到该进程)——进程已经退出,或者 PID 不正确。用 ps 或 pgrep 找到当前的 PID 后重试。在上面 sleep 的例子中,进程会在 60 秒后消失。
nice 值似乎并不影响性能——nice 值只在进程争用 CPU 时间时才起作用。在空闲的系统上,一个被设置过 nice 值的进程仍能用满一个 CPU 核心。用 htop 检查该负载是否受 CPU 限制。
自动分组(autogrouping)和 cgroup CPU 调度也会影响 CPU 时间如何被共享。启用自动分组后,在一个终端会话里改变 nice 值,可能对另一个会话中的作业影响甚微。进程的 nice 值只在同一个调度组内生效;详情请参阅 Linux 调度文档。
备份仍然拖慢了磁盘访问——nice 和 renice 调整的是 CPU 调度,因此它们并不会直接设定磁盘 I/O 优先级。有些 I/O 调度器会从 CPU 的 nice 值派生一个默认的 I/O 优先级,但具体行为取决于调度器。当你需要显式设定 I/O 优先级时,请使用 ionice,并确认你当前使用的 I/O 调度器是否支持它。
结论
对于一个可以等待的长时间任务,先从默认的 nice 调整量开始,并用 ps 查看它的 NI 值。如果你更倾向于交互式地调整优先级,请参阅如何在 htop 中改变 nice 值。