
Linux系统里最不缺的就是进程——你用鼠标点一下桌面图标是进程在终端敲一条命令也秒变进程甚至你此刻看到的这篇文字背后也有一堆浏览器渲染进程在跑。RH124这本书把“监控和管理Linux进程”放在第8章位置很讲究前面讲完文件、用户、权限这些静态资源从这一章开始系统才真正“活”起来。作为RHCSA认证的必考点这一章的内容也是实际运维里用得最勤的操作进程怎么查、怎么判断谁吃满CPU、怎么优雅地让某个失控的程序退场都属于基础生存技能。这一章能解决的实际问题很直接服务器突然变卡你要在两分钟内揪出元凶写了个脚本跑起来就停不下来你要知道怎么安全结束它部署的服务莫名其妙没响应你需要判断进程是在正常运行还是在挂死。如果你是在准备RH124考试或者刚转到Linux运维方向这章值得反复吃透。我当年学这章的时候觉得内容琐碎后来真到了生产环境才发现进程管理是所有排障动作的起点没有之一。2. 进程基础概念监控之前先建立几个底层认知2.1 进程、线程与程序别把三者混为一谈很多初学者一上来就死记命令但命令只是末端底层概念不清楚排障时很容易抓瞎。先区分三个词程序、进程、线程。程序是静态的是躺在磁盘上的那堆二进制文件比如/usr/bin/nginx文件不动它就是一个普通文件。进程是程序运行起来后的动态实例内核会给它分配PID、内存、文件描述符等资源。打个比方程序是菜谱进程是按照菜谱在灶台上翻炒的那口锅里的菜菜谱可以复印无数份但灶台上一份菜对应一个炒菜过程这就是进程。同一个程序可以启动多个进程典型的是Nginx一个master进程加多个worker进程。线程则是进程内部的执行单位。一个进程可以包含多个线程共享进程的地址空间和资源独立调度执行。这是热词里“线程与进程”的区别核心进程是资源分配的最小单位线程是CPU调度的最小单位。在Linux上线程本质上是轻量级进程LWP用ps -eLf能看到一个进程下的多个线程。Java进程是典型的“多线程大户”一个JVM进程内部往往有几十个线程在跑GC、执行业务、做网络IO。理解这一点排Java服务高CPU问题时才有方向——top看到的PID是进程要用top -H才能定位到具体线程。另外提一嘴“进程池”这个词它在实际系统中很常见。PHP-FPM、Apache、Java线程池都是预先创建一批进程/线程等待任务避免频繁创建销毁的开销。你通过ps -ef | grep php-fpm会看到一堆php-fpm进程那就是进程池在工作的样子。还有进程间通信IPC这也是热词里高频出现的内容。进程之间默认是隔离的一个进程不能直接访问另一个进程的内存要协作就得走IPC常见方式包括管道pipe、消息队列、信号、共享内存、套接字等。RH124第8章不展开讲IPC的API但要明白kill发信号本身就是一种IPC方式是内核帮你做进程间通知的手段。2.2 进程状态读懂STAT那一列的字母密码ps aux输出里有一列 STAT里面全是一个个字母比如 S、R、D、T、Z后面偶尔跟着 、、N、s、l。这些字母不是随机的每个都有明确含义看懂它们你就能初步判断进程是不是“卡住”了。RRunning/Runnable进程正在CPU上运行或者在运行队列里等待被调度。注意R不代表“正在消耗CPU”有时候它只是处于可运行状态是排队状态但长期处于 R 且占用CPU高基本可以断定它在忙。SSleeping可中断睡眠进程在等待某个事件比如等待IO完成、等待网络数据这是大多数后台服务进程的常态。只要不死S是正常状态。DUninterruptible Sleep不可中断睡眠通常是在等待硬件IO比如磁盘读写。这种状态是内核层面的等待连kill -9都不一定能立刻杀掉因为它暂时无法处理信号。等待IO卡死时会出现一堆D状态进程这是系统级的头疼问题往往要等IO恢复或重启才能解决。TStopped/Traced进程被停止通常是被CtrlZ暂停或者被调试器如gdb挂起。ZZombie僵尸进程子进程已经退出但父进程还没有读取它的退出状态这个“尸体”就留在进程表里占个位置。IIdle不可中断睡眠的内核线程常见于内核线程不需要过度关注。另外还有一些附加符号表示高优先级N表示低优先级nice值为正s表示会话领导者session leaderl表示多线程进程表示在前台进程组中运行。我工作中最常用这个判断场景某个服务进程状态是 D 且长时间不变化大概率是磁盘或存储卡了如果是 Z那就是父进程写代码偷懒没回收如果是 T多半是有人手动挂起了它。看到这些状态第一反应不是胡乱kill而是先想清楚“它为什么会变成这个状态”。2.3 父子关系、孤儿进程与僵尸进程进程不是凭空存在的除了开机后内核启动的第一个进程通常是systemdPID为1其他进程都由父进程创建。创建进程的系统调用是fork子进程是父进程的一份拷贝如果想运行一个全新的程序再用exec族函数替换子进程的代码。命令行里每次敲一个命令你的shell比如bash就是父进程它fork出一个子进程去执行命令。用ps -ef能看到每个进程的PPID父进程PIDpstree则能直观展示整棵进程树。排障时这个很重要你想确认某个服务的进程归属往前推父进程是谁能帮你快速判断是不是被别的程序拉起来还是有人手动启动的。孤儿进程是指父进程先退出子进程还没退出此时这个子进程会被PID为1的systemd进程收养变成systemd的直接子进程。这在守护进程的设计里很常见一个进程想脱离终端的控制就让中间层退出自己被init收养这样终端关闭也不影响它运行。僵尸进程则是相反的情况子进程先退出父进程还活着但父进程没有调用wait系统调用去回收子进程的退出状态于是这个子进程的退出信息残留在内核进程表里。僵尸进程几乎不消耗CPU和内存但它占用一个PID槽位。如果僵尸进程越积越多最后可能导致系统无法创建新进程因为PID和进程表条目有限。处理僵尸进程的正道是杀掉父进程让父进程的退出导致这些僵尸被系统重新收养并清理粗暴地kill僵尸进程本身是没用的因为它已经死了无法响应信号。3. 进程查看实战用ps和top把系统看穿3.1 ps命令日常排障最常用的快照工具ps是Process Status的缩写它打印的是某一瞬间的进程快照。参数比较绕老系统管用的有两种风格Unix风格用单个横杠比如ps -efBSD风格没有横杠比如ps aux。实际工作中两者都能用重点是要会读输出。ps aux是最常用的ps aux | head -n 20输出里的列分别是USER进程属于哪个用户、PID、%CPU、%MEM、VSZ虚拟内存大小、RSS常驻物理内存大小、TTY、STAT、START、TIME、COMMAND。排查时我第一眼看%CPU第二眼看STAT第三眼看COMMAND。ps -ef则更适合看进程树关系因为输出里有PID和PPID两列配合ps -ef --forest可以画出进程的树形结构。另外再介绍几个实用组合# 查看线程信息排Java等多线程程序时用 ps -eLf | grep java # 按CPU使用率排序显示前10个进程 ps aux --sort-%cpu | head -n 11 # 按内存使用率排序显示前10个进程 ps aux --sort-%mem | head -n 11说一个我踩过的坑ps aux | sort -k3 -rn和ps aux --sort-%cpu结果可能略有不同因为ps aux默认输出的%CPU并不是实时采样值而是进程累计CPU时间除以存活时间的平均值。对于刚刚启动的进程它哪怕一瞬间占满CPU平均下来%CPU也可能显示很低。所以快速判断“谁在消耗CPU”最好用top这类动态工具ps的快照数据更适合做静态分析。3.2 top命令动态监控的默认选择top命令是交互式的动态监控工具默认每3秒刷新一次它上面的全景信息量大第一行是系统运行时间和负载第二行是任务数量第三行是CPU状态第四五行是内存和交换分区下面则是按CPU占用排序的实时进程列表。top第一行里和监控关系最紧密的是load average后面三个数字分别代表过去1分钟、5分钟、15分钟的平均负载。注意负载值不是简单的百分比它表示处于可运行状态和不可中断状态的进程数。单核CPU如果负载长期超过1.0说明任务队列一直在排队系统忙不过来多核系统则要看比值比如四核机器负载到4.0说明四个核都跑满了。很多人一看到load average到2就紧张其实在16核机器上这个负载非常正常。CPU状态行里的细分项也要会认us用户态时间、sy内核态时间、ni被调整过优先级的进程占用的CPU时间、id空闲、wa等待IO完成、hi硬件中断、si软件中断、st被虚拟机偷走的时间。这里最值得关注的是wa一旦wa偏高说明磁盘或网络IO可能是瓶颈这时候光看进程的CPU占用是不够的要用iostat或vmstat去确认IO拥挤情况。进入top界面后交互按键是提升效率的关键。按P按CPU占用排序按M按内存占用排序按T按累计时间排序。想杀掉某个进程直接按k系统会问你PID输入后输入信号编号默认15是优雅结束。要调整某个进程优先级按r。按u加用户名可以只查看指定用户的进程。按1展开每个CPU核心的单独使用率。按c切换是否显示完整命令行。需要在脚本或监控程序里用top的结果就加-b批处理模式比如top -b -n 1 | head -n 30另外top -p PID1,PID2可以直接监控指定的进程不用在一堆列表里找。不过这里要坦白说一句生产环境其实我更推荐用htop它支持鼠标操作、彩色显示、甚至树状视图但红帽考试默认环境不装htop所以top的基本功必须打牢。你连top的排序键都记不住考试时大概率要紧张翻车。3.3 实战场景根据负载特征定位元凶进程光会敲命令还不够得会组合着用。这里分享一个我排查“服务器突然卡死”类问题的最常用流程。先看负载和整体状态uptime free -h iostat -x 1 3如果load average高、CPU又被打满就进top按P排序列出CPU占用最高的进程。如果是磁盘IO导致的高负载CPU状态行wa会很高此时再去ps auxk -%cpu里找那些STAT为D的进程这些大概率是正在卡在IO上的进程。如果是内存不足导致疯狂使用swapfree的available数值会很低同时top里swap使用率飙升按M排序看谁RSS占得多重点排查Java这类大内存应用。定位具体的PID之后下一步是确认这个进程到底是什么。光看COMMAND列可能只是一条路径再用如下命令看进程的详细信息# 查看进程的可执行文件真实路径 ls -l /proc/PID/exe # 查看进程的工作目录 ls -l /proc/PID/cwd # 查看进程打开的“重要文件” lsof -p PID/proc/PID目录是Linux进程信息的核心宝库里面每个文件都有意义排障时多翻翻这个目录远比瞎猜靠谱。比如/proc/PID/status里有进程状态、内存、线程数等关键信息/proc/PID/environ能看到进程启动时注入的环境变量/proc/PID/fd目录下列出了进程打开的所有文件描述符这对于排查“文件被占用删不掉”这种经典问题是直接答案。4. 进程管理实操从信号到优先级再到systemd4.1 kill不是“杀”是“发信号”新手最常见的误区是把kill当成“杀掉”的同义词。严格来说kill命令的作用是向进程发送信号只不过默认发送的是编号15的SIGTERM请求进程终止。进程收到信号后也未必会立刻退出——它可以选择忽略、自行处理所以kill默认甚至不保证能杀死进程。查看全部信号用kill -l。实际运维里最基本的是这几个1SIGHUP挂断信号通常用于让守护进程重新加载配置文件比如kill -HUP 1可让主进程重载。很多服务支持用这个信号实现平滑重载而不中断服务。2SIGINT中断信号相当于终端里按CtrlC。9SIGKILL强制杀死进程无法捕获和忽略这个信号内核直接将其终止。这是最后的杀手锏使用时要非常谨慎因为进程没有机会清理资源。15SIGTERM终止信号默认信号请求进程正常退出进程可以捕获做清理工作。18SIGCONT继续运行已停止的进程。19SIGSTOP强制停止进程进程也无法捕获相当于按了暂停键。看懂了信号再谈三个发送命令的区别。kill按PID精确发信号killall按进程名发信号比如killall -15 nginx会向所有名为nginx的进程发SIGTERMpkill支持按名称模糊匹配和更多条件比如pkill -u nginx可以结束指定用户的全部进程。pkill的坑在于按名称匹配可能误伤同名进程所以使用前先pgrep -l确认一下匹配范围。处理失控进程时有个原则从软到硬。也就是先发15号SIGTERM给它几秒钟时间清理并退出实在不退再考虑9号SIGKILL。生产环境服务如果直接KILL可能会导致数据损坏或配置没写完这个代价往往比进程卡住更麻烦。4.2 前后台任务切换CtrlZ、jobs、bg、fg与nohup在交互式shell里命令默认在前台运行CtrlC直接中止它。有时候你运行了一个耗时任务想让它去后台继续跑好腾出终端干别的这就用到作业控制。最简单的后台方式是命令末尾加sleep 3000 按下回车shell会输出一个作业编号[1]和PID。用jobs -l查看所有作业和它们的状态fg %1把1号作业调回前台bg %1把暂停的作业放到后台运行。如果一个前台任务跑太久又不方便中断可以按CtrlZ暂时挂起它此时作业状态是Stopped然后bg让它到后台继续一条龙操作非常顺手。这里必须提醒一个经典问题和nohup是两码事。只是把作业放进后台但它的标准输出、标准错误仍然连着当前终端且它仍然属于这个会话当你关闭终端即会话结束这个后台进程会收到挂断信号SIGHUP而退出。nohup command 则是命令启动时不理会SIGHUP信号加上输出重定向才能真正实现“关终端仍在跑”。标准写法是nohup ./start.sh /tmp/start.log 21 这条命令的意义是忽略挂断信号、标准输出和错误都写到日志文件、后台运行。很多新手只用就以为万事大吉结果一关SSH工具服务就跟着断多半是这个原因。再往后端一点真正的守护进程远不止nohup这么简单还要脱离会话、脱离控制终端、改变工作目录、设置文件掩码等。传统上一个进程想变成守护进程要调用setsid系统调用这正是“守护进程与会话”这个热词背后的关键点。现代Linux服务基本由systemd管理正常情况下你不需要手动写这些流程但理解会话session和进程组的概念对排查“进程被挂在哪个终端下”非常有用。简单说一个会话包含多个进程组进程组包含多个进程终端关闭时内核会向会话的前台进程组发送挂断信号。这也是为什么脱离会话的进程能躲过终端关闭的影响。4.3 nice与renice进程优先级控制Linux使用动态优先级调度而nice值就是普通进程主动调整优先级的方式。nice值的范围是-20到19数值越小优先级越高。默认情况下进程的nice值是0。为什么叫“nice”大概意思是你越谦让给别人的正数越大自己越不占便宜反过来负值就是“不客气”要求内核多给我分CPU时间。修改启动时友好度用nice -n 5 command注意只有root能把nice值往负数方向调提高优先级普通用户只能往正数方向调降低优先级这是防止普通用户把系统资源抢爆的合理限制。对已运行的进程调友好度用renicerenice -n -5 -p PIDtop里按r也能交互式修改。实际使用场景里我之前会对备份这类低优先级任务直接nice -n 10让它别挤占业务进程的CPU但如果一个任务的CPU占用已经导致系统响应变慢单纯降优先级不够最好还是直接叫停重新规划调度时间。有一点要区分清楚nice值低不等于“占CPU更快”它只是影响CPU时间片的分配权重。I/O密集型的进程靠nice值不一定有明显改善因为瓶颈在磁盘等待而不是CPU调度。4.4 systemd管理下守护进程的现代姿势RH124这章在进程管理部分必然会涉及systemd因为现代红帽系发行版里服务基本上都以守护进程daemon形式由systemd拉起并维护。它不仅仅是“开机启动管理”它还承担了进程监控、自动重启、资源限制、日志收集的职责。最基本的一套systemctl命令systemctl start 服务名 systemctl stop 服务名 systemctl restart 服务名 systemctl status 服务名 systemctl enable 服务名 systemctl disable 服务名 systemctl reload 服务名面试和实操里最容易被忽视的是start和enable的区别start是“现在启动”enable是“开机自启”两者是独立的。刚装了一个服务只start不enable重启后就没有了只enable不start这次开机期间服务并不会运行。正确姿势是两者都做或者用systemctl enable --now一步到位。想看某个服务的进程状态最直接是systemctl status 服务名它会把主进程PID、运行状态、最近日志都展示出来。日常定位服务为什么没起来第一条铁律是先看status它会提示你“Active: inactive (dead)”“Active: failed”还是“Active: running”。如果failed用journalctl -u 服务名 -n 50看日志比瞎翻配置文件高效得多。服务单元文件通常放在/usr/lib/systemd/system/系统自带和/etc/systemd/system/管理员自定义后者优先级更高。单元文件里的ExecStart决定进程怎么启动加Restartalways可以让服务在意外退出后自动拉起。额外说一个经验排查服务死亡时不要光看进程在不在还要看systemd是否把它标记为failed有时代理进程还残留着但systemd已经认为服务挂了。5. 常见问题排查与避坑实录5.1 僵尸进程杀不掉只能找它爹前面讲过僵尸进程是子进程退出后父进程没有回收。排查时用ps aux | awk $8Z如果发现大量僵尸先看它们的PPID是谁ps -o pid,ppid,stat,cmd -p 出现Z的PID然后找PPID对应的父进程。如果是常见的Java服务、Nginx、数据库进程出现了僵尸通常说明代码里有子进程退出没被wait的问题这在长期运行的服务上尤其常见数量会缓慢增长。临时处理办法是重启父进程服务重启后僵尸会被systemd收养并清理根治要检查程序代码或启动脚本里的子进程回收逻辑。注意不要试图对僵尸进程kill -9它本身已经死了发信号只会提示Process doesnt exist。另一种特殊情况是PID为1的进程自己成了僵尸这在内核层面几乎不可能正常处理只能重启系统但这种极端情况极少见到。5.2 进程杀不掉先想为什么杀不掉经常有人问“kill -9都杀不掉的进程怎么办”。这时候别急着再敲一次kill先做判断进程处于D不可中断睡眠状态它卡在IO上比如等待NFS响应、磁盘坏道、存储卡死。这种状态kill -9无效因为内核还不能切换到信号处理。处理方向是恢复IO比如重新挂载存储、恢复网络如果实在无法恢复又必须清理只能重启系统。进程是僵尸本身就死了不需要杀。进程在不停重生systemd配置了Restartalways或者有一个父进程在自动拉起它。这种必须先把上层拉起的机制停掉否则你按一个雷它冒一个芽。处理手法是先systemctl stop 服务或先kill父进程再处理子进程。当前用户权限不够普通用户不能杀别人的进程需要用sudo或root确认信号是否被进程捕获或忽略。一个排查盲区是kill -9立刻返回了但用ps看进程还在。这偶尔是因为ps显示的是内存缓存里的旧数据可以过两秒再查一次也可能是进程卡在exit后的内核清理阶段通常等一下就能消失这时候别再重复发信号反而要观察进程状态是否是Z。5.3 端口被占用找到占用进程最快路径运维里天天遇到的场景是服务启动报“Address already in use”说明端口被其他进程占用了。查端口占用最快的是ss而非过时的netstatss -tunlp | grep 8080输出里的pid和进程名就是答案。如果想进一步确认进程细节再ps -fp PID。如果看到PID是1说明某个服务由systemd直接占用可能它被配置为监听了该端口如果没有pid信息多半是当前用户权限不够要加sudo。有时候端口看起来没被占用但服务还是起不来这时候检查一下是否有进程处于TIME_WAIT状态占着端口实际上TIME_WAIT不影响bindLinux默认允许绑定处于TIME_WAIT的端口所以这种判断基本属于误区真正要查的是LISTEN状态。5.4 Java进程CPU飙升与内存溢出OOMJava进程在服务器占用Top榜上常年霸榜处理它们既要懂Linux又要懂JVM这里分享一个完整排查思路。首先在top里按P找到Java进程PID如果CPU飙升用top -H -p PID定位到占CPU最高的线程TID。注意TID是十进制而JVM线程dump里的nid是十六进制要转换printf %x\n TID然后抓线程栈jstack PID /tmp/stack.log在dump文件里搜转换后的十六进制nid就能看到那个线程正在执行什么代码比如GC线程在疯狂回收或者是某个业务线程死循环。这个过程在面试里也算经典题目答得好能加分不少。内存溢出OutOfMemoryError类问题则是另一类高发。很多人第一反应是“给JVM加堆内存”比如调整-Xmx8000m但盲调堆大小往往没有对症。更靠谱的排查顺序是先用jstat -gc PID 1s 5看GC动态确认是老年代满了还是堆本身就小再用jmap -dump:formatb,fileheap.hprof PID导出堆快照配合jhat或Eclipse Memory Analyzer分析大对象。如果OOM发生在堆外比如元空间满或线程栈溢出调-Xmx也没用。生产环境更不能随便调大堆内存尤其是容器环境直接超出容器限制会被操作系统直接杀掉进程。还有一个Java相关的实操细节热词里提到的“jps增量注解进程已禁用”这一条。当你在IDE或Maven里看到类似提示时其实指JVM的注解处理进程被禁用了。它本身不是致命的编译错误只是增量编译某些依赖的注解处理不会执行可能影响代码生成。处理方式是关掉增量编译或调整编译器配置。这类问题不算RH124考点但排障时确实容易遇到就一起写进来。5.5 进程排障问题速查表症状优先排查点常用命令系统负载高CPU打满找CPU占用最高进程确认是否业务需要top → P负载高但CPU空闲检查磁盘IO等待和D状态进程iostat、vmstat、ps aux内存占用异常找RSS最大进程检查是否存在内存泄漏top → M、free -h、ps aux --sort-%mem进程卡死无法结束判断状态是D、T还是Z再决定对策ps -o stat,cmd -p PID端口被占用查LISTEN进程ss -tunlp服务频繁重启看systemd状态和错误日志systemctl status、journalctl -uJava进程CPU高线程转储分析定位线程top -H、jstackJava进程OOM查GC日志和堆快照jstat、jmap这张表我建议保存下来实际排障就是对照表格一个个排除。排障最忌讳的就是不看状态瞎操作先花30秒把系统状态搞清楚比盲目kill高效得多。6. 结尾说几句操作习惯上的体会聊到最后比起单个命令我更想强调操作习惯。RH124第8章的知识点不算多但它们是每一种更深层排障的共同地基。我在实际工作中见过太多次因为急着kill掉一个进程导致更严重事故的情况比如数据库崩了杀了个进程让数据恢复跑不起来或者清理僵尸没找父进程业务重启了又立刻被拉起来的进程干掉新服务。所以我的建议始终是查状态再动手、从软到硬发信号、先确认父子关系再决定杀谁。一个小技巧作为收尾每次用kill之前先想三秒“这个进程的父进程是谁它有没有守护者”你会在生产环境少踩很多坑。把这些基本功练成本能不管考试还是日常排障你会发现自己从背命令的人变成了真正看得懂系统的人。