前端排查一次Python进程重启的经验分享

Python重启排查分享

本文分享的是不同职能工种,在AI工具的辅助下,技能边界扩展的一个实践案例 主要讲述前端同学在AI辅助下,排查一次并发重启问题的经验 由于对Python和操作系统了解深度有限,若行文有误,欢迎勘误指教

背景

需求内容是,通过AI能力,进行视频的生产,而我们在新增一个功能上线后,线上部分机器出现了重启现象,本文主要记录下排查这个重启问题的过程和经验教训。

线上的并发是基于视频完整生产流程的流式调用,也就是并发的入口是从最开始的素材生产开始,直到一个视频被生产完成落库才算一个并发执行完成了(线上机器配置:1000标准核(大概物理核65核),80~100G内存,一般是28个并发)。

事件经过

时间线

第1次上线

  • 划重点功能上线后,28并发出现了多个机器重启问题
  • 当并发降低至10后,重启机器变少,但是仍有出现,而之前几乎没有机器在动画环节重启

通过对比排除,确定是新增功能引起的重启

环境:由于线上无日志和监控,无法定位明确的重启原因;前端无物理机和沙盒环境,没有复现条件

分析方向:由于缺乏有效信息,初步分析动画代码,发现单任务峰值内存>在1.4G,结合并发数和机器配置,怀疑内存溢出(OOM)

优化方向:优化了一点峰值内存使用

第2次上线

  • 重启仍出现,这次在内存监控中发现存在内存泄露问题 线上仍缺少日志,从内存监控曲线中明确存在内存泄露

环境:搭建了沙盒环境(配置减半)和物理机环境

分析方向:认为还是因为内存泄露,在物理机上并发测试发现带图内容内存泄露更严重,针对媒体文件内存泄露优化,同时修改日志打印,支持在线上能输出到架构日志中

沙盒验证:100个case 12个并发左右在沙盒上压测可跑完,14个并发会出现重启

第3次上线

  • 在线上运行的时间较长,发现并发调低之后重启仍然出现
  • 调整为1个并发之后,仍有重启
  • 线上日志发现,重启多集中在generate_audio,视频绘制阶段也有少量,但是没有崩溃日志

第4次排查

  • 关闭视频绘制阶段,仅1个并发压测generate_audio asr阶段,重启频繁,几分钟一次,但代码日志未捕获到异常内容
python 复制代码
    Fatal Python error: Segmentation fault
    Current thread 0x00007f0d2149a640 (most recent call first):
      File "/python_3916/lib/python3.9/site-packages/stable_whisper/stabilization/nonvad.py", line 53 in wav2mask
      File "/python_3916/lib/python3.9/site-packages/stable_whisper/stabilization/__init__.py", line 246 in predict_with_nonvad
      File "/python_3916/lib/python3.9/site-packages/stable_whisper/stabilization/__init__.py", line 83 in predict
      File "/python_3916/lib/python3.9/site-packages/stable_whisper/non_whisper/alignment.py", line 294 in align
      File "/python_3916/lib/python3.9/site-packages/stable_whisper/alignment.py", line 210 in align
      File "/vs_agc_producer_animation_sandbox/dps/modules/edu_video/video_production/utils/tool_subtitle.py", line 95 in asr_process

环境:沙盒环境增加了一台机器,并发执行1h左右,有一台机器复现了重启

分析方向:generate_audio是前置生成视频素材的环节,主要是使用asr模型提取关键词时长,与视频绘制属于同一个服务

排除法,先确认重启是asr模型还是视频绘制阶段问题,分段注释新增功能相关的代码进行压测看重启发生在哪个阶段:仅压测asr模型关闭视频绘制、纯视频绘制等

分析方向:在各阶段代码处,增加了系统崩溃日志捕获,用faulthandler记录进程崩溃时的堆栈信息日志

沙盒验证:仅压测asr阶段,重启现象频繁,且获取到崩溃日志

asr底层出现了Fatal Python error: Segmentation fault

明确了频繁重启来自使用的asr模型的问题

常见重启原因

OOM

What's OOM

OOM 是 "Out Of Memory" 的缩写 ,中文意思是 "内存溢出" 或 "内存不足"。OOM Killer 是 Linux 内核的"内存保护机制"。当一个程序请求使用的内存超过了系统或运行时(如 Python、JVM、操作系统等)分配给它的最大内存限制时,就会发生 OOM 错误,程序会因此崩溃或被操作系统强制终止。简单说,就是可用内存不足时,会触发OOM Killer

