Linux进程管理与计划任务实战:从ps到crontab的运维核心技能
Linux 服务器用久了你会发现日常的活儿翻来覆去就那么几件看一眼系统负载把跑飞的进程找出来处理掉再定时让脚本替你把日志清理、数据备份、缓存刷新这些事干了。这些事的底层全都落在“进程管理”和“计划任务管理”这两块基本功上。我见过不少开发同事代码写得漂亮一上服务器就懵ps认不全输出crontab只敢照抄模板任务不执行了不知道从哪排查。这篇文章不刷概念专门讲实际运维里怎么把进程看清楚、管明白怎么让计划任务老老实实按点干活顺便把那些文档里不会写的坑一并抖出来。适合刚接手服务器运维的同学也适合写代码但需要自己部署服务的开发者照着做能少踩很多冤枉坑。1. 先把进程这回事吃透后面操作才不慌进程管理看似只是几个命令的事但底层不理解遇到问题很容易卡在“现象都能看到原因说不清楚”的状态。所以我先花点篇幅讲进程在系统里的存在形式这决定了后面每个操作的选择。1.1 进程不是程序它是程序活着的样子程序和进程是两码事。程序是磁盘上的一个文件比如/usr/bin/nginx躺在那不动。进程是程序被加载进内存、分配了CPU时间片、有了PID号之后活生生的执行实例。同一个程序可以同时跑出多个进程比如Nginx有两个master进程加一堆worker进程它们代码相同、PID不同、内存空间独立。Linux里查看进程这件事本质上是看内核的进程表。每个进程在内核里都有一个task_struct结构记录它的PID、父进程PID、状态、打开的文件、环境变量、信号状态等。你执行的ps命令做的就是遍历这个进程表然后格式化输出。理解了这一点你就能明白为什么有些进程你明明没直接启动但它就是在那跑着——比如系统启动时由init或systemd拉起来的各种守护进程它们是内核直接或间接创建的子进程。进程中有一个概念必须单独拎出来强调父进程与子进程。在Linux下进程天生是树状结构。所有进程的祖先都是PID为1的进程现在大部分发行版是systemd。你每次在终端敲一个命令shell会fork出一个子进程去执行这个子进程的父进程就是你的shell。用pstree -p能看得非常直观层次清清楚楚。排查问题的时候顺着父子关系找源头能省不少力气比如一个脚本莫名在后台跑通过ps -ef找到它的父进程是谁基本就能猜到是谁把它拉起来的。1.2 必须认识的几种进程状态ps输出里的STAT列很多人不看但这是判断进程健康度的关键。常见状态用字母表示RRunning正在运行或可运行占用着CPU或排队等CPU。R状态多不可怕可怕的是大量R状态且CPU爆满说明系统负载过高或有进程陷入死循环。SSleeping可中断睡眠正在等待某个事件比如等I/O、等网络数据。绝大多数正常进程平时都处于S状态这非常健康。DUninterruptible Sleep不可中断睡眠通常是在等待磁盘I/O。D状态本身不是坏事但如果大量D状态堆积就要注意是不是磁盘出了故障或者存储性能严重跟不上。这种进程很难被kill因为它不会响应信号哪怕你kill -9也得等内核I/O操作完成才肯死。TStopped被暂停通常是被CtrlZ挂起或者被SIGSTOP信号暂停。这种进程还活着kill -SIGCONT可以让它继续跑。ZZombie僵尸进程子进程退出后父进程没有调用wait()回收它的残留状态。僵尸进程杀不掉因为它在技术上已经死了唯一的办法是让它的父进程去回收或者把父进程干掉让systemd收养并回收。几个僵尸可能没事但大量堆积会占满进程表导致无法创建新进程。我梳理了一个表方便你日常快速对照判断状态含义常见原因处理思路R运行/可运行正常消耗CPU或死循环、CPU压力大结合top看CPU占用是否异常S可中断睡眠等待资源正常状态无需处理D不可中断睡眠等待磁盘I/O磁盘故障或性能瓶颈检查磁盘健康和使用率T暂停CtrlZ或信号挂起确认是否需要kill -SIGCONT恢复Z僵尸父进程未回收多见于编程不当的守护进程排查父进程必要时重启父进程1.3 进程之间怎么协商信号机制进程管理里的“杀”“停”“续”动作本质都是发信号。Linux信号是内核与进程之间的一种软中断通知方式进程收到后按预设行为处理。最常见的信号SIGTERM15标准终止信号请求进程优雅退出。进程可以捕获它做清理工作比如保存数据、释放资源。kill PID默认发这个应该作为第一选择。SIGKILL9强制杀死内核直接终止进程进程没有机会清理。这是万不得已才用的手段可能导致数据丢失或文件损坏。SIGHUP1挂断信号终端断开时自动发给进程。很多守护进程把它重新解释为“重新加载配置”所以kill -HUP PID常用于让nginx、sshd等服务平滑重读配置。SIGSTOP19/ SIGCONT18暂停和恢复。我自己的习惯是先sigterm温柔地让进程退等几秒看进程还在不在如果还在再用sigkill暴力收场。见过太多人上来就kill -9把一个本来能优雅退出的任务搞得半途而废数据库这类对一致性要求高的服务尤其容易出问题。另外提醒一句kill命令不只是按PID杀也可以配合pgrep、pkill按名字批量匹配但批量操作前一定要先pgrep -l确认匹配范围免得误伤同名进程。2. 进程管理实操从“看”到“控”的一整套动作这一节是每天都要用的东西。我把查看、排序、调优先级、终止和后台托管串成一条线每步都讲清楚为什么这么做而不是只丢命令。2.1 ps平时怎么看看哪些字段ps是静态快照适合快速定位“有哪些进程”“PID是多少”“父子关系如何”。我最常用的组合# 查看所有进程显示完整命令行 ps -ef # 自定义字段看更关注的信息 ps -eo pid,ppid,user,stat,%cpu,%mem,cmd --sort-%cpu # 查找某个进程的PID ps -ef | grep nginx-eo配合--sort是我比较推荐的用法能一眼看到CPU占用最高的进程是谁。ps -ef里比较坑的一点是CMD列会被截断要看完整命令行就用ps -efww。实际排查时有个心得怀疑某个进程异常先看它的父进程和启动时间。启动时间和系统启动时间非常接近的一般是开机自启动的守护进程启动时间很新又没人认领的可能是被脚本临时拉起的任务。再看它的工作目录/proc/PID/cwd和打开的文件/proc/PID/fd经常能发现脚本调错了路径、或者日志写进了奇怪位置的线索。2.2 top / htop动态监控的两种姿势ps只能看一刹那要观察趋势就得用top。它每3秒刷新一次按P按CPU排序按M按内存排序按k可以输入PID杀进程按r可以调整进程优先级相当于renice。top里最需要关注的是load average三个值它表示最近1分钟、5分钟、15分钟的系统平均负载。这个负载不是CPU使用率而是处于R状态和D状态的进程总数。我判断系统是否过载的经验是三个数都持续高于CPU核数很多说明排队严重三个数差异大说明负载正在快速变化做过压测的同学应该深有体会。htop是top的进阶版界面彩色支持鼠标按F6就能按列排序F9直接调出击杀菜单F7/F8调nice值还能通过F5展开进程树。如果环境允许装我强烈建议装一个htop日常排查效率能提升不少。不过生产服务器上有时没装所以top的基本功不能丢。2.3 调整优先级nice和reniceLinux调度器会根据优先级分配CPU时间优先级就体现在一个叫nice值的参数上。范围为-20到19默认0数值越小优先级越高也就是说-20是最高优先级、19是最低。用nice启动进程时指定优先级用renice调整已经是运行中的进程# 以低优先级启动一个任务 nice -n 10 /usr/local/bin/backup.sh # 把某个PID的优先级调高 renice -n -5 -p 12345为什么要调这个最常见的场景是凌晨跑批处理任务比如数据统计、全量备份和线上业务抢CPU业务延迟飙升。更优雅的做法是给批处理任务设置较高的nice值让它有CPU可用但不抢占时机的资源。反过来某个关键服务响应变慢可以把它nice值调低比如renice -n -5 -p PID让它多享有一点CPU。注意普通用户只能调低优先级增加nice值调高需要root权限这是安全设计。我在实践中用过一次很成功的案例某公司一台数据库服务器上同时跑了个数据仓库ETL任务业务高峰期查询明显变慢后来把ETL的nice值调到了15左右问题直接缓解不用再给数据库扩容。2.4 终止进程kill的多种姿势前面讲了信号基础这里说实操。终止进程有几种手段按推荐顺序# 1. 温柔终止 kill PID # 2. 几秒后仍存活强制终止 kill -9 PID # 3. 按名字批量终止先确认再执行 pgrep -fl nginx pkill -f nginx我遇到过不止一次kill -9杀不掉的进程查状态发现是D状态在等磁盘I/O这种情况只能先查磁盘是不是被挖满了、文件系统是不是出问题等I/O恢复后进程自然消亡。还有一种情况是真僵尸进程Z状态kill -9根本没用因为它已经死了只能按前面说的处理父进程。按名字杀进程时有个细节pkill -f会自动去匹配整个命令行参数比如你执行pkill -f test.py会把命令行里包含test.py的进程全干掉。这个很危险——如果有个进程命令行是python /home/user/run_test.py --env prod它也可能被匹配到取决于字符串出现位置。所以批量杀进程前先pgrep -af把完整匹配情况打出来亲眼确认一遍再动手。2.5 让进程脱离终端nohup、setsid与后台托管在终端里跑一个耗时任务只要把终端关了或者网络断了、SSH会话超时进程就会收到SIGHUP信号然后退出。这在实际运维里很常见本地连服务器跑个迁移脚本关了电脑脚本也断气了数据迁一半停在那。解决思路是让进程脱离当前会话最传统的方式是# nohup 后台运行 输出重定向 nohup python /opt/scripts/migrate.py /var/log/migrate.log 21 nohup的意思是忽略SIGHUP信号。把进程放到后台。输出必须做重定向否则进程往当前终端写日志终端关了照样出问题。但nohup只防挂断不防个彻底的基础问题——它不负责把进程从当前会话分离。更彻底的方式是用setsid让进程成为新的会话首领setsid python /opt/scripts/migrate.py /var/log/migrate.log 21 /dev/null 这里注意我加了 /dev/null把进程的标准输入也从终端断开有些进程在后台跑会因为等输入而卡住。如果你要对长时间运行的交互式任务做托管比如用vim改远程配置、跑一个需要持续交互的程序推荐直接用screen或tmux。我个人的选择是tmux既可以随时断开重连还能开多个窗口防挂断的能力也更强。新上手的话screen更简单但功能少一些。总结一句单纯丢后台用nohup或setsid要长期会话托管用tmux。3. 计划任务管理让机器按点干活进程搞定了接下来是自动化。计划任务的场景太多了凌晨清理日志、每天同步数据、每周打包备份、每月生成报表。没有计划任务这些全得人工盯不现实。Linux下最经典的是cron但现代系统里systemd timer也在不断追上来两者各有各的优点。3.1 cron的底层机制与配置文件位置cron的架构不复杂后台常驻一个crond守护进程每分钟苏醒一次检查系统里的任务表看哪些任务符合当前时间分、时、日、月、周条件符合就启动一个子进程去执行命令。任务表分几个层级/etc/crontab系统级的主任务文件包含run-parts调用每天、每周、每月的脚本目录。/etc/cron.d/系统级包管理的任务目录软件安装时会往这丢任务文件。/var/spool/cron/用户级别的crontab文件每个用户一个文件名就是用户名。日常用crontab -e编辑的就是这个。/etc/cron.hourly/、/etc/cron.daily/、/etc/cron.weekly/、/etc/cron.monthly/系统预定义周期脚本目录配合run-parts使用往目录里丢一个脚本就会被周期执行。给普通用户配置计划任务推荐直接用crontab -e它会自动语法检查编辑完就生效。直接改/var/spool/cron/下文件也行但不建议手工改容易语法错误且需要确定permission没错。3.2 crontab语法拆解五个时间位和一个命令crontab的每行由6部分组成前5部分是时间表达式第6部分是命令。五个时间位从左到右是分0-59、时0-23、日1-31、月1-12、周0-70和7都表示周日。常用的写法# 每分钟执行 * * * * * command # 每天凌晨2点半执行 30 2 * * * command # 每周一到周五上午9点执行 0 9 * * 1-5 command # 每隔5分钟执行一次 */5 * * * * command # 每天8点、12点、18点各执行一次 0 8,12,18 * * * command有几个坑必须记住。第一个坑分和时字段的*/n表示的是“从0开始每隔n个时间单位”不是“从当前时间开始”。*/5在小时字段上表示0、5、10、15、20点触发想表达“在某个范围每5小时一次”得写成1-23/5这种形式没那么直观我一般尽量避免。第二个坑日和周两个字段是“或”的关系。写0 0 1 * 1表达的意思很拧巴——它是“每月1号”和“每周一”的并集即每个月的1号以及每个周一都执行而不是“每月1号且正好是周一才执行”这个解释在一致性的cron实现里是标准行为。想表达“每个月第一个周一”得用更复杂的方式比如在命令里加判断条件或者用/etc/cron.d里支持额外选项的形式。第三个坑命令里的%会被转义。比如命令中用了date %Y%m%d在crontab里必须写成date \%Y\%m\%d否则cron会把%当成换行处理命令就被截断得莫名其妙。我因为这个问题吃过亏建议直接使用单引号包住%。第四个坑crontab -e的时区。系统时区决定了“几点执行”是几点。很多服务器默认UTC你看着命令里写的是18点实际跑的时候是北京时间凌晨2点。先date看下系统时区如果有差第一反应应该是调整时区而不是把任务时间往前移否则一到夏令时切换就会乱。3.3 几个实用的cron场景与写法直接给几个我个人常用的模板改改路径就能用# 每天凌晨1点清理7天前的临时文件 0 1 * * * find /tmp/app_tmp -type f -mtime 7 -delete /var/log/clean_tmp.log 21 # 每天凌晨3点备份数据库保留最近7天 0 3 * * * /opt/scripts/backup_db.sh /var/log/backup_db.log 21 # 工作日每5分钟检测一次某个服务是否存活 */5 * * * 1-5 /opt/scripts/health_check.sh /var/log/health_check.log 21这里一定注意日志重定向。cron默认会把任务的输出通过邮件发送给MAILTO指定的人或本机用户。服务器没配邮件服务时邮件会被送到本地/var/spool/postfix/mailbox时间一长邮箱目录占满磁盘这是非常经典的“cron跑了一年磁盘满了”事故。最稳妥的做法是每个任务都重定向到日志文件并且日志文件单独留目录、定期轮转既方便排查又能避免邮件堆积问题。另外MAILTO可以在crontab文件顶部设置取消邮件通知。设置后记得确认自己的命令将输出重定向了不然日志丢了才是更大的麻烦。3.4 at一次性任务的补充方案cron适合周期任务如果只是“今晚23点跑一次”用它反而不直观。at就是干这个的# 今晚23点执行一次脚本 at 23:00 /opt/scripts/one_time_task.sh CtrlD结束输入at还支持相对时间和日期组合# 5分钟后执行 at now 5 minutes # 明天下午3点执行 at 3pm 1 day查看和删除任务用atq和atrm 任务编号。系统里通常用/etc/at.allow和/etc/at.deny控制谁能使用普通用户默认允许如果想管控维护一个at.allow列表更保险。at执行的任务同样会邮件输出所以也别忘了重定向。说实话at用得没有cron频繁但在一次性补数据、临时任务上比写临时crontab然后忘记了删要安全得多推荐养成习惯。3.5 systemd timer更现代、更强的计划任务方案如果你用的是比较新的发行版且系统已经完全运行在systemd下那我建议你在新需求上优先考虑systemd timer而不是cron。它的优势非常明显依赖管理可以定义任务在某个service启动成功后再执行这对“等某服务就绪再跑脚本”的需求太有用了。精确语义支持日历语法类似cron但有更精确规则和单调时间比如“开机后10分钟”“上次执行后每周一”还能应对错过执行时间Persistenttrue的情况。日志一体化任务的输出统一由journald管理journalctl -u timer名就能看日志不再收到邮件垃圾排查也方便。一个基本的timer包含两个文件比如test-task.service和test-task.timer# /etc/systemd/system/test-task.service [Unit] DescriptionMy test task [Service] Typeoneshot ExecStart/opt/scripts/test_task.sh# /etc/systemd/system/test-task.timer [Unit] DescriptionRun test task every day at 3am [Timer] OnCalendar*-*-* 03:00:00 Persistenttrue [Install] WantedBytimers.target启用和查看systemctl daemon-reload systemctl enable --now test-task.timer systemctl list-timersOnCalendar的语法比cron更灵活也更严格比如Mon..Fri 09:00:00表示周一到周五早上9点*-*-1..7 02:00:00表示每月前7天凌晨2点。Persistenttrue是它最棒的特性之一机器在应该执行任务时是关机的下次开机后systemd会把错过的任务补跑这让它天然具备了类似anacron的能力。我一般把备份任务、日志清理任务都放在systemd timer里依赖和日志都好管。3.6 anacron解决关机漏跑问题的补丁cron有个先天性局限它假定机器7x24小时开着。如果你有一台个人用的机器每天定时关机的那么cron里写的“每天凌晨2点跑备份”就永远没法生效。这时候靠anacron。anacron的设计逻辑完全不同它不是“到点就跑”而是“开启时看看有没有该跑而没跑的任务有就补跑”。默认的/etc/anacrontab通过run-parts调用cron.daily、cron.weekly等目录# 格式周期天数 延迟分钟 任务名 命令 1 5 cron.daily /usr/bin/run-parts /etc/cron.daily 7 10 cron.weekly /usr/bin/run-parts /etc/cron.weekly意思是距上次执行超过1天开机后等5分钟跑daily任务超过7天开机后等10分钟跑weekly任务。如果系统是长期运行的服务器crond是主力如果是经常关机的桌面机器或者工作机建议把重要的“每天一次”任务放进/etc/cron.daily/让anacron兜底这个组合比较实用。4. 常见问题与排查技巧实录这两个主题实操多了真正常出问题的地方基本就集中在那几个点。下面把我这些年踩过的坑集中整理了一遍。4.1 cron任务就是不执行怎么查出现这种问题我有一套固定的排查路径检查crond是否在跑systemctl status crond不在就启动并设开机自启。有些精简过的容器镜像里压根没装crond安装了以后必须手动启动。看crontab文件内容crontab -l确认写入的任务真的存在。注意修改完不检查就关闭编辑器的错误任务写成了* * * * * command注释掉的状态也是有的。手动执行命令确认命令本身没问题很多时候不是cron的问题是命令在手动环境下能跑在cron环境下跑不了这个下面单独讲。看cron自己的日志不同发行版日志位置不同有的在/var/log/cron有的交给journald。grep CRON /var/log/syslog或者journalctl -u crond重点看有没有出现CMD行以及错误信息。确认脚本有可执行权限如果ExecStart或cron直接调脚本路径脚本需要有执行权限且首行要有正确的shebang如#!/bin/bash。查日志这一步很关键。有一次同事说某个统计任务天天不跑日志一查发现cron启动了一个子进程但子进程因为没有权限去读某个配置文件直接退了输出被丢弃。这类问题不查日志真的很难定位。4.2 环境变量导致的“手动行自动不行”这是cron任务失败率最高的一类原因。cron执行任务时环境比你的登录shell“干净”得多——它不读取用户的家目录配置如~/.bash_profile也不继承当前的PATH默认PATH往往只有/usr/bin:/bin。如果你脚本里用到了/usr/local/bin下的工具或者依赖JAVA_HOME、PYTHONPATH等环境变量在cron环境下就会“找不到命令”。解决思路有几个按优先级排列脚本内部通过source或显式export设置需要的环境变量。凡是命令依赖环境变量的一律在脚本开头处理。命令路径全用绝对路径。比如/usr/bin/find而不是裸写find。脚本里调用各种工具也一样。如果不涉及的敏感信息也可以在crontab文件顶部声明公共变量比如SHELL/bin/bash PATH/usr/local/bin:/usr/bin:/bin PYTHONPATH/opt/venv/lib/python3.11/site-packages 30 2 * * * /opt/scripts/task.py有个细节cron执行脚本时工作目录也不是脚本所在的目录默认是用户的家目录。所以脚本里用相对路径很危险。我习惯在脚本第一行强制cd到脚本所在目录#!/bin/bash cd $(dirname $0)这样无论从哪调用相对路径都不会错。这个习惯救了我好几次。4.3 进程杀不掉、僵死、端口占用进程相关问题里常见的三个连招杀不掉的进程先ps -o stat,pid,cmd -p PID看状态。D状态等待I/O的查找磁盘是否满、文件系统是否卡住Z状态僵尸的找父进程并考虑重启父进程T状态暂停的用kill -SIGCONT让它恢复。如果进程是某个服务的子进程重启整个服务往往更省事。端口被占用找凶手# 查找占用8080端口的进程 ss -lntp | grep 8080 lsof -i :8080拿到PID后用ps -fp PID看是什么进程。这里提醒一下使用容器时ss看到的往往是外面宿主机上端口映射的进程要结合docker ps一起排查别误杀了宿主机的系统进程。僵尸进程堆积优先看父进程是谁很多时候是“射线型”脚本比如父进程启动了一堆子进程做并行处理每个子进程结束了父进程没去wait。这种个案例最好的方案是修复代码里的进程回收逻辑临时应急办法是重启父进程因为僵尸进程由systemd接管后会被快速回收。长期堆积的僵尸如果父进程是systemdPID为1那更说明是系统层面循环产生没有可靠回收直接考虑重启比较干净。4.4 任务重复执行与并发问题写了定时任务最怕的不是不跑而是“多跑了”。常见场景任务跑得很慢下次cron又到点了上一个还没结束两个一起跑数据库锁冲突、数据重复、磁盘空间迅速耗尽。解决思路是给任务加“互斥锁”让同一时间只允许一个实例运行。最常用且简单的实现是用flock# 在crontab里调用脚本前加锁 */5 * * * * /usr/bin/flock -xn /var/lock/backup.lock -c /opt/scripts/backup.sh /var/log/backup.log 21-x表示排他锁-n表示如果锁被占直接放弃不等待。这样如果上一次任务还没完下一次就不会再启动。flock的粒度是锁文件所以每个任务要用独立的锁文件路径。脚本内部也可以自己实现pid文件互斥但flock在命令层就能解决简单可靠我推荐直接用。另一个相关问题是任务的幂等性。即使有了互斥锁也尽量要求脚本本身可重入——即重复执行结果一致不产生副作用。比如数据同步脚本用INSERT ... ON DUPLICATE KEY UPDATE而不是纯INSERT文件备份用覆盖写而不是追加这样就算真出了并发意外损失也可控。4.5 计划任务的安全与审计建议计划任务的权限和内容长期没人管会变成安全隐患。至少做三件事严格限制cron白名单如果需要控制哪些用户可以配置cron创建/etc/cron.allow只写允许的用户名。存在allow文件时其他所有人都会被禁掉。这是比维护deny更稳妥的白名单思路。监控任务执行状态重要任务备份、监控脚本、数据采集执行失败时必须能及时通知到人。可以在脚本里加失败检测检测到就通过curl调用企业微信/钉钉/邮件webhook通知。简单点也可以依赖cron的MAILTO把错误输出发给运维邮箱前提是邮件系统正常工作。定期审查任务列表每三个月把crontab -l和各服务的timer列一遍和同事确认“这些任务还需要吗”。我发现过不少“离职同事留下的临时任务还在跑”的情况有些甚至已经没有任何意义白白消耗服务器资源。滥用的crontab偶尔也会成为攻击者的持久化手段——如果一个系统被入侵过攻击者很可能在crontab里埋一个每五分钟执行一次的远程下载脚本。所以重点服务器上检查crontab -l、/etc/cron.*/目录和systemctl list-timers的输出应该成为安全巡检的常规动作。5. 几个提升效率的进阶技巧最后的这个部分放一些我平时在用的组合技巧也许能帮你少走弯路。5.1 结合Shell脚本做自重启与告警不要把“进程管理”限制在手动执行命令这层。一个合格的生产环境里服务挂了应该能自动被拉起来而不是等人发现。最简单的自愈方案是结合计划任务和健康检查脚本#!/bin/bash # 检查某个进程是否存在不存在则拉起并告警 if ! pgrep -f my_service.py /dev/null; then /opt/scripts/my_service.py /var/log/my_service.log 21 echo $(date) service down, restarted /var/log/my_service_restarts.log fi然后通过crontab每5分钟执行一次。这种方式比supervisor、systemd对某些临时进程更灵活适合一些原生不是受systemd管理的特殊进程比如某些第三方软件包。但注意这只适合无状态的服务有状态的比如数据库还靠定时脚本硬拉很容易数据损坏这种情况必须用systemd或集群方案管理。5.2 用top与ps的组合快速定位高负载元凶系统负载飙高时我的排查顺序是固定的这里直接分享出来先top看整体负载和CPU分布P按CPU排序记下最耗CPU的前几个进程的PID。ps -p PID -o pid,ppid,user,etime,cmd看这些进程的启动时间和调用者。如果是Java/Python这类动态语言的进程上下文信息不够再进/proc/PID/查看cwd、environ或者直接strace -p PID观察系统调用这个操作对线上进程有影响别乱用。如果高负载总和负载相差悬殊比如15分钟负载很高1分钟负载还低说明负载是短时间内冲上来的重点找刚启动的进程——比如某个定时任务刚刚开始跑了。这时候ps -eo pid,etime,cmd --sort-etime | head -20能直接看到“新起”的那批进程。这些动作配合起来几分钟内就基本能定位故事主角。5.3 定时任务里的日志规范最后敬告一句给计划任务养好日志习惯排查会轻松一大截。我个人的格式是每个任务的日志单独文件文件名包含任务名和日期内容至少包括执行时间、执行结果、关键错误信息。比如备份任务脚本里统一加两行echo $(date %Y-%m-%d %H:%M:%S) backup start $LOG_FILE if /usr/bin/rsync -a /data /backup; then echo backup success $LOG_FILE else echo backup failed, exit code: $? $LOG_FILE fi日后查问题看日志能直接定位“到底卡在哪一步”比登服务器手动重跑一遍高效得多。再加上日志轮转logrotate防止日志文件无限增长这套小习惯坚持下来收益非常大。我个人做了三年多运维的感受是进程管理和计划任务管理听起来是芝麻大的基础技能但它们决定了你在系统出问题时是慌张还是镇定是两小时定位还是一个命令抓住要害。把这里讲到的命令、状态、信号、互斥锁、日志规范这些一点点落实到自己服务器上你应付日常运维的底气会完全不一样。真遇到奇怪问题也别慌按着前面那些排查路径一步步走八成能很快找到答案。