上一章我们把僵尸进程和孤儿进程的底裤扒了个干净。今天我们要正面硬刚 Linux 内核里另一个至关重要的前置概念——进程优先级Priority。我们会从它存在的底层逻辑讲起一路往下挖Linux 下怎么查看优先级、核心计算公式长什么样、Nice值的映射有什么限制最后再动手用系统调用改一把彻底看清操作系统调度的本质。好了不废话直接开始。目录一、CPU竞争与时间片机制——理解进程调度的基础1.1 竞争、独立、并发与并行1.1.1 四个概念的区别与联系1.1.2 生活中的例子——一起喝咖啡理解并发1.2 时间片——分时系统如何调度进程1.2.1 什么是时间片1.2.2 时间片轮转机制的作用二、进程优先级——为什么有的进程更“重要”2.1 为什么需要进程优先级2.1.1 资源稀缺与调度竞争2.1.2 进程优先级与权限的区别2.1.2.1 UIDUser ID与用户身份2.1.3 进程优先级的实际作用2.2 Linux中如何查看进程优先级2.2.1 使用ps -l查看进程信息2.2.2 核心指标——PRI与NI2.3 Linux进程优先级的计算与关系2.3.1 PRI与NI的基本关系2.3.2 Nice值如何影响进程优先级2.4 Nice值的取值范围与限制2.4.1 Nice值的取值范围2.4.2 为什么需要限制Nice值的调整范围2.5 进程优先级的修改方法2.5.1 修改进程优先级的基本特点2.5.2 修改优先级的常用方式2.5.2.1 使用命令行工具调整优先级2.5.2.2 通过系统调用修改优先级2.5.2.3 通过top交互式调整优先级2.5.3 优先级修改中的权限与安全限制一、CPU竞争与时间片机制——理解进程调度的基础1.1 竞争、独立、并发与并行1.1.1 四个概念的区别与联系先别急着扎进优先级里四个基础概念得先摆正不然后面越学越晕。竞争性系统里进程一抓一大把CPU却少得可怜可能只有一颗。这么多进程抢这么点资源竞争自然就来了。为了让大家别挤成一团能更合理地分配CPU时间优先级就应运而生它决定了谁先上桌吃饭谁先在旁边等着。独立性多个进程同时跑各自有自己的一亩三分地内存空间、文件描述符、寄存器状态互不插手。你崩你的我跑我的谁也别想干扰谁。这是操作系统必须守住的基本盘。并行如果你有多个CPU核心进程们就能真正地“同时”跑一个核跑A另一个核跑B各干各的谁也不等谁。这才叫货真价实的并行。并发可如果只有一个CPU呢那就只能靠快速切换来制造“同时”的错觉。CPU一会儿跑A一会儿跑B切得飞快让每个进程都在一段时间内有所推进。虽然任何瞬间只有一个进程在跑但宏观上看大家都动起来了。这就是并发。这四个概念日常里经常被混着说。但现在你应该清楚了并行是物理上的同时并发是逻辑上的同时。把这条线划清后面理解优先级调度会顺畅很多。1.1.2 生活中的例子——一起喝咖啡理解并发并发Concurrent想象一家小咖啡店店里只有一台咖啡机单核 CPU柜台前却排了两条长队。咖啡师只有一个人怎么办他只能先给A队做一杯立刻转身再给B队做一杯如此来回倒腾靠极快的切换速度让两条队伍都感觉在慢慢往前挪。虽然每一个瞬间他只在服务一个人但拉长到一段时间看两队都在前进没有人被晾在原地。这就是并发不是同时而是“忙得让你以为在同时”。并行Parallel还是这家咖啡店但老板一咬牙升级了装备直接摆上两台一模一样的咖啡机多核CPU再请来一位咖啡师。现在两位咖啡师各守一台机器同一时刻你在这台机器做你的我在那台机器做我的两条队伍在各自的物理设备上独立推进互不干扰。这才是货真价实的“同时”不是来回切换装出来的假象而是两杯咖啡真的在同一秒里各自冒着热气。想知道你的机器到底是单核还是多核一条命令就能查个底朝天。cat etc/cpuinfo1.2 时间片——分时系统如何调度进程早期的计算机可没现在这么“博爱”。单任务批处理系统当道时一个程序不跑完后面的程序只能排着队干等像一间只开一个窗口的办事大厅。而现代Linux早就不吃这一套了它是典型的分时操作系统讲究的是雨露均沾。1.2.1 什么是时间片内核为了让一堆进程“看起来”在同时运行掏出了看家本领给运行队列里的每个进程都发一张极短暂的CPU体验券。这张体验券就是时间片。它的长度通常只有几毫秒到几十毫秒短到人类根本来不及察觉。1.2.2 时间片轮转机制的作用靠这套时间片轮转机制Linux 的多任务运行呈现出一种非常有意思的景观微观串行把镜头拉到极短的一个时间片内CPU核心依然是铁面无私的串行执行者它只专心跑一个进程的指令心无旁骛。宏观并行当一个进程的时间片烧完操作系统会毫不留情地没收它的CPU使用权把它塞回队列末尾重新排队转头就把CPU交给下一个进程。这就是所谓的“进程切换”。因为时间片短得可怜切换又频繁得要命在人类的肉眼看来所有程序就像真的在同时流畅运行一样。这种“微观上独奏、宏观上合奏”的假象正是分时操作系统最精妙的障眼法。二、进程优先级——为什么有的进程更“重要”在多任务操作系统里CPU是稀缺资源等着要跑的进程却往往挤得头破血流。僧多粥少怎么分总不能靠猜拳决定谁先上。操作系统必须有一套硬性机制明确地排出一个先后顺序谁先跑谁后跑谁多跑一会儿谁先靠边站。这套机制就是进程优先级。优先级的存在本质上是给每个进程贴了一张“重要性标签”。系统会根据这些标签把有限的 CPU 时间切得明明白白重要的进程优先拿时间片次要的进程先晾在队列里。没有这套规则整个系统就会退化成一场无序的资源争夺战谁都跑不痛快。2.1 为什么需要进程优先级2.1.1 资源稀缺与调度竞争优先级这个东西说到底是让CPU这个稀缺资源分出个先来后到。一个CPU核心同一瞬间只能伺候一个进程这是物理铁律谁都改不了。可系统里同时待在R状态、眼巴巴等CPU的进程往往一大把。怎么办排队。谁先谁后总得有个规矩。优先级就是这套规矩的核心它决定了在运行队列这条长队里谁能站到前面去谁能先摸到CPU的那张时间片。说白了优先级就是操作系统给进程排的“加塞资格”。资格高的插队到前排资格低的老老实实在后面候着。没有这个机制所有进程一拥而上系统瞬间就成了一锅粥。2.1.2 进程优先级与权限的区别很多初学者会把“优先级”和“权限”这两个概念搅在一起其实它们在操作系统里完全是两码事分工明确得很权限回答的是“你能不能碰”的问题。它是一道门槛挡在门口查你的资格。比如普通用户能不能读root的文件这就是权限在管。没权限门都进不去后面的事免谈。优先级回答的是“你什么时候轮到”的问题。它默认你已经跨过了权限那道门已经有资格做事了现在只是排个先后。优先级不关心你够不够格它只管在合格的人里让谁先上谁后上。一句话权限是入场券优先级是排队号。有券没号你能进场但得等着有号没券门卫根本不放你进去。先有权限再谈优先级这个顺序别搞反了。2.1.2.1 UIDUser ID与用户身份在 Linux 内核眼里用来识别“你是谁”的硬核凭据从来不是root、alice这种字符串用户名而是一串干巴巴的数字UIDUser ID用户标识符。内核对身份的数字化绑定非常直接每当你在终端启动一个程序内核在给这个程序创建进程控制块task_struct的时候会顺手把你的UID写进去。也就是说系统里跑着的每一个进程底层都刻着一个数字那个数字就是它主人的身份证号。UID 的划分规则很简单0超级用户root的专属编号永远是0。最高权力没有之一。1000 起现代 Linux 发行版里普通用户的UID一般从1000开始往后排1000、1001、1002……一路加下去。怎么查看一个进程的UID用ps -l或者ps -al命令输出结果里就有一列叫UID对应的数字就代表这个进程是谁家的。这里顺带提一下-n选项。平时我们敲ls -l文件所有者那栏显示的是用户名但这个用户名其实是被系统“包装”过的。如果你在ls后面加上-n比如ls -ln它就会把用户名和组名直接换成数字UID 和GID显示出来。包装一撕底层的数字身份就露出来了。那为什么讲优先级要突然插进UID这个概念别急这条线其实是通的。你想访问一个文件是什么东西去访问是进程在访问。那进程怎么知道自己有没有资格动这个文件它得看权限。而权限又是从哪里来的从创建这个进程的用户身上来而用户在内核里的唯一标识就是UID。所以整条链是进程 → UID → 权限 → 能不能访问。想明白这一层你就知道为什么内核在创建task_struct时非得把UID塞进去了——因为它要替每个进程记清楚你背后站着谁你说话的分量有多重。权限是门槛UID是通行证上的编号优先级则是你进了门之后的排队号。三样东西各司其职串起来就是操作系统身份管理的完整图景。2.1.3 进程优先级的实际作用优先级这套机制不是摆设用好了能实打实提升系统的整体表现。具体好处至少有两条改善系统性能让那些关键的系统核心服务优先跑起来。比如硬件中断的处理、音视频流的实时解码这些活儿一刻都不能拖。给它们高优先级就能避免关键任务被普通进程挤到后面防止卡顿和延迟。提升用户体验你正在拖动的窗口、正在敲击的键盘、正在移动的鼠标这些交互式进程天生就该被温柔以待。给它们更高的优先级系统响应就会更跟手、更流畅。没人愿意点个按钮等半秒那半秒往往是调度器没把交互进程当回事。在Linux的O(1)调度算法里优先级和时间片是直接挂钩的。一个简单粗暴的规则优先级越高的进程分到的时间片越长、越充裕优先级越低的分到的时间片越短。这背后的逻辑很直白调度器信任高优先级的进程愿意把更多的CPU时间押在它身上低优先级的进程则要自觉一些拿到的时间只够浅尝辄止。优先级不只是“谁先谁后”的排队号它更直接决定了每个进程能占用CPU的总时长。高优先级等于在食堂打饭既排前头又拿大勺。2.2 Linux中如何查看进程优先级2.2.1 使用ps -l查看进程信息想知道一个进程的优先级最直接的办法就是在终端里敲一句ps -l如果你想把别的终端和后台进程也一并揪出来看那就得写成ps -al那个a别丢了它管的是“所有用户和终端”。命令敲下去输出会是类似下面这样一长串字段F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 0 S 1000 3214 3200 0 80 0 - 29115 do_wai pts/0 0:00:00 bash 0 R 1000 3521 3214 0 80 0 - 38312 - pts/0 0:00:00 ps别被这一排英文吓住我们今天只盯两列PRI和NI。这俩就是进程优先级的核心数据后面会围着它们反复展开。2.2.2 核心指标——PRI与NI在这一排眼花缭乱的字段里真正决定进程调度次序的其实就是两列PRI和NI。别的先放一边盯紧这俩就够。PRIPriority进程优先级这是进程最终被CPU执行时的“实锤优先级”。数值越小优先级越高越容易被调度器点名上台。这一点跟我们的直觉完全相反从小到大排惯了队突然告诉你“数字小的先走”初看确实别扭。记住一条就行在PRI的世界里数字小才是大爷。NINice谦让值这是我们唯一能动手调整的修正参数。它刻画的是进程的“谦让程度”。Nice值越小负数这进程就越不客气抢起CPU来毫不手软直接把PRI往下拽让自己更靠前Nice值越大正数它就越绅士愿意把资源往后让一让PRI自然被抬高自己乖乖排到后面去。这套设计有个很妙的地方我们改优先级的时候根本不用关心这个进程当前PRI是多少。只需要调Nice值内核会自动把PRI算出来。相当于你只控制“它有多谦虚”剩下的排名系统帮你搞定。这比直接盯着一个动态变化的数字改来改去省心太多了。2.3 Linux进程优先级的计算与关系2.3.1 PRI与NI的基本关系在Linux里一个进程的最终优先级PRI从来不是一个焊死的数字。它会随着Nice值的调整而上下浮动是个动态计算出来的结果。核心公式简单到只有一行最终PRI 基准PRI NINice值那这个基准PRI到底是多少在标准Linux内核中普通进程的默认基准值通常固定为80。这个80就像一条画好的起跑线。所有普通进程一出生都站在这条线上接下来是往前冲还是往后让全看Nice值怎么调。Nice是负数就相当于起跑线往前挪了几步Nice是正数就自觉往后退几格。最终站在哪个位置就是进程的最终PRI。2.3.2 Nice值如何影响进程优先级套进公式里一算Nice 值怎么摆布优先级一眼就透默认状态NI 0最终PRI 80 0 80。不争不抢原地待命。不想谦让NI -10最终PRI 80 (-10) 70。数字小下去了优先级反而蹿上来了。进程瞬间从“老实人”变身“霸道总裁”。极度谦让NI 15最终PRI 80 15 95。数字大起来了优先级却跌下去了。进程主动退到队尾把CPU让给别人先跑。所以核心口诀就两句Nice值为负进程变霸道PRI变小优先级变高。Nice值为正进程变谦让PRI变大优先级变低。2.4 Nice值的取值范围与限制2.4.1 Nice值的取值范围你可能会想既然Nice值能调那我是不是可以把它压到负无穷让我的进程一个人霸占整颗CPU想得美。Linux内核早就在这上面上了一道锁而且锁得死死的。Nice值的合法活动范围只有窄窄的一段Nice ∈ [-20, 19]满打满算一共就40个级别。多一个没有少一个不行。这意味着什么哪怕你把命令敲出火星子系统允许的最低Nice值也就到-20为止。此时普通进程能摸到的最高优先级是80 (-20) 60。反过来最“谦让”的Nice值19对应的是最低优先级80 19 99。从60到99普通进程的PRI就被焊死在这个区间里谁也越不了界。为什么要卡得这么死理由很现实防止某个进程通过疯狂调低Nice值把自己变成系统里的“吸血鬼”。要是Nice值可以无限往下调一个恶意或失控的进程就能长期霸占CPU其他所有进程都得给它让路系统迟早被拖到假死甚至崩溃。40个级别是一道安全阀既给了你足够的调整空间又彻底封死了“一家独大”的后路。所以记住这个区间[-20, 19]。想霸道最多霸道到-20想谦让最多谦让到19。再往前一步内核就不答应了。2.4.2 为什么需要限制Nice值的调整范围Linux把Nice值锁死在[-20, 19]根本目的就一个防止“进程饥饿”Process Starvation。操作系统必须守住一条底线公平调度。设想一下如果Nice值可以无底线地往下调某些恶意进程或者失控的高优先级进程就会像食堂里拿着特权卡的插队狂魔死死霸住CPU窗口让后面所有普通后台进程、系统维护进程永远分不到哪怕一个时间片。时间片这个概念我们后面会专门讲现在你只需要知道那些分不到CPU的进程只能蹲在队列里干等等得越久越没希望最后活活“饿死”在运行队列里。这不是夸张这就是进程饥饿的真实下场。2.5 进程优先级的修改方法2.5.1 修改进程优先级的基本特点优先级不是焊死的它可以调。而且两个时间点都能动手进程启动时先定个基调或者进程跑起来之后再动态修正。灵活得很。2.5.2 修改优先级的常用方式2.5.2.1 使用命令行工具调整优先级Linux提供了两个趁手的命令一个管“出生设定”一个管“半路改命”。nice命令在进程启动时给它指定一个初始Nice值。相当于在孩子出生前就定好它的性格是霸道还是谦让。renice命令在进程运行期间动态调整它的Nice值。进程已经跑起来了发现它太闹腾或者太老实随时用 renice 给它改改脾气。nice示例以较低优先级Nice值为10启动一个耗时的编译任务让它在后台慢慢磨别跟其他交互任务抢资源。nice -n 10 g main.cpp -o main小提示如果不写-n参数nice会默认把Nice值设成10。所以nice g main.cpp -o main和上面那行效果一样都是让这个编译任务礼貌让行。renice示例把一个正在运行的、PID为1234的进程Nice值调整到5让它稍微谦让一点。renice -n 5 -p 1234提高优先级需要root权限把PID为1234的进程Nice值压到-5让它的优先级更高跑得更靠前。sudo renice -n -5 -p 1234这里有个权限上的讲究调低Nice值提高优先级需要root权限因为这意味着你要从系统手里多抢资源普通用户没这个资格而调高Nice值降低优先级则相对宽松毕竟你是在主动谦让系统乐见其成。所以想让进程变霸道先掂量掂量自己是不是root。2.5.2.2 通过系统调用修改优先级除了命令行工具Linux也提供了编程接口让程序自己动手查询或修改优先级。需要引入两个头文件#include sys/time.h #include sys/resource.h int getpriority(int which, int who); int setpriority(int which, int who, int prio);这两个函数是一对搭档一个负责查一个负责改。which指定要操作的对象类型who是具体的 IDprio就是要设置的Nice值。跟命令行工具背后干的活儿完全一样只不过这次是让你的代码亲自下场操作。2.5.2.3 通过top交互式调整优先级如果你嫌敲命令麻烦还可以直接在top这个性能监视器里点点键盘就搞定。进入控制台终端里敲top启动实时性能面板。触发修改机制在top界面下按一下键盘上的r键进入renice调整模式。选择目标进程此时界面会弹出一行提示PID to renice [default pid 24300]。方括号里那个PID是当前默认选中的进程如果你要改的就是它直接回车如果想换一个手动输入目标PID比如25110再回车。修改 Nice 值紧接着界面会问Renice PID 25110 to value在这里填进你期望的新Nice值合法范围-20到19回车确认修改立刻生效。2.5.3 优先级修改中的权限与安全限制这里有个细节要留意出于安全考虑操作系统不会任由你频繁地改优先级尤其对非 root 用户卡得比较紧。普通用户想把Nice值调低也就是提高优先级随时可能被内核拦下来。背后的逻辑很直接提高优先级等于从系统手里多抢CPU资源这种操作如果人人随时随地都能干那来个贪心的进程天天把自己调到最高其他人还活不活了所以想越权压低Nice值只有两条路可走要么切到root用户要么在命令前面加sudo借一下管理员权限。有了root加持这个限制才会对你放开。如果这篇文章对你有帮助欢迎点赞、收藏、关注三连支持你的反馈是我继续肝下一篇的最大动力。我们下篇见。