When to kill

  • 操作系统中有物理内存(RAM)、虚拟内存(VMS)、虚拟内存扩展(Swap)等,具体是哪种内存不足呢?
情况 OOM 会不会触发?
物理内存满了,swap 还有空间 ❌ 不会立刻 OOM,内核会尝试把内存页换到 swap
物理 + swap 都满了 ✅ 会 OOM,系统已经无计可施
分配"不可换出"内存(如内核、locked page)失败 ✅ 特殊情况会提前触发 OOM
设置 vm.overcommit_memory=2且超限 ✅ 会在 malloc 时就失败

因此OOM主要受物理内存(RAM)和交换空间(Swap)影响

OOM Kill Who

OOM杀死的是 进程 ,那么具体是什么样的进程会被OOM Kill?

比如,是不是在内存不足前最后一个申请的进程,会被OOM杀死呢?❌ 答案是否定的

OOM Killer选择"要杀谁"有一套自有的逻辑,Linux 内核通过计算一个 oom_score(或在早期版本中叫 badness score)来决定哪个进程最应该被杀掉。

OOM score 的主要影响因素:

因素 描述
内存(主要是物理内存)占用量 占用越多,score 越高(核心指标)
是否 root 用户 root 权限进程通常 score 会被下调
进程优先级oom_score_adj 某些系统服务会人为调低(如系统守护进程)
最近是否 fork 大量子进程 如果创建了大量僵尸或短命子进程,也会被打分
用户设定的保护策略 比如容器/服务手动设置 oom_score_adj降低杀死优先级
bash 复制代码
cat /proc/<pid>/oom_score
cat /proc/<pid>/oom_score_adj

总结就是:OOM Killer 杀的是"最该死的进程",不是"最后一个出事的进程"。通常是占用内存最多或优先级最低的进程

如何判断是否是OOM

被杀进程的表现
  • 程序突然崩溃退出,没有 Python/Java 异常,脚本中的 try...except 是捕获不到 OOM 的异常

如果你经常遇到程序突然"消失",建议用以下命令查看是否是 OOM:

方法 1:查看 dmesg 日志
perl 复制代码
dmesg | grep -i "killed process"

dmesg 或系统日志里能看到类似:

yaml 复制代码
Out of memory: Kill process 12345 (python) score 987 or sacrifice childKilled process 12345 (python)
total-vm: 1234567kB, anon-rss: 1000000kB
方法 2:shell 检查退出码为 137
  • Linux 中 OOM Killer 默认使用 SIGKILL 杀进程
  • echo $? 的工作原理:返回 上一个命令或进程的退出码
    • 0:表示正常退出(成功)
    • 0:表示失败或被信号终止(如 137 表示被 SIGKILL 杀掉)
  • 退出码 137 = 128 + 9 → 表示被信号 9 (SIGKILL) 杀掉
  • 单个进程查看退出码
