Linux进程优先级操作
- 进程优先级
-
- 基本概念
- [Linux 中的优先级](#Linux 中的优先级)
-
- [ps -l查看系统进程](#ps -l查看系统进程)
- [Linux 下测试调整优先级](#Linux 下测试调整优先级)
- 进程优先级有关的系统调用
- Linux进程优先级相关命令
-
- top指令
- [ps -l](#ps -l)
- nice和renice
忙完毕业的各种琐事,短暂回归。
进程优先级
在生活中,排队的本质就叫做优先级。例如食堂的窗口,谁先到谁就能获得食物。
所有的排队行为都取决于资源不足,包括计算机。计算机上面有很多硬件资源,例如 CPU,内存,磁盘等等,在很多时候软、硬件资源是没法满足巨量的进程的需求的,所以需要进行排队。
系统进程数目众多 ,而 CPU 资源只有少量 ,甚至 1 个,所以进程之间是具有竞争性 的。为了高效完成任务 ,更合理竞争相关资源 ,便具有了优先级。
若进程数量极少,使得大量外部设备处于空闲状态,操作系统会进行资源管理和能效优化,操作系统的管理策略会从"追求性能"向"追求能效和快速响应"转变。
这个不是这里的重点,以后有机会再补充。
计算机在设计时,多选择冯诺依曼体系结构,是因为这个结构有考虑成本,它的软件方面也在帮用户考虑成本。正是因为它的各种算法,各种队列的存在,才使得用户的计算机里不需要存在很多的键盘,显示器,CPU ,也照样使计算机能进行工作。
所以优先级的本质 是得到某种资源的先后顺序 ,它存在的意义是要用更少的资源来完成各种任务。
优先级是学习CPU 调度算法(CFS、O(1)调度器)的基础。很多面试、笔试都可能会考察,实际应用时需结合实战,这里只能先铺垫一些基础知识,大饼只能留到以后。
基本概念
CPU 资源分配的先后顺序 ,就是指进程的优先权(priority)。
优先权高的进程有优先执行权利。配置进程优先权对多任务环境的 Linux 很有用,可以改善系统性能,还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。
优先级和权限:
优先级是保证能够得到申请的资源,只不过需要进行等待。
而权限是限制哪些用户能访问目标资源,哪些用户不能。进程能访问某种资源,前提条件是进程要先有权限。
有人问过优先级和权限的区别,这里就简单描述一下。
Linux 中的优先级
和之前的进程 ID 、进程状态一样,优先级 就是 PCB 中的一个整型字段 (整型变量 )。这个字段的数值越小 ,优先级越大 ,反之就是字段的数值越大 ,优先级越小。
在 Linux 中,优先级字段因为是 PCB 内的成员,所以也可以在 task_struct 结构体内找到:
c
//文件地址:linux-2.6.32.19\include\linux\sched.h
struct task_struct {
//省略
int prio, static_prio, normal_prio;
unsigned int rt_priority;
struct sched_entity se;
struct sched_rt_entity rt;
//省略
};
其中的整型变量 prio (priority)就是进程的优先级变量。它的数值范围在 [60,99] 。默认进程的优先级都是 80 ,所以进程在创建时的优先级统一都是 80,后面根据需要进行优先级的更改。
ps -l查看系统进程
在 Linux 或者 Unix 系统中,用 ps --l 命令则会类似输出以下几个内容:
-
F:即 flag,进程相关的标志。这是一个数字,通常与进程的权限和状态相关,在现代 Linux 很少使用,通常为 0 。
-
S:即 state,运行状态。
-
UID:代表执行者的身份。
-
PID:代表这个进程的代号。
-
PPID:即父进程的代号。
-
C:CPU的利用率百分比(CPU Utilization)。
-
ADDR:进程在内存中的地址。
-
SZ:进程占用的物理内存大小,单位通常是页(Page)。一页通常为 4KB 。
-
WCHAN:进程休眠时所在的内核函数名称(Waiting Channel)。
-
TTY:进程所关联的终端。
-
TIME:进程实际使用 CPU 的累计时间,格式为
分:秒。 -
CMD:启动进程所用的命令名称。
和优先级有关的:
- PRI:实际优先级 ,代表这个进程可被执行的优先级,其值越小越早被执行。
- NI:优先级调整因子,代表这个进程的 nice 值,也有资料叫谦让度。
交互界面:
bash
[Bjarne@VM-8-8-centos cTest]$ ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1001 23263 23262 0 80 0 - 29280 do_wai pts/0 00:00:00 bash
0 R 1001 23581 23263 0 80 0 - 38332 - pts/0 00:00:00 ps
[Bjarne@VM-8-8-centos cTest]$
PRI (priority)表示进程的优先级,或者通俗点说就是程序被 CPU 执行的先后顺序,此值越小进程的优先级别越高。
NI (nice)表示进程可被执行的优先级的修正数值。PRI 值越小越快被执行。
那么加入 nice 值后,将会使得 PRI 变为: PRI(new)=PRI(old)+nice \text{PRI(new)=PRI(old)+nice} PRI(new)=PRI(old)+nice 。即最终进程的优先级是由 prio 和 nice 值共同决定。
为什么要这样设定?若让用户随意调整优先级,那么用户就可以通过更改优先级的方式,使得自己的进程优先被调动。
用户把自己的进程的优先级调高,这样的高优先级进程数量一旦变多,可能会导致有有很多常规的进程,甚至优先级较低的进程,长时间得不到 cpu 的调度。这个叫做进程饥饿问题。简单理解都是,和用户有关的进程都插队,使得调度不公平。
操作系统在进行进程调度的时候,一定要均衡地让每一个进程在一个时间段内都或多或少都能够享受到 cpu 资源。这在操作系统的课本中称这种操作系统叫分时系统。
Linux 下测试调整优先级
在 Linux 中的进程,Linux 虽然支持优先级动态调整,但是它不允许用户直接去改这个 prio 这个变量。用户要改优先级是通过修改 nice 值来达到修改优先级的目的。
当进程的 nice 值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行。
所以,调整进程优先级 ,在 Linux 下就是调整进程 nice 值。nice 其取值范围是 [-20,19],一共40个级别。正好对应 PRI 的取值范围 [60,99] 和默认值 80 。
为什么这 2 个变量是这个范围,和操作系统的调度算法有关。详细后面再补充。
而且每个用户除了 root,不能更改其他人的进程的优先级,只能改变自己的。
进程的 nice 值不是进程的优先级,他们不是一个概念,但是进程 nice 值会影响到进程的优先级变化。
可以理解为 nice 值是进程优先级的修正修正数据。
这里给一个测试源码:
c
#include<stdio.h>
#include<unistd.h>
int main(){
while(1){
printf("abcd%d",getpid());
sleep(1);
}
}
然后在另一个窗口输入 top 打开 Linux 的任务管理器 。

输入 r,相当于是 renice ,就是重新调整 nice 值的意思。然后 top 会提示 PID to renice [default pid = 357]。这里输入想更改优先级的进程。

然后又会提示 Renice PID pid to value,问想将这个进程的优先级改成多少。这里先输入一个 10 ,表示 nice 值为 10 ,这时进程的优先级就是 90。

然后再按 Ctrl + c 退出,使用 ps -al|head -1&&ps -al|grep a.exe 查看进程的优先级信息。可以看到 PRI 一栏变成了 90 。
bash
[Bjarne@VM-8-8-centos cTest]$ ps -al|head -1&&ps -al|grep a.exe
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1001 32662 31792 0 90 10 - 1054 hrtime pts/0 00:00:00 a.exe
[Bjarne@VM-8-8-centos cTest]$
普通用户可以将优先级调低(prio 变量存储的数据增加),但不能调高。root 权限则无所谓。
要调整优先级,每次都给用户一个确定的起点即 80 这个进程的默认优先级,每次想调整优先级的时候,之前是什么值不重要,只需要知道想把它修整成多少。
例如这里使用 sudo top 打开任务管理器(也可以先切换成 root 账户)。

按照同样的方法将进程优先级的 nice 值改为 -10,再通过 ps 指令可查到进程优先级变成了 70。
bash
[Bjarne@VM-8-8-centos cTest]$ ps -al|head -1&&ps -al|grep a.exe
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1001 32662 31792 0 70 -10 - 1054 hrtime pts/0 00:00:00 a.exe
[Bjarne@VM-8-8-centos cTest]$
若不按照 nice 的取值范围 [-20,19] 去修改,例如 nice 值为 200(数值不合理,可能需要 root 权限)。再查看进程的优先级变化:
bash
[Bjarne@VM-8-8-centos cTest]$ ps -al|head -1&&ps -al|grep a.exe
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1001 32662 31792 0 99 19 - 1054 hrtime pts/0 00:00:00 a.exe
[Bjarne@VM-8-8-centos cTest]$
最多只能变成 99 。nice 的值特别小例如 -233 也是同理。
c
[Bjarne@VM-8-8-centos cTest]$ ps -al|head -1&&ps -al|grep a.exe
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1001 32662 31792 0 60 -20 - 1054 hrtime pts/0 00:00:00 a.exe
[Bjarne@VM-8-8-centos cTest]$
进程优先级替换当然不止这种方式,这里介绍最简单的一种。
进程优先级有关的系统调用
首先是 getpriority 和 setpriority。输入 man getpriority 或 man setpriority 可查看该系统调用。
c
//该系统调用存在的头文件
#include <sys/time.h>
#include <sys/resource.h>
//系统调用接口原型
int getpriority(int which, int who);
int setpriority(int which, int who, int prio);
根据 man 手册的描述,getpriority() 用于获取进程 、进程组或用户的调度优先级 ,setpriority() 用于设置该优先级。
参数 which 取值为 PRIO_PROCESS、PRIO_PGRP 或 PRIO_USER 之一。
参数 who 将根据 which 的值被解释,
- 对
PRIO_PROCESS是进程标识符; - 对
PRIO_PGRP是进程组标识符; - 对
PRIO_USER是用户 ID。
who 值为 0 时分别表示调用进程自身、调用进程所属的进程组或调用进程的实际用户 ID。
prio 是一个范围在 -20 到 19 之间的值。默认优先级为 0。这个实参可以理解为上传的是 nice 的值。
getpriority() 会返回指定进程中享有的最高优先级(即最小的数值)。setpriority() 调用则将所有指定进程的优先级设置为指定值。
getpriority() 可能返回 -1,因此有必要在调用前将外部变量 errno 清零,然后在调用后检查它,以确定返回的 -1 是错误还是合法值。setpriority() 调用在成功时返回 0,出错时返回 -1。
例如,给一个简单的程序,返回自身的 nice 值。
c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/resource.h>
#include<errno.h>
int main(){
errno=0;//清除errno
int a=getpriority(PRIO_PROCESS,0);
if(!errno){
perror("getpriority failed");
exit(1);
}
printf("nice value:%dn",a); //初始时nice 默认为0。
return 0;
}
该程序输出0,就不演示交互界面了。
这里通过 setpriority 修改进程本身的 nice 值。
c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/resource.h>
#include<errno.h>
int main(){
errno=0;
if(setpriority(PRIO_PROCESS,0,10)==-1){
perror("setpriority failed");
exit(1);
}
int a=getpriority(PRIO_PROCESS,0);
if(errno){
perror("getpriority failed");
exit(1);
}
printf("nice value:%d\n",a);
return 0;
}
交互界面:
c
[Bjarne@VM-8-8-centos cTest]$ gcc a.c -o a.exe
[Bjarne@VM-8-8-centos cTest]$ ./a.exe
nice value:10
[Bjarne@VM-8-8-centos cTest]$
Linux进程优先级相关命令
top指令
top 是 Linux 的实时系统监控工具,相当于 Windows 中的任务管理器。
输入 top 即可打开任务管理器。在任务管理器中输入 q 即可退出。
top 界面:

对每一行信息进行描述(建议将 Linux 窗口放在一旁再阅读):
第1行:系统概览
top - 19:42:22:当前时间。up 64 days:系统已运行时间。这里因为是云服务器,所以显示 64 天。1 user:当前登录用户数。这里只有普通用户 Bjarne 。load average: 0.75, 0.45, 0.20:系统平均负载(1分钟、5分钟、15分钟的平均值)。这个值除以 CPU 核心数,如果大于 1 则表示负载过高。
第2行:任务(进程)
total:总进程数。running:正在运行的进程数。若显示 1 的话,可能指top指令。sleeping:睡眠中的进程数。stopped:停止的进程数。zombie:僵尸进程数。这里应重点关注,非 0 表示有进程处于僵尸状态。
第3行:CPU 使用率
us:用户空间进程占用 CPU 百分比。sy:内核空间进程占用 CPU 百分比。ni:被调整过优先级的进程占用 CPU 百分比。id:空闲 CPU 百分比。wa:等待 I/O 操作的 CPU 百分比,如果过高,可能磁盘有瓶颈。hi:处理硬件中断的 CPU 百分比。si:处理软件中断的 CPU 百分比。st:被虚拟机偷走的时间百分比。
第4行:物理内存(Mem)
total:总内存。free:空闲内存。used:已使用内存。buff/cache:用作内核缓冲区、页面缓存和 slab 的内存。
第5行:交换分区(Swap)
total:总交换分区大小。free:空闲交换分区大小。used:已使用的交换分区大小。如果这个值不为 0 且持续增长,说明物理内存不足。
然后就是进程列表:
PID:进程 ID 。USER:进程所有者。PR:进程优先级。NI:Nice 值,负值表示高优先级。VIRT:进程使用的虚拟内存总量。RES:进程使用的、未被换出的物理内存大小(单位:KB)。即进程的实际占用内存。SHR:共享内存大小。S:进程状态。%CPU:进程占用的 CPU 百分比。%MEM:进程占用的物理内存百分比。TIME+:进程使用的总 CPU 时间。COMMAND:启动进程的命令行。
top 更像是任务管理器,所以支持和用户通过命令进行交互。
基础:
h或?:显示帮助界面。q:退出top。- space 或 Enter:立即刷新显示。
排序相关:
P(大写):默认排序,按 CPU 使用率降序排列。M(大写):按内存使用率(%MEM) 降序排列。N(大写):按 PID 降序排列。T(大写):按 TIME+(CPU 时间)降序排列。R:反转排序顺序。
进程操作:
k:终止一个进程。输入k后,会提示输入要终止的进程的 PID,然后还会提示你发送什么信号(默认是 15,SIGTERM,正常终止;强制杀死用 9,SIGKILL)。类似kill指令。r:调整一个进程的优先级(Nice值)。输入r后,会提示输入 PID,然后输入新的 nice值。在上文已体验过。
显示相关:
1(数字一):展开/折叠显示所有 CPU 核心的单独使用情况。l(小写L):切换显示/隐藏第一行的平均负载信息。t:切换显示/隐藏第2行(任务)和第3行(CPU)的图形化显示模式。m:切换显示/隐藏第4行(内存)和第5行(Swap)的图形化显示模式。u:然后输入用户名,只显示属于该用户的进程。f:进入字段管理界面,可以自定义显示哪些列,以及排序的列。用上下键选择,按d或空格键切换显示/隐藏,按s键设置排序列,然后按q返回主界面。Z:改变颜色配置。
ps -l
输入 ps -al 会显示和进程有关的信息,其中就有进程的优先级信息 PRI 和 NI。
bash
[Bjarne@VM-8-8-centos cTest]$ ps -al
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1001 22068 6992 0 80 0 - 27092 do_wai pts/0 00:00:00 make
0 S 1001 22069 22068 0 80 0 - 1054 hrtime pts/0 00:00:00 a.exe
0 R 1001 22232 22100 0 80 0 - 38332 - pts/1 00:00:00 ps
[Bjarne@VM-8-8-centos cTest]$
nice和renice
nice 既是指令,也是进程的信息之一。nice 作为指令用于在启动进程时设置优先级相关的 nice 值,例如nice -n <优先级> <命令>。
bash
# 界面1:
[Bjarne@VM-8-8-centos cTest]$ nice -n 13 ./a.exe # nice值设置为13
asdfg
# 省略
# 界面2:
[Bjarne@VM-8-8-centos cTest]$ ps -al|grep a.exe
0 S 1001 23409 6992 0 93 13 - 1054 hrtime pts/0 00:00:00 a.exe
[Bjarne@VM-8-8-centos cTest]$
renice 用于修改已运行进程的优先级。格式renice <优先级> -p <PID>。
bash
[Bjarne@VM-8-8-centos cTest]$ ps -al|head -1&&ps -al|grep a.exe
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1001 25039 6992 0 94 14 - 1054 hrtime pts/0 00:00:00 a.exe
[Bjarne@VM-8-8-centos cTest]$ renice 16 -p 25039
25039 (process ID) old priority 14, new priority 16
[Bjarne@VM-8-8-centos cTest]$ ps -al|head -1&&ps -al|grep a.exe
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1001 25039 6992 0 96 16 - 1054 hrtime pts/0 00:00:00 a.exe
[Bjarne@VM-8-8-centos cTest]$
nice 和 renice 修改进程优先级信息同样遵循之前的规律,例如普通用户不可调小优先级,nice 值的设置范围等。