ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Linux进程管理与后台任务实战:从ps、top到kill的完整指南

Linux进程管理与后台任务实战:从ps、top到kill的完整指南 当你在 Linux 上敲下ps、top、kill这几条命令的时候多半不是在学习而是遇到了问题后台起的服务找不到了、端口被占、进程杀不掉、U 盘弹不出来甚至 dpkg 告诉你“另一个进程已经加锁”。这些都是典型的任务、进程、程序管理场景也是从新手到资深工程师都绕不开的日常操作。这篇文章我按自己实际排查问题的顺序来梳理先讲清楚程序、进程、线程这些底层概念再给你一整套查看进程、管理后台任务、终止进程的实战命令最后把高频报错和排坑经验整理出来。刚接触 Linux 的开发者、需要在服务器上部署维护服务的运维都能在这里找到直接能抄作业的命令。1. 先搞明白什么是程序、任务和进程1.1 程序是静态的进程是动态的很多新手会把程序和进程混为一谈其实一句话就能讲清楚程序是躺在磁盘上的可执行文件是静态的进程是程序被加载到内存后正在运行的实例是动态的。就好比菜谱是程序你照着菜谱炒菜的过程才是进程。ls -l /usr/bin/python3看到的是程序文件ps aux | grep python3看到的是 Python 解释器正在运行的进程实例。同一个程序可以被多次启动产生多个进程每个进程有独立的 PID、独立的内存地址空间。比如你开了三个终端窗口每个窗口里都运行着bash那系统里就有三个 bash 进程它们之间互不干扰。进程还有一个关键属性——父子关系你在 bash 里执行一条命令bash 是父进程命令对应的进程是子进程用pstree -p就能看到整棵进程树。这个父子关系在后面处理僵尸进程、批量杀进程时特别关键。进程内部又可以分为多个线程。线程是进程内的执行单元共享进程的内存、文件描述符等资源。这就是“进程和线程的区别”这个问题最常见的考点进程是资源分配的最小单位线程是 CPU 调度的最小单位。进程间相互隔离一个崩了不影响另一个但线程挂了可能导致整个进程崩溃。日常排查时用ps -eLf可以查看进程内的线程看到 LWP 列如果你发现某个 Java 服务 CPU 飙到 300%多半是它内部开了多线程得去查线程栈而不是只盯进程 PID。搜索词里出现的“进程池”“进程通信IPC”也都是在进程/线程这个根基上建立的。进程池是程序内部为了复用线程或子进程避免频繁创建销毁开销而做的池化技术IPC 则是进程间通信的机制常见有管道、信号、消息队列、共享内存、Socket 等。这些属于进阶话题但理解“每个进程是独立王国互相之间需要协商协作”是最基本的出发点。1.2 进程状态和关键字段面试和排查都靠它看进程状态是排查问题的基础功。Linux 进程常见状态可以用ps的 STAT 列看到状态含义常见场景R正在运行或可运行当前占用 CPU 的进程S可中断睡眠等待 I/O 或事件最常见D不可中断睡眠等待磁盘 I/O卡住时很难处理T已停止CtrlZ 挂起或收到 SIGSTOPZ僵尸进程子进程已结束但父进程未回收高优先级nice 值为负N低优先级nice 值为正l多线程该进程下有多线程这里面最需要警惕的是 Z 状态和 D 状态。僵尸进程只占一个进程表项不占 CPU但数量多了会导致系统 PID 耗尽表现为“fork: Cannot allocate memory”。D 状态是进程在等磁盘 I/O 返回这时候连kill -9都杀不掉得等底层 I/O 恢复。搜热词里有人问“什么叫空进程”多半是看到了这种状态栏里显示异常或者名字存在但什么都不干的进程。我遇到过的“空进程”大多是僵尸进程defunct或者是已经启动但没有任何任务的空闲服务进程比如你起了一个 web 服务但没人访问它在 ps 里 S 状态挂着看起来就像“空跑”。ps aux的输出字段也要能看懂USER运行用户、PID、%CPU、%MEM、VSZ虚拟内存大小、RSS常驻物理内存、TTY关联终端?表示无终端通常是守护进程、STAT状态、START启动时间、TIME累计 CPU 时间、COMMAND命令。排查“CPU 温度、占用及内存占用异常进程”时我第一眼就是看 %CPU 和 %MEM 两列再用排序把异常进程挑出来。2. 查看正在运行的进程和后台程序2.1 ps 命令的几种组合用熟比记全更重要ps是查看进程快照的核心命令但它选项特别多关键是要记住几组高频组合而不是把所有参数背下来。我最常用的是ps aux它能显示所有用户的进程并带出 CPU 和内存占用百分比。还有一个是ps -ef它更偏重显示 PID、PPID父进程 ID和完整命令便于梳理进程树。两者都记场景不同选用不同。如果你想查看某个特定 PID 的详细信息直接ps -fp 12345-p 指定 PID-f 显示完整格式。想找某个程序的进程最稳的是ps -ef | grep java但这里有个经典坑grep 本身也是一个进程所以结果里会混入一条grep --colorauto java新手经常对着它发愣。解决办法是用ps aux | grep java | grep -v grep过滤或者干脆用pgrep -l java来查找后者更干净。如果需要按 CPU 或内存占用排序ps aux --sort-%cpu按 CPU 降序ps aux --sort-%mem按内存降序配合head只看前几条排查“哪个进程把内存吃光了”时很快。我在线上服务器排查 OOM 问题时第一反应经常就是ps aux --sort-%mem | head -10这套命令不带交互、纯文本输出适合写到脚本里做监控告警。比如每分钟检查一下有没有 CPU 超标的进程记录下来再用 top 或 htop 做人工二次确认。2.2 top / htop 实时监控定位 CPU 和内存占用ps是快照想看实时变化得用top。top默认每 3 秒刷新一次第一行显示系统运行时间和 load average负载均值第二行看进程总数、运行数、睡眠数第三四行是 CPU 和内存占用总览。load average 有三个数字分别代表 1 分钟、5 分钟、15 分钟的平均负载。很多人会问这个数字多大算高其实要结合 CPU 核心数判断4 核机器 load 到 4 说明 CPU 刚好跑满超过 4 就是有任务在排队等待已经过载了。在top交互界面里按大写 P 按 CPU 排序按大写 M 按内存排序按 1 可以展开每个 CPU 核心的使用情况。如果你看到某个进程 CPU 持续 99%基本可以锁定它就是元凶。按 k 会提示输入 PID 和信号可以在 top 里直接终止进程按 r 可以调整进程优先级。但我不太建议新手在 top 里直接 kill容易误操作我都是先记住 PID退出 top 再用kill精确处理。htop是 top 的增强版界面更友好支持鼠标操作、树形视图、F6 排序、F9 杀进程还能直观显示每个核心的负载条。Ubuntu/Debian 用apt install htopCentOS 用yum install htop或dnf install htop。我在自己电脑上习惯直接装 htop因为它能把进程树、CPU、内存、负载全放一个屏幕里排查问题时信息密度比 top 高一大截。不过在只允许最小化安装、不能联网装包的服务器上top依然是兜底方案所以两个都要熟练。2.3 用 pgrep、pidof、pstree 快速锁定目标进程除了 ps 加 grepLinux 还提供了一批专门用于定位进程的命令效率更高也更不容易出错。pgrep通过进程名匹配 PID配合-l显示进程名pgrep -l sshd pgrep -u root php-fpm pgrep -f java -jar app.jar-f参数非常强大它匹配的是完整命令行而不是进程名。搜热词里“未找到 baidunetdiskhost 进程”这类场景其实也是同理你以为的进程名可能不对或者它改了名字需要先用ps aux | grep -i baidu去模糊搜。pgrep -f在找 Java 程序、Python 脚本这类启动命令比较长的进程时特别好用因为进程名往往就是java或python3直接按名字匹配会出来一堆按命令匹配才能精确定位。pidof是用来精确匹配二进制名的比如pidof nginx会列出所有 nginx 进程的 PID比 pgrep 更严格。pstree -p则展示那棵进程树加上 PID 后能一眼看清父子关系。排查“为什么这个服务起来了又被杀掉”“谁是我的服务父进程”的时候pstree比 ps aux 直观得多。我通常在确认服务被 systemd 托管时还会顺手执行systemctl status 服务名因为它能直接告诉你进程的 PID、状态、最近日志比在 ps 里大海捞针强太多。3. 前后台任务切换与后台运行3.1 前台、后台与守护进程一个终端三种命运Linux 里手动执行的进程按是否占用终端分为前台进程和后台进程还有一种是完全脱离开终端的守护进程daemon。前台进程占用当前终端你在这个终端里什么都干不了只能等它跑完后台进程通过放到后台不占用输入但你关掉终端窗口时它可能一起死掉守护进程则是完全脱离终端、由 init/systemd 托管的进程比如 nginx、sshd、mysqld它们跟你的登录会话没有关系关了终端照样跑。理解了这三者区别你就明白几个经典场景的根源了。热词里有“监控前台进程”其实就是系统里跑着跑着你想知道哪个进程占了前台、怎么把它切到后台去继续跑又怎么把它拉回来。还有“idea 怎么多开进程服务”本质上是 IDE 启动的 Java 进程是前台子进程你关掉窗口它就可能被终止要保持后台运行得靠 nohup 或系统服务方式而不是简单点个关闭按钮。我在服务器上部署应用时经常遇到的情况是一条命令执行时间很长比如打包、同步、执行数据库迁移执行到一半突然想干别的但又不能 CtrlC一按就中断了任务。这种时候就要靠任务切换技巧而不是傻等。3.2 jobs、fg、bg、CtrlZ 的前后台切换实操如果一条命令正在前台运行你先按CtrlZ它会把当前前台进程挂起暂停回到 shell 提示符。注意挂起不是终止进程还在内存里只是暂停执行。然后输入jobs -l查看当前 shell 的后台任务列表每个任务前面有个编号[1]、[2]后面跟着 PID 和状态。想让暂停的任务继续在后台运行用bg %1想把它调回前台继续占用终端用fg %1。完整的流程大概是# 运行一个耗时任务 $ ./build.sh ^Z [1] Stopped ./build.sh # 查看任务列表 $ jobs -l [1] 12345 Stopped ./build.sh # 放到后台继续跑 $ bg %1 # 调回前台 $ fg %1这套组合拳在本地开发时很有用。有一次我编译一个大型项目预计要十几分钟中途想起来要改个配置文件直接 CtrlZ 挂起改完bg %1让它在后台继续编译。这里有个细节jobs只能看到当前 shell 启动的任务如果你重新开一个终端jobs是空的但进程本身还在。也就是说任务列表是 shell 层面的概念不是全局的全局的进程列表还是得回到ps去看。3.3 nohup 与日志重定向真正安全的后台运行前面说放在后台运行的进程终端关闭时会收到 SIGHUP挂断信号默认动作是终止进程。这就是为什么你 SSH 登录服务器./app 启动后一断开连接再连上来发现服务没了。要解决这个问题核心思路是让进程忽略挂断信号标准做法是nohupnohup java -jar app.jar --server.port8080 app.log 21 拆开来看nohup让进程忽略 SIGHUP放后台 app.log把标准输出重定向到文件21把标准错误也合并到同一个文件。最后一步很多人会漏导致程序报错信息没进日志全打在已经关掉的终端上你排查问题时什么都看不到。为什么不直接./app 而要 nohup因为只解决“不占用终端”的问题没有解决“关闭终端时被 SIGHUP 杀死”的问题。所以真正要在生产环境后台长期跑的进程我的习惯是写清楚日志路径和重定向用nohup ... 启动启动后立刻echo $!记录 PID过几秒ps -fp PID确认进程还活着再tail -f 日志文件看启动日志。还有更彻底的做法是setsid它让进程完全脱离当前会话成为独立的会话首进程。不过我日常部署很少用 setsid因为 nohup 加日志重定向已经够用而且可读性好。另外补充一个容易被忽略的点有些进程启动时会检查当前终端状态不使用 nohup 可能会因为终端信息丢失而启动失败。所以如果你写了个脚本要在系统启动时调起某个服务即使脚本里已经加了最好还是配合 nohup 或者交给 systemd 管理省得踩“明明后台运行了却莫名其妙退出”的坑。4. 终止任务与进程kill 全家桶实战4.1 信号机制为什么 kill 不一定能杀掉进程很多人以为kill就是“杀进程”其实它只是向进程发送一个信号。进程收到信号后怎么处理由它自己决定。kill默认发送 SIGTERM编号 15这是一个“请优雅退出”的请求进程可以自己清理资源、保存状态后再退出。kill -9发送 SIGKILL这是内核强制终止进程没有机会做任何善后。常用信号我整理成一张表信号编号默认动作典型使用场景SIGHUP1终止进程nohup 忽略它nginx -s reload 重载配置SIGINT2中断进程终端按 CtrlC 触发SIGKILL9强制终止kill -9 PIDSIGTERM15请求终止kill PID 默认信号SIGSTOP19暂停进程无法被捕获或忽略CtrlZ 会发送类似信号为什么业内一直强调“先 kill -15再 kill -9”因为很多程序在退出时需要保存状态、释放端口、清理临时文件。比如 MySQL、Redis 这类数据库直接 kill -9 可能导致数据文件损坏或未落盘的数据丢失。我处理生产进程的标准流程是先kill PID等个三五秒看进程退出没有没退出再kill -9 PID。这个过程看起来多了一步但能避免大量踩坑。4.2 kill、killall、pkill名字相近脾气不同kill是按 PID 操作适合精确打击killall是按进程名操作会杀掉所有同名的进程pkill是按进程名或命令行模式匹配比 killall 更灵活也更危险。# 精确杀 kill 12345 kill -9 12345 # 按名字杀所有 nginx 进程全停 killall nginx # 按模式匹配完整命令行 pkill -f java -jar app.jarpkill -f是我在服务器上最常用的方式因为 Java 类应用启动后进程名都是javakillall java会把服务器上所有 Java 服务全停掉后果很严重。但pkill -f app.jar能精准匹配启动命令行里包含 app.jar 的进程既准确又不过度。热词里有“安全卫士进程无法中止进程拒绝访问怎么办”这种报错本质是权限不足你的用户不是进程所有者也没有 root 权限信号发不过去。解决办法是sudo kill -9 PID或者先用ps -fp PID确认进程所有者再决定是否需要切换用户。这里有个 pkill 经典坑pkill -f xxx会匹配所有命令行里包含 xxx 的进程包括你自己正在执行的这条命令本身。比如你执行pkill -f grep很可能把当前 shell 执行的这条命令行也匹配进去导致命令刚执行完就被自己杀掉。更常见的是ps aux | grep xxx时grep 进程自己出现在结果里。所以当你要用模式匹配杀进程前最好先pgrep -f 模式看一下会匹配到哪些 PID确认无误再下手。4.3 杀完进程后端口仍被占为什么一个非常常见的场景你kill -9了一个服务接着重启服务结果报“端口被占用”用ss -lntup | grep :8080一查发现之前的 PID 已经没了但端口还是闲置不了。这通常有两种情况连接处于 TIME_WAIT 状态。TCP 四次挥手后主动关闭方要等待 2MSL 才能彻底释放连接端口会短暂处于 TIME_WAIT。这时新进程 bind 同一个端口一般会失败解决方法是等几十秒或者设置SO_REUSEADDR让程序可以重用地址。服务有守护进程或自动拉起机制。你杀掉的只是一个子进程它的父进程或 systemd 马上又拉起了一个新进程于是端口依然被占。排查方法是ps -ef | grep 服务名看是不是又出现新 PID再看父进程是谁如果是 systemd 托管的服务直接systemctl stop 服务名才是正规姿势而不是kill -9。我自己见过最多的情况是第二种。有人部署服务时图省事直接把 jar 放在 systemd 里管但排查时又只用 kill结果每次杀完 systemd 自动拉起还以为是 kill 命令不生效。正确的做法是先判断这个进程是谁拉起的pstree -p PID看父进程如果父进程是 1systemd说明它被系统托管要用systemctl管理如果父进程是一个 shell 或 supervisor要从源头停掉。5. 常见问题与排查技巧实录5.1 端口被占用怎么找到是哪个进程这是被搜索最多的场景之一其实命令就两条。一条是新的 iproute2 系列自带ss -lntup | grep :8080另一条是老牌的 netstatnetstat -tulnp | grep :8080输出里能看到监听该端口的进程 PID 和进程名然后ps -fp PID看详情再决定是 kill 还是保留。如果 ss/netstat 输出里没有进程名和 PID多半是权限不够加sudo再试。热词里“centos怎么看进程的网络占用”问的其实就是这个。除了查端口ss -s可以看整体连接统计ss -tp可以看每个 socket 对应的进程排查谁在发起外部连接时很实用。顺带说一句lsof -i :8080也能达到同样效果但 lsof 不一定预装ss基本每个发行版都带所以我优先推荐 ss。端口占用问题解决后U 盘弹不出来的原因也差不多本质是有进程在占用挂载目录。5.2 U 盘无法弹出、dpkg 前端锁的通用排查思路“U盘无法弹出请先结束占用进程”是 Windows 上的提示Linux 下表现是umount报target is busy。排查方法和端口占用如出一辙找出谁在用那个目录。# 查看占用 /mnt/usb 目录的进程 lsof f -- /mnt/usb # 或者 fuser -m /mnt/usb然后把这些进程的当前工作目录切走或者直接结束占用进程再执行umount /mnt/usb。这里有个特殊注意点即使你没有进程正在读写 U 盘只要某个终端的当前目录在 U 盘里umount 也会报 busy。我一开始经常被这个坑到后来学乖了fuser -m看到 PID 后先确认是不是自己的 shell是的话cd /再卸载就行。dpkg 前端锁的报错也是同款思路。执行apt install时报“另外一个进程已经为 dpkg 前端锁 加锁”通常说明系统里已经有一个 dpkg 或 apt 任务在跑。排查方法ps aux | grep -E apt|dpkg如果确实有 apt 进程正在安装等它完成如果没有可能是上次安装异常退出留下的锁文件可以安全删除sudo rm /var/lib/dpkg/lock-frontend sudo rm /var/lib/dpkg/lock但这里必须强调删锁是最后一步。你要先判断系统里有没有还在执行的任务否则正在跑一半的安装被你删了锁dpkg 状态可能损坏后患无穷。5.3 僵尸进程和“杀不掉的进程”怎么处理僵尸进程在 ps 里状态是 ZCOMMAND 列带defunct。它无法被 kill因为它的生命已经结束只是进程表项还留着等父进程回收。你杀不了僵尸只能杀它的父进程让父进程退出后由 initPID 1领养并回收。具体操作# 找到僵尸进程的 PID 和 PPID ps aux | grep defunct # 杀掉父进程 kill -9 PPID如果父进程是 systemd 托管服务systemctl restart 服务名通常也能触发回收。这里最怕的是父进程是个长驻服务比如 java 应用杀了它会影响业务这时候需要评估僵尸进程数量数量少影响不大可以先观察数量多说明程序有 bug子进程退出后没调用wait()回收要改代码或者在系统层面监控。还有一种“杀不掉”是 D 状态进程在内核里等待磁盘 I/Okill -9也无效只能等 I/O 恢复。遇到 D 状态进程卡死我会先看dmesg有没有磁盘错误然后检查是不是磁盘满、NFS 挂载不可达。NFS 服务端挂掉特别容易产生一堆 D 状态进程这时候干掉 NFS 客户端进程之前先恢复网络/磁盘才是正解。5.4 桌面环境里的“可关闭吗”类进程热词里“mate-indicators 进程可关闭吗”“chatgpt 桌面端启动之后只有进程没有窗口”都是桌面环境相关的问题。mate-indicators是 MATE 桌面环境的指示器进程负责托盘图标、输入法、网络状态等显示。杀掉它不会崩系统但托盘图标会消失。类似的环境还有nm-applet网络管理器、bluetooth-applet蓝牙。这类“系统面板进程”我的建议是如果你在图形桌面里正常用别随便 kill如果服务器没装桌面看到这些进程才需要奇怪。“只有进程没有窗口”的情况通常是 GUI 应用启动时连不上显示服务比如 DISPLAY 环境变量没设置进程起来了但窗口渲染不出来。这时候先检查echo $DISPLAY确认是不是在 SSH 会话里想启动图形程序或者在 Wayland/X11 环境下重置桌面会话。这些现象和进程管理本身关系不大但排查路径都是同一条先ps确认进程活着再往上层找原因。最后分享个人的一点习惯做了这么多年运维和开发我总结一个非常朴素的流程遇到任何进程问题先用ps aux | grep 关键字定位再看pstree -p弄清父子关系判断进程是谁拉起的然后决定是 kill 还是 systemctl stop。千万不要上来就kill -9给自己留一点观察和判断的时间很多时候你只需要kill加tail -f看日志就解决问题了。还有一点操作前多看一眼 PID我见过太多把pkill -f写太宽导致整个服务全没了的惨案。命令都简单难的是养成先确认、再动手的肌肉记忆。如果这篇能帮你少走一次弯路那就值了。
返回列表