什么是nice值
Linux与大多数其他Unix实现一样,调度进程使用CPU的默认时间是循环时间共享。在这种模型中,每个进程轮流使用CPU一段时间,这段时间被称为时间片或者量子。循环时间共享满足率交互式多任务系统的两个重要需求:
- 公平性:每个进程都有机会用到CPU
- 响应度:一个进程在使用CPU之前无需等待太长时间
在循环时间共享算法中,进程无法直接控制何时使用CPU以及使用CPU的时间。在默认情况下,每个进程轮流使用CPU知道时间片被用光或者自己主动放弃CPU(比如进入睡眠或者执行一个磁盘读取操作)。如果所有进程都试图尽可能的多使用CPU(即没有睡眠或者被IO操作阻塞),那么它们使用CPU的时间差不多是相等的。
进程特性nice值运行进程间接地影响内核的调度算法:
- 每个进程都拥有一个 nice 值,其取值范围为−20(高优先级)~19(低优先级),默认值为 0
- 在传统的 UNIX 实现中,只有特权进程才能够赋给自己(或其他进程)一个负(高)优先级
- 非特权进程只能降低自己的优先级,即赋一个大于默认值 0 的nice 值。
这样做之后它们就对其他进程“友好(nice)”了,这个特性的名称也由此而来。
使用fork()创建子进程时会继承nice值并且该值会在exec()调用中得到保持
getpriority()系统调用服务例程不会返回实际的nice值,相反,它会返回一个范围在 1(低优先级)~40(高优先级)之间的数字,这个数字是通过公式 unice=20-knice 计算得来的。这样做是为了避免让系统调用服务例程返回一个负值,因为负值一般都表示错误
nice值的影响
- 进程的调度不是严格按照nice值的层次进行的,nice值只是一个权重因素,会导致内核调度器倾向于调度高优先级的进程。
- 给一个进程赋一个低优先级(即高 nice 值)并不会导致它完全无法用到 CPU,但会导致它使用 CPU 的时间变少
- nice值对进程调度的影响程度则依据Linux内核版本的不同而不同,在不同Unix系统之间也是不同的
从版本号为 2.6.23 的内核开始,nice 值之间的差别对新内核调度算法的影响比对之前的内核中的调度算法的影响要强。因此,低 nice 值的进程使用 CPU 的时间将比以前少,高nice 值的进程占用 CPU 的时间将大大提高
获取和修改优先级
getpriority()和 setpriority()系统调用允许一个进程获取和修改自身或其他进程的 nice 值
NAME
getpriority, setpriority - get/set program scheduling priority
SYNOPSIS
#include <sys/time.h>
#include <sys/resource.h>
int getpriority(int which, int who);
int setpriority(int which, int who, int prio);
RETURN VALUE
由于 getpriority() 可以合法地返回值 -1,因此需要在调用之前清除外部变量 errno,然后再检查
它以确定 -1 是错误还是合法值。 如果没有错误,则 setpriority() 调用返回 0,如果有则返回 -1
两个系统调用都接收参数 which 和 who,这两个参数用于标识需读取或修改优先级的进程。which 参数确定 who 参数如何被解释。这个参数的取值为下面这些值中的一个
- PRIO_PROCESS :操作进程 ID 为 who 的进程。如果 who 为 0,那么使用调用者的进程 ID
- PRIO_PGRP :操作进程组 ID 为 who 的进程组中的所有成员。如果 who 为 0,那么使用调用者的进程组。
- PRIO_USER :操作所有真实用户 ID 为 who 的进程。如果 who 为 0,那么使用调用者的真实用户 ID
who 参数的类型 id_t 是一个大小能容纳进程 ID 或用户 ID 的整型。
getpriority()系统调用返回由which和who指定的进程的nice值。
- 如果有多个进程符号指定的标准(which 为 PRIO_PGRP 或 PRIO_USER),那么返回优先级最高的nice 值(即最小的数值)
- 由于 getpriority()可能会在成功时返回−1,因此在调用这个函数之前必须要将 errno 设置为 0,接着在调用之后检查返回值为−1 以及 errno 不为 0 才能确认调用成功。
setpriority()系统调用会将由 which 和 who 指定的进程的 nice 值设置为 prio。试图将 nice 值设置为一个超出允许范围的值(-20~+19)时会直接将 nice 值设置为边界值。
- 以前 nice 值是通过调用 nice(incr)来完成的,这个函数会将调用进程的 nice 值加上 incr。现在这个函数仍然是可用的,但已经被更通用的 setpriority()系统调用所取代了。
- 在命令行中与 setpriority()系统调用实现类似功能的命令是 nice(1),非特权用户可以使用这个命令来运行一个优先级更低的命令,特权用户则可以运行一个优先级更高的命令,超级用户则可以使用 renice(8)来修改既有进程的 nice 值。
从版本号为 2.6.12 的内核开始,Linux 提供了 RLIMIT_NICE 资源限制,即允许非特权进程提升 nice 值。非特权进程能够将自己的 nice 值最高提高到公式 20−rlim_cur 指定的值,其中 rlim_cur是当前的 RLIMIT_NICE 软资源限制。
#include <cstdlib>
#include <sys/time.h>
#include <sys/resource.h>
#include <cstdio>
#include <cstring>
#include <errno.h>
int main(int argc, char *argv[])
{
int which, prio;
id_t who;
if (argc != 4 || strchr("pgu", argv[1][0]) == NULL){
printf("%s {p|g|u} who priority\n"
" set priority of: p=process; g=process group; "
"u=processes for user\n", argv[0]);
exit(EXIT_FAILURE);
}
/* Set nice value according to command-line arguments */
which = (argv[1][0] == 'p') ? PRIO_PROCESS :
(argv[1][0] == 'g') ? PRIO_PGRP : PRIO_USER;
who = atoi(argv[2]);
prio = atoi(argv[3]);
if (setpriority(which, who, prio) == -1){
perror("setpriority");
exit(EXIT_FAILURE);
}
/* Retrieve nice value to check the change */
errno = 0; /* Because successful call may return -1 */
prio = getpriority(which, who);
if (prio == -1 && errno != 0){
perror("getpriority");
exit(EXIT_FAILURE);
}
printf("Nice value = %d\n", prio);
exit(EXIT_SUCCESS);
}