bash 复制代码
some_command
echo $?   # 获取 some_command 的退出码
  • 使用 Shell 后台并发(&
bash 复制代码
python3 my_script.py &
pid=$!
wait $pid
echo $?
  • 使用 Python 或其他语言的并发(如 multiprocessing)
python 复制代码
from multiprocessing import Process
import os

def worker():
    print(f"PID={os.getpid()}")
    ...

p = Process(target=worker)
p.start()
p.join()
print(f"Exit code: {p.exitcode}")  # -9 表示被 SIGKILL 杀死

到这里我们明确了OOM主要是和内存使用有关,大部分的脚本代码,可以着重关心物理内存RAM的使用情况

OOM总体可以归因为两类:

内存溢出(Out of Memory): 程序在申请内存的时候,已经没有足够内存空间可使用了

内存泄露(Memory Leak):程序在申请内存后,在执行完后没有释放已申请的内存空间,多次泄露堆积后,会导致内存溢出

结合到代码中,可参考的有两类情况

  1. 部分代码运行过程中的内存峰值较高,设置并发数量过大,导致OOM
  2. 代码运行过程中有内存泄漏,长时间并发执行完,内存逐步耗尽

Python中有内存自动回收机制,依赖引用计数为主,GC 为辅 ,大多数对象表现高效,但循环引用、内存池、长期持有引用可能无法及时回收

常见导致内存异常的场景 描述 可优化思路
加载大文件,多线程/多进程场景下竞争内存 并发进程重复加载太大的数据(如大型模型、视频、数据集)进内存# 🌰 比如一次性加载的模型非常大,高达X G 模型加载到主进程后共享给子进程/线程(视资源是否可共享而定)使用 multiprocessing.Process 执行任务,主进程监控子进程,执行完后销毁子进程以回收内存。
底层服务加载模型后未及时释放聂村 某些第三方库(如 PyTorch、TensorFlow)在 线程中加载模型后不会及时释放内存。Python底层调用的C/C++服务出现异常,它无法干预由于C服务使用的内存,可能导致内存泄露🌰:如Manim库中Tex对象绘制公式时,会调用latex、ffmepg等底层服务,但绘制执行完,内存无法及时回收 限制并发数
媒体文件句柄未及时释放 文件句柄没有管理,读取之后没有及时关闭,在内存中长期驻留🌰: AudioSegment会将MP3文件都读取到内存中,内存占用大,同时由于被存储到全局变量中,所以MP3文件会长期留驻 始终使用上下文管理器 (with open() as f),with 上下文管理器在代码块结束时,会自动调用对象的 exit() 方法,可以自动关闭文件不要在线程/进程之间传递已打开的文件对象,应传路径如PIL.Image.open()、cv2.VideoCapture()、h5py.File() 等也属于"文件句柄",需要手动 .close() 或用 with 包裹
循环引用 不再使用的对象还被引用,GC 无法回收,导致内存不断增长循环引用 确认循环引用链,并去除循环引用
创建过多对象,都挂在在全局变量中 例如循环中不断创建大对象、列表、图片、Tensor 等,且把数据储存到对象的全局变量中 用临时变量或者函数传参替代闭包引用对象
递归过深 递归轮次太多,堆栈空间爆满(有时被称为 StackOverflow) 限制递归最多轮次

内存占用过高系统可能会OOM Kill,那么如果CPU使用率过高会有类似的场景吗?

什么情况代表CPU使用率高?

CPU过载,特定情况可能也会导致进程被KILL

通过 top / htop 查看进程的 %CPU:

  • 某个单进程 %CPU 持续接近 100%,表示它占满一个核心。
  • 多核时,某进程可能显示 >100%(如 200%,表示用了两个核心)。
  • 如果多个进程合计使用率接近或超过总核数,就属于高 CPU 压力状态

通常,CPU占用高时,Linux系统响应调度速度会变慢,但是在无特殊设置的情况下,系统不会主动杀死进程

  • 如果有些环境限制了单进程的CPU负载限制,超限的进程也可能被KILL
  • 进程申请CPU失败,导致内部逻辑报错,程序崩溃被KILL
  • CPU长期过载,CPU过热烧了(概率低)

Segmentation Fault

  • 错误信息:Fatal Python error: Segmentation fault
  • 原因:
    • 非 Python 层的错误(通常来自 C 扩展、C/C++绑定库)
    • 使用了 ctypescffi、NumPy、PyTorch、TensorFlow 等底层库,访问了非法内存
    • GIL(全局解释器锁)错误释放
python 复制代码
import ctypes 
ctypes.string_at(0) # 访问空指针
  • 排查方式dmesg 查看崩溃信息、faulthandler 捕获异常日志
perl 复制代码
# 推荐查看最近的崩溃日志
dmesg | grep -i segfault

非法指令(Illegal Instruction)

  • 错误信息:Illegal instruction (core dumped)
  • 原因:
  • 调用了某些非法 CPU 指令(如 AVX512 而 CPU 不支持)
  • C 扩展模块 bug
  • 排查方式 :查看 dmesg 或 core dump
    • dmesg | grep -i illegal
    • core dump 文件配合 gdb 调试

双重释放 / 错误释放资源

  • 来自 C 扩展中 free() 两次或越界释放等,导致崩溃
  • 常见来源:C/C++ 扩展模块、NumPy、TensorFlow、PyTorch、手写 C 接口

Python 崩溃堆栈中的 C/C++ 函数

  • 会看到 libpythonX.solibc.so、.so 文件中的函数崩溃
  • 表示这个异常根本不是 Python 自己的问题,而是它依赖的底层库出了错
  • 常见来源 :所有含底层绑定或 native 执行的库,如 ctypes、Cython、TensorRT、faiss 等

两类问题排查方式:启用 core dump + GDB 分析:

bash 复制代码
ulimit -c unlimited
./your_program
# 崩溃后生成 core 文件
gdb python core

参考阅读: 程序异常分析指南

如何定位异常代码

明确重启的表现

虽然本文主要是针对进程重启这种场景的排查经验,但实际排查前仍可以参考这些问题来缩小问题Scope

明确问题 举例
什么东西重启? 进程被KILL后重启、系统直接重启、容器(比如docker容器)重启?
什么场景重启? 串行时是否会重启?并发是否重启?多少数量并发会重启?重启的机器是随机还是特定机器?重启机器配置如何?重启前执行到哪?是在特定地方稳定重启吗?
重启的频率? 间隔多久重启一次?是规律的多少分钟重启?
重启前后日志或监控 关注CPU、内存监控、代码执行日志、系统崩溃日志等

日志的Tips

代码日志输出带上唯一参数

关键功能日志输出带上便于排查的业务参数,比如唯一ID、时间戳等信息

尤其是在并发场景,不同进程日志会有交叉,通过唯一ID便于区分

底层代码异常详情往外抛

核心方法内部做好try catch,保证异常的详细内容能流式抛出到日志输出层,避免调用层级深的方法的异常详情被吞,外层函数无法打印详细信息

程序崩溃日志的捕获

例如Python有的依赖库会调用底层服务,若底层服务崩溃,简单try catch捕获不到异常

需用faulthandler等工具单独捕获异常崩溃堆栈信息,捕获异常信息

AI辅助日志分析

日志信息通常很长,可以直接把日志汇总后,投给AI来对关键信息进行分析统计对比,或者绘制图标等辅助分析

内存&CPU分析工具

psutil

  • 使用psutil等工具,在不同代码阶段把进程内存和系统内存使用记录到日志中
  • 需要重点关注:进程的RSS系统的可用内存变化,定位脚本中的峰值内存情况
  • 如果缺少监控图标,可以把日志发给AI,帮你生成日志的内存数据变化的分析报告
python 复制代码
process = psutil.Process(os.getpid())
print(f"\n=== {stage_name} - 系统资源监控 ===")
# 获取内存信息
memory_info = process.memory_info()
print(f"📊 进程内存使用情况:")
print(f"   RSS (物理内存): {memory_info.rss / 1024 / 1024:.2f} MB")
print(f"   VMS (虚拟内存): {memory_info.vms / 1024 / 1024:.2f} MB")
# 系统总体内存情况
sys_memory = psutil.virtual_memory()
print(f"🌐 系统内存:")
print(f"   总内存: {sys_memory.total / 1024 / 1024 / 1024:.2f} GB")
print(f"   可用内存: {sys_memory.available / 1024 / 1024 / 1024:.2f} GB")
print(f"   使用率: {sys_memory.percent:.1f}%")

内存分析工具

使用内存分析工具:tracemallocobjgraphmemory_profiler

工具 类型 作用重点 优势 局限 适合场景
tracemalloc 内置模块 追踪文件内存分配 原生、定位精确、性能好 不显示对象结构 排查哪里分配了太多内存
objgraph 第三方库 分析对象引用关系 图形化展示引用链 性能较慢,仅对象层面 排查引用泄漏、循环引用
Objgraph

objgraph.most_common_types (limit =limit)

记录各阶段内存中最多的对象类型,通过输出各阶段内存中数量最多的对象,可以分析是哪些代码导致了内存的暴涨,但不能直接展示对象占用的内存(可以用pympler 补充展示内存信息)

yaml 复制代码
=== 题目阅读完成后内存状态 ===
Point                     33265
dict                      33178
function                  23593
tuple                     12676
Line                      7737
list                      5551
ReferenceType             4591
QuadraticBezier           4477
cell                      4177

objgraph.show_backrefs

可绘制对象引用图,可用于排查循环引用等问题 扩展阅读

填坑总结:python内存泄漏排查小技巧

tracemalloc

显示内存分配最多的10个文件:

ini 复制代码
import tracemalloc

tracemalloc.start()

# ... run your application ...

snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')

print("[ Top 10 ]")
for stat in top_stats[:10]:
    print(stat)

Python测试套件的输出示例:

ini 复制代码
[ Top 10 ]
<frozen importlib._bootstrap>:716: size=4855 KiB, count=39328, average=126 B
<frozen importlib._bootstrap>:284: size=521 KiB, count=3199, average=167 B
/usr/lib/python3.4/collections/__init__.py:368: size=244 KiB, count=2315, average=108 B
/usr/lib/python3.4/unittest/case.py:381: size=185 KiB, count=779, average=243 B
/usr/lib/python3.4/unittest/case.py:402: size=154 KiB, count=378, average=416 B
/usr/lib/python3.4/abc.py:133: size=88.7 KiB, count=347, average=262 B
<frozen importlib._bootstrap>:1446: size=70.4 KiB, count=911, average=79 B
<frozen importlib._bootstrap>:1454: size=52.0 KiB, count=25, average=2131 B
<string>:5: size=49.7 KiB, count=148, average=344 B
/usr/lib/python3.4/sysconfig.py:411: size=48.0 KiB, count=1, average=48.0 KiB

并发场景查看进程

ps命令

每隔一段时间执行 px auxww 查看进程的RSS/CPU情况,把内容保存到log文件中,日志信息大致是这样

bash 复制代码
[root@6addf2783656 video_template] ps auxww
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root     92694  212  0.1 5841524 605536 pts/20 Sl   21:10   0:55 python3 -m edu_video.video_template.generate_script -m /home/work/search/output/dps/modules/edu_video/data_process/1832575879901844001/mp3_output -md /home/work/search/output/dps/modules/edu_video/data_process/1832575879901844001 -p /home/work/search/output/dps/modules/edu_video/data_process/1832575879901844001/problem_set.json
root     92858  193  0.0 5470416 309916 pts/20 Rl   21:10   0:50 python3 -m edu_video.video_template.generate_script  -m /home/work/search/output/dps/modules/edu_video/data_process/1711411862572852054/mp3_output -md /home/work/search/output/dps/modules/edu_video/data_process/1711411862572852054 -p /home/work/search/output/dps/modules/edu_video/data_process/1711411862572852054/problem_set.json
root     92860  217  0.0 4986892 203692 pts/20 Sl   21:10   0:56 python3 -m edu_video.video_template.generate_script  -m /home/work/search/output/dps/modules/edu_video/data_process/1728829923696894961/mp3_output -md /home/work/search/output/dps/modules/edu_video/data_process/1728829923696894961 -p /home/work/search/output/dps/modules/edu_video/data_process/1728829923696894961/problem_set.json
root     93189  223  0.1 5721624 656212 pts/20 Sl   21:10   0:49 python3 -m edu_video.video_template.generate_script -m /home/work/search/output/dps/modules/edu_video/data_process/1739374529260571876/mp3_output -md /home/work/search/output/dps/modules/edu_video/data_process/1739374529260571876 -p /home/work/search/output/dps/modules/edu_video/data_process/1739374529260571876/problem_set.json
root     94405  288  0.1 5706596 625756 pts/20 Sl+  21:10   0:28 python3 -m edu_video.video_template.generate_script  -m /home/work/search/output/dps/modules/edu_video/data_process/1711411862572852054/mp3_output -md /home/work/search/output/dps/modules/edu_video/data_process/1711411862572852054 -p /home/work/search/output/dps/modules/edu_video/data_process/1711411862572852054/problem_set.json
root     94627  274  0.1 5708568 651144 pts/20 Sl+  21:10   0:19 python3 -m edu_video.video_template.generate_script  -m /home/work/search/output/dps/modules/edu_video/data_process/1727642782119859073/mp3_output -md /home/work/search/output/dps/modules/edu_video/data_process/1727642782119859073 -p /home/work/search/output/dps/modules/edu_video/data_process/1727642782119859073/problem_set.json
root     94755  301  0.1 5707336 629776 pts/20 Sl+  21:10   0:18 python3 -m edu_video.video_template.generate_script -m /home/work/search/output/dps/modules/edu_video/data_process/1728829923696894961/mp3_output -md /home/work/search/output/dps/modules/edu_video/data_process/1728829923696894961 -p /home/work/search/output/dps/modules/edu_video/data_process/1728829923696894961/problem_set.json
root     95025  393  0.0 4833380 146124 pts/20 R+   21:11   0:11 python3 -m edu_video.video_template.generate_script -m /home/work/search/output/dps/modules/edu_video/data_process/1832575879901844001/mp3_output -md /home/work/search/output/dps/modules/edu_video/data_process/1832575879901844001 -p /home/work/search/output/dps/modules/edu_video/data_process/1832575879901844001/problem_set.json
root     95026  399  0.0 4949232 141384 pts/20 Rl+  21:11   0:11 python3 -m edu_video.video_template.generate_script -m /home/work/search/output/dps/modules/edu_video/data_process/1744371703217467466/mp3_output -md /home/work/search/output/dps/modules/edu_video/data_process/1744371703217467466 -p /home/work/search/output/dps/modules/edu_video/data_process/1744371703217467466/problem_set.json
root     95321 73.5  0.0 485992 203988 pts/20  R    21:11   0:01 xelatex -no-pdf -interaction=batchmode -halt-on-error -output-directory=/home/work/search/output/dps/modules/edu_video/data_process/1728829923696894961/Tex /home/work/search/output/dps/modules/edu_video/data_process/1728829923696894961/Tex/da41e0bc14b9e98e.tex
root     95391 50.5  0.0 365452 201464 pts/20  R    21:11   0:01 xelatex -no-pdf -interaction=batchmode -halt-on-error -output-directory=/home/work/search/output/dps/modules/edu_video/data_process/1727642782119859073/Tex /home/work/search/output/dps/modules/edu_video/data_process/1727642782119859073/Tex/b5fab9d9f2cf2d88.tex
root     95394 1032  0.0 5373444 82116 pts/20  Rl+  21:11   0:10 python3 -m edu_video.video_template.generate_script -t baidu_edu -tt baidu_edu -m /home/work/search/output/dps/modules/edu_video/data_process/1726767675275195282/mp3_output -md /home/work/search/output/dps/modules/edu_video/data_process/1726767675275195282 -p /home/work/search/output/dps/modules/edu_video/data_process/1726767675275195282/problem_set.json
root     95494  621  0.0 3018188 33684 pts/20  Rl+  21:11   0:06 python3 -m edu_video.video_template.generate_script  -m /home/work/search/output/dps/modules/edu_video/data_process/1762126247169747934/mp3_output -md /home/work/search/output/dps/modules/edu_video/data_process/1762126247169747934 -p /home/work/search/output/dps/modules/edu_video/data_process/1762126247169747934/problem_set.json
root     95593 14.0  0.0  69848 17040 pts/20   R    21:11   0:00 xelatex -no-pdf -interaction=batchmode -halt-on-error -output-directory=/home/work/search/output/dps/modules/edu_video/data_process/1726740599950033618/Tex /home/work/search/output/dps/modules/edu_video/data_process/1726740599950033618/Tex/888bedebd14dc08c.tex
列名 含义(标黄为重要信息)
USER 启动该进程的用户
PID 进程 ID(Process ID)
%CPU 该进程使用的 CPU 百分比(与其它进程相比)
%MEM 该进程使用的物理内存百分比
VSZ 虚拟内存大小(Virtual Memory Size),单位 KB(包含代码段 + 数据段 + 堆 + 映射库 + 未使用空间)
RSS 实际驻留物理内存大小(Resident Set Size),单位 KB(真正占用 RAM 的部分)
STAT 是一个或多个字符的组合,用于表示进程当前的状态和特征,分为主状态和可选标志主状态:R:Running,正在运行,或在运行队列中等待运行。S:Sleeping,处于可中断睡眠状态(通常在等待某个事件,如输入)。D:Uninterruptible sleep,不可中断的睡眠状态,通常用于等待 I/O。T:Stopped,进程已停止,可能是被信号暂停(如 Ctrl+Z)或被调试器暂停。Z:Zombie,僵尸进程,进程已经结束但其父进程尚未调用 wait() 回收其状态。I:Idle,内核线程的空闲状态(Linux 2.6.23 及以后可见)。可选标志(出现在主状态后):+:进程是前台进程组的成员,通常与终端交互。<:高优先级(高调度优先级)。N:低优先级(使用了 nice 值提高友好度)。L:有 pages locked 在内存中(不能被换出)。s:是一个 session leader,通常是 shell 或启动终端的主程序。l(小写 L):多线程(使用 CLONE_THREAD 标志创建的线程)。n:进程是一个低优先级任务。W:进程没有足够的内存分页支持(已经废弃,不常见)。X:进程已经死掉(dead),这也是一种极少见的状态。K:内核线程。
START 进程启动时间(或日期)
TIME 占用 CPU 的总时间(累计 user + sys)

示例解析:

  • R+:正在运行,属于前台进程组。
  • Sl:正在休眠(sleeping),是一个多线程进程。
  • Z:僵尸进程。
  • Ds:I/O 等待中,且是一个 session leader。
  • T:进程处于暂停(例如按了 Ctrl+Z)。

常用技巧:

  • 按内存排序:ps aux --sort=-%mem
  • 按 CPU 排序:ps aux --sort=-%cpu
  • 查某个程序:ps auxww | grep python
  • 查某个用户进程:ps -u myuser
top命令

top 是一个实时监控工具,能显示当前系统的整体资源使用状态和各个进程的详细情况,适合用于:系统卡顿排查、CPU / 内存异常进程定位、进程是否进入死循环、是否出现了 I/O 阻塞、负载是否逼近系统瓶颈等问题的排查

yaml 复制代码
# 系统信息区
top - 10:53:19 up 2 days,  3:18,  3 users,  load average: 1.24, 0.75, 0.65
Tasks: 250 total,   2 running, 248 sleeping,   0 stopped,   0 zombie
%Cpu(s): 12.3 us,  1.5 sy,  0.0 ni, 84.5 id,  0.0 wa,  0.1 hi,  1.6 si,  0.0 st
MiB Mem :  15872.0 total,   420.0 free, 12200.0 used, 3252.0 buff/cache
MiB Swap:   2048.0 total,  2048.0 free,    0.0 used.  2000.0 avail Mem
# 进程信息区
PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 1234 myuser    20   0  1.2g   500m   8.5m S  75.2  3.1   1:34.32 python

系统信息区各行说明:

  • 第一行:系统时间、运行时间、登录用户数、系统负载(过去1/5/15分钟的平均负载)
  • 第二行:任务(进程)状态数量(总数、运行中、睡眠、僵尸等)
  • 第三行:CPU 使用情况(us 用户态,sy 系统态,id 空闲,wa I/O 等待)
  • 第四行:物理内存情况(总量、已用、空闲、缓存)
  • 第五行:Swap 交换分区使用情况

top 一般用于排查哪些问题?

排查目标 如何用 top实现
❗CPU 飙高 看 %CPU列,找出占用最多的进程(排序方式:按 P键)
❗内存泄漏 看 %MEM、RES列,进程是否不断增长
❗系统负载高 看第一行 load average,大于核心数说明负载过高
❗进程状态异常 看 S列:是否大量进程卡在 D(I/O 等待)或 Z(僵尸)
❗swap 使用异常 查看 Swap 行,判断是否频繁使用 swap 导致卡顿
❗进程是否活跃 查看某进程是否在 R状态,且 CPU 时间不断增长
Htop

htoptop 的增强版,支持鼠标、彩色、高亮、支持树状结构显示进程

详细的使用方法可以参考以下文档

github-htop

第 3.2 部分 - Linux 任務管理器、頂端和 htop

你一定用過 htop,但你有看懂每個欄位嗎?

Linux 的 htop 系統狀態即時監控指令工具使用教學

搬砖方法论

工作流踩坑的教训

💣 踩坑1:

第一次发现重启时没有及时留存日志,因此机器日志、线上执行日志、监控日志都没有,只有一个现象描述;同时脚本在Mac未复现,前端侧缺少Linux环境,也无沙盒环境可验证

物理机、沙盒依赖后端和架构,人力紧张,有一定推动成本,所以没有第一时间去推动环境搭建,就先自顾自看代码分析了

推动问题复现是第一要义

磨刀不误砍柴工,先复现问题,找到原因比一股脑分析更重要!

如果推动有困难,可尝试及时上升,让更高层的人来决定优先级

💣 踩坑2:

一开始一直以为动画阶段仅包含前端实现的视频绘制代码,一直埋头苦查,直到上线之后发现异常出现在generate_audio前后,才发现动画阶段其实还有其他环节代码

看问题前,先问清楚问题涉及的上下游和Scope

又是一个磨刀不误砍柴工的问题

明确问题发生时涉及的上下游,问清楚问题功能所包含的所有流程,而不仅仅是自己负责的这块代码

💣 踩坑3:

由于第二次的内存监控中明确出现了内存泄露,想当然的认为是动画阶段内存泄露,依然还是按这个方向排查优化,如果仔细分析重启前的内存占比只有40%,可能就能从OOM的视角转移

💣 踩坑4:

前期对代码进行了大量分析,但是依然很难定位到具体的问题。最终确定根因其实还是依靠对比排除法,单独压测asr之后发现了问题

有问题 != 找到根本问题

千头万绪的排查过程中,突然发现一些问题的时候,迷惑性确实大,但是很多问题只是表象问题,并不是根因

与其着急解决,不如先验证它是不是异常的根因

排除法是简单高效的必杀技

在明确了代码和问题的Scope之后,分阶段排除法验证问题,其实是最高效定位问题的方法

AI辅助建议

现在的AI工具的Coding能力确实非常强大,所以即使是完全陌生的领域,通过AI的辅助也可以很好的完成需求。

但是当需求变得复杂、问题更加深入,尤其是涉及性能优化、线上异常这些深水区问题,AI的局限性也会越来越明显。

尤其是,AI本身也存在错误、幻觉问题,那在自己也不了解的领域,怎么尽可能最大化AI的助力呢?

未知问题,不要偏信单一模型的回答,多模型对比

同样的问题,不同模型的答案会有不同侧重点,而且不同模型对于模糊Prompt的拆解能力也有区别

我个人主要使用,Claude 4、GPT 4o、Grok、Deepseek

  • Cursor + Claude 4 :对日常需求效果已经很好,但复杂问题的拆解效果略逊于GPT 4o
  • 让GPT 4o拆解方案,发给Claude 4来实现代码效果更好

AI也跟人一样,会偷懒,倾向于不费脑的答案,所以Prompt的时候,最好优先预设方案的倾向性

在让AI分析内存泄露问题时,不管哪个模型都会倾向于先给出,在事后强制清理对象的方案,因为根本不是根因,所以这个方案实际上是无效的

而对于导致内存泄漏代码的优化,需要再Prompt里面明确指定让它如何避免问题产生,才会往问题代码解决的思路上回答

做好代码多版本备份,尽量少用Accept All,AI生成代码越来越高效,但是屎山代码概率也成倍增加,做好备份多一份保障

未知方向先用搜索引擎查阅真实的技术分享,找思路

比如重启排查,先看下别人分享的重启问题的真实的排查经验,对于工具链和方向有个大体思路

对大模型的回答进行反问,问题细化,检验回答可靠性

比如我问GTP 4o OOM中的内存超限,它回答是通过Swap+物理内存判定的,通过搜索引擎的技术文章试验里面是主要指标关注的其实是物理内存,把文章发给GPT 4o,进行诘问

GPT 4o分析后,指出了核心区别是计算OOM Score主要是靠物理内存,但是不代表Swap是完全无用的

AI无法解决的问题,回归搜索引擎,扩展搜索关键词仍有惊喜

在实现Python绘制二维时,由于样式变化比较大,AI的代码无法精细化实现样式,框架封装的样式也不支持

最终通过关键词在github的评论区找到了自定义样式的实现方式

相关推荐
葫芦和十三23 分钟前
破局与重构:关于 UGC 平台多身份账号体系的架构思考
架构·ai编程·领域驱动设计
ASDyushui1 小时前
Kubernetes 架构原理与集群环境部署
容器·架构·kubernetes
望获linux1 小时前
【实时Linux实战系列】实时系统的安全性架构
java·linux·服务器·开发语言·架构·嵌入式软件
闲猫3 小时前
WEB安全架构
安全·web安全·架构
艾特小小4 小时前
spring-cloud微服务部署-feign服务间调用
java·spring boot·spring cloud·微服务·架构
摸鱼仙人~5 小时前
Spring Boot 分层架构详解:Controller、Service、Mapper...
spring boot·后端·架构
强哥之神6 小时前
一文深入:AI 智能体系统架构设计
深度学习·语言模型·架构·llm·transformer·ai agent
森焱森6 小时前
高端伺服驱动“ARM+FPGA”架构的技术
arm开发·单片机·算法·fpga开发·架构
发发喜欢用AI6 小时前
什么是MoE?MoE 架构详解
架构