Linux 系统调试学习2

1、ps、top、htop 分别干什么?

先记这个区别:

复制代码
ps
= 看"当前这一刻"的进程快照

top
= 实时刷新系统进程状态

htop
= 更友好的交互式 top

所以:

复制代码
想快速查一个进程有没有启动
→ ps

想持续观察 CPU / 内存
→ top

想更方便地看线程、排序、搜索
→ htop

ps aux

常见列:

复制代码
USER
PID
%CPU
%MEM
VSZ
RSS
STAT
START
COMMAND

你不用全背。

现阶段重点:

复制代码
PID
%CPU
%MEM
RSS
STAT
COMMAND

R
= running / runnable

S
= sleeping

D
= uninterruptible sleep

T
= stopped

Z
= zombie

如果一个网络接收线程对应的进程大多数时间:

复制代码
S

很正常。

因为它可能正在:

复制代码
recv
poll
epoll_wait
condition_variable

等待事件。


zombie

僵尸进程:

复制代码
Z

不是说进程还在正常执行。

而是:

子进程已经退出,但父进程还没有回收它的退出状态。

可以理解:

复制代码
child 已经死了
↓
内核还保留一小份退出信息
↓
parent 还没 wait()

所以 zombie:

复制代码
通常不占大量 CPU / 内存

但如果大量出现:

复制代码
说明父进程的子进程管理有问题

运行:

复制代码
top

通常上面会看到:

复制代码
load average
Tasks
CPU
Memory
Swap

下面是进程列表。

你现阶段优先看:

复制代码
%CPU
%MEM
RES
PID
COMMAND

比如:

复制代码
PID    %CPU   %MEM   RES    COMMAND
4321   98.5   2.1    300M   gmr_node
4500    3.0   1.0    120M   robot_output

那第一反应:

复制代码
gmr_node 明显是 CPU 热点

然后继续查:

复制代码
线程
函数

如果只关心一个进程:

复制代码
top -p 4321

这样界面干净很多。

特别适合现场:

复制代码
只盯 robot_control

top -H -p 4321

意思:

看这个进程里的线程。

例如:

复制代码
TID    %CPU
4321    5%
4322   95%
4323    1%
4324    3%

马上知道:

复制代码
线程 4322

最忙。

然后下一步可以:

复制代码
perf
gdb
strace

继续看它在干什么。

这就是你之前 perf 那节的链路:

复制代码
进程高
↓
top -H
↓
线程高
↓
perf
↓
函数高

某线程 100%

往往意味着:

这个线程几乎一直在执行,没有怎么阻塞。

如果它本来应该:

复制代码
等待 UDP

结果却 100%,就很可疑。

可能是:

复制代码
busy loop
EAGAIN 重试
轮询错误
while(true)

htop 的价值

htop 比 top 更友好。

运行:

复制代码
htop

一般可以:

复制代码
搜索进程
按 CPU 排序
按内存排序
展开线程
树状查看父子关系

对初学者很适合。

比如你可以更直观看:

复制代码
ros2 launch
├── node_a
├── node_b
└── node_c

这种父子关系。


ps 看线程

例如:

复制代码
ps -T -p 4321

或者:

复制代码
ps -L -p 4321

可以看到线程。

如果想更详细:

复制代码
ps -L -p 4321 -o pid,tid,psr,pcpu,stat,comm

其中:

复制代码
tid
= 线程 ID

psr
= 当前/最近运行在哪个 CPU 上

pcpu
= CPU 占用

这会和后面的:

复制代码
CPU affinity

直接连接。


怎么知道进程什么时候启动?

可以:

复制代码
ps -o pid,lstart,etime,cmd -p 4321

其中:

复制代码
lstart
= 启动时间

etime
= 已运行多久

这很适合排查:

复制代码
程序是不是刚刚自动重启过?

例如你以为它运行 8 小时,

结果:

复制代码
etime = 00:02:15

说明:

复制代码
2 分钟前刚重启

那就要继续查:

复制代码
systemd
崩溃
watchdog

查看 CPU 核数?

简单:

复制代码
nproc

例如:

复制代码
8

说明有 8 个逻辑 CPU。

也可以:

复制代码
lscpu

看更详细信息。


查看进程树

常用:

复制代码
pstree -p

你可能看到:

复制代码
systemd(1)
 └─ros2(3000)
    ├─mocap_node(3100)
    ├─gmr_node(3101)
    └─robot_output(3102)

这对 ROS2 launch/systemd 服务排查非常直观。

复制代码
pgrep -a robot_control

找到 PID。

然后:

复制代码
ps -o pid,ppid,etime,stat,%cpu,%mem,rss,args -p PID

一次看:

复制代码
PID
父进程
运行时间
状态
CPU
内存
完整启动参数

这个非常适合机器人现场。


只看网络相关系统调用

复制代码
strace -e trace=network -p 4321

这样重点看:

复制代码
socket
bind
connect
accept
send
recv

只看文件系统调用

例如配置文件加载失败:

复制代码
strace -e trace=file ./robot_control

你可能看到:

复制代码
openat(..., "/etc/robot/config.yaml", ...) = -1 ENOENT

这说明:

复制代码
No such file or directory

可能程序其实在错误目录找配置。

这类问题非常常见。


futex

你以后 strace 非常容易看到:

复制代码
futex(...)

先不要怕。

它和:

复制代码
mutex
condition_variable
线程同步

密切相关。

可以粗略理解:

Linux 用户态线程在等待锁或条件变化时,底层经常会使用 futex。

所以如果程序卡住,多个线程都:

复制代码
futex(... FUTEX_WAIT ...)

你应该开始怀疑:

复制代码
是不是在等 mutex / condition_variable

甚至可能:

复制代码
deadlock

-T 特别适合看谁卡得久

例如:

复制代码
recvfrom(...) <0.000012>
openat(...)   <0.000030>
connect(...)  <10.001234>

一看:

复制代码
connect

花了 10 秒。

所以:

strace 不只是看"调用了什么",还能帮助看"系统调用卡了多久"。


保存 strace 日志

例如:

复制代码
strace -o trace.log -p 4321

日志保存到:

复制代码
trace.log

现场问题特别适合。

还可以:

复制代码
strace -ff -o trace -p 4321

多线程/子进程场景会生成多个 trace 文件。

现在知道即可。

复制代码
程序崩了
→ gdb / core dump

程序卡了
→ strace + gdb

CPU高
→ top -H + perf

内存涨
→ RSS + ASan/Valgrind

系统调用异常
→ strace

线程竞争
→ TSan

这就是你现在的完整排障工具链。


2、systemd

systemd = Linux 上用来管理系统服务和进程生命周期的服务管理器。

它能帮你做:

复制代码
开机自启
↓
启动程序
↓
监控是否退出
↓
异常时自动重启
↓
统一日志
↓
正常 stop
↓
管理依赖关系

所以机器人上的:

复制代码
robot_driver
controller
diagnostics
network_bridge

很适合交给 systemd 管。

复制代码
&
= shell后台运行

而:

复制代码
systemd
= 服务生命周期管理

完全不是一个层级。

假设文件放在:

复制代码
/etc/systemd/system/robot_control.service

然后:

复制代码
sudo systemctl daemon-reload

告诉 systemd:

unit 文件更新了,重新加载。

启动:

复制代码
sudo systemctl start robot_control

查看:

复制代码
systemctl status robot_control

停止:

复制代码
sudo systemctl stop robot_control

重启:

复制代码
sudo systemctl restart robot_control

这几个非常常用。


Type=simple

先简单理解:

systemd 认为 ExecStart 启动的进程就是服务主进程。

对于很多普通机器人程序:

复制代码
Type=simple

已经够用。

以后还会看到:

复制代码
notify
forking
oneshot

现在不用展开。

对于:

复制代码
Type=simple

systemd 直接跟踪:

复制代码
ExecStart

启动的进程。

所以:

复制代码
systemctl status robot_control

能看到:

复制代码
Main PID

这也是为什么前面说:

复制代码
启动脚本里使用 exec

会让进程关系更干净。

3、Linux

Linux 权限决定"哪个用户/进程,可以对哪个资源做什么"。

dialout 组

复制代码
sudo usermod -aG dialout $USER

意思:

把当前用户加入 dialout 组。

之后重新登录,让组信息生效。

再:

复制代码
groups

确认有:

复制代码
dialout

这时候你的进程就可以:

复制代码
以普通用户身份
↓
访问 /dev/ttyUSB0

比:

复制代码
sudo ./robot_driver

更合理。

udev

udev = Linux 用来管理设备节点和设备事件的一套机制。

比如你插入 USB 串口:

复制代码
USB 设备插入
↓
Linux 内核识别
↓
udev 处理
↓
创建 /dev/ttyUSB0
↓
设置 owner / group / mode
↓
甚至创建稳定别名

所以:

复制代码
/dev/ttyUSB0

不是你手工创建的。

很多时候是:

复制代码
udev

帮你创建和配置。

udev rule 是一组规则:

当某种设备出现时,应该怎么命名、怎么设权限、属于哪个组。

规则一般放在:

复制代码
/etc/udev/rules.d/

例如:

复制代码
99-robot.rules

为什么 udev 比 chmod 更适合工程部署?

因为:

复制代码
插拔设备
↓
udev 自动匹配
↓
自动应用权限

你不需要每次:

复制代码
sudo chmod ...

所以:

复制代码
chmod
= 临时人工操作

而:

复制代码
udev rule
= 可重复、可部署、自动化配置

这就是工程上的区别。

怎么查 USB 设备的 Vendor/Product ID?

最常用:

复制代码
lsusb

例如:

复制代码
Bus 001 Device 005: ID 0403:6015 Future Technology Devices...

这里:

复制代码
0403

是 vendor ID。

复制代码
6015

是 product ID。

这类信息可以用于写 udev rule。


udevadm info

比如:

复制代码
udevadm info -a -n /dev/ttyUSB0

它会输出设备属性。

你可以找到:

复制代码
idVendor
idProduct
serial
manufacturer

然后选择稳定属性写规则。


udev rule 改完怎么生效?

常见:

复制代码
sudo udevadm control --reload-rules

然后:

复制代码
sudo udevadm trigger

或者直接重新插拔设备。

再:

复制代码
ls -l /dev/robot_imu

确认规则是否生效。如果规则没生效怎么查?

可以:

复制代码
udevadm info -a -n /dev/ttyUSB0

确认你匹配的属性到底在哪一层。

因为:

复制代码
ATTR

和:

复制代码
ATTRS

含义有差别。

现阶段不需要钻语法细节。

你先建立排障流程:

复制代码
设备存在吗?
↓
ls -l 权限是什么?
↓
udevadm info 看属性
↓
rule 是否匹配?
↓
reload
↓
重新插拔

capability

Linux 不只有:

复制代码
普通用户
root

两档。

还可以把 root 的能力拆成很多:

复制代码
capabilities

比如:

复制代码
CAP_NET_ADMIN
CAP_SYS_NICE
CAP_IPC_LOCK

这些以后实时控制也会碰到。

例如:

复制代码
SCHED_FIFO

高实时优先级可能需要:

复制代码
CAP_SYS_NICE

mlockall 某些配置下可能涉及:

复制代码
MEMLOCK limit

网络接口配置可能涉及:

复制代码
CAP_NET_ADMIN

所以:

能力可以细分授权,而不是所有事都 sudo root。


目录权限里的 x 是什么意思?

这里很容易踩坑。

对普通文件:

复制代码
x

一般是:

复制代码
execute

但对目录:

复制代码
x

更接近:

允许进入/穿过这个目录。

例如:

复制代码
/home/robot/config/

即使:

复制代码
robot.yaml

本身可读,

但上级目录没有执行权限:

复制代码
程序还是可能打不开

所以权限问题不能只看目标文件。


udev 还能做什么?

除了:

复制代码
设置权限
创建 symlink

它还可以:

复制代码
根据设备插拔触发规则
设置属性
设备命名

不过机器人项目里你现阶段最重要的是:

复制代码
稳定命名
+
访问权限

这两个


4、权限

ip addr:先看网卡和 IP

最常用:

复制代码
ip addr

或者:

复制代码
ip a

你会看到类似:

复制代码
2: enp3s0:
    inet 192.168.1.10/24

这里:

复制代码
enp3s0
= 网卡接口名

192.168.1.10
= IP

/24
= 子网前缀

机器人现场第一步经常就是:

目标网卡到底有没有正确 IP?


ip link:看网卡是不是 UP

执行:

复制代码
ip link

可能看到:

复制代码
enp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP>

重点:

复制代码
UP

表示接口被启用。

复制代码
LOWER_UP

通常说明物理链路也起来了。

如果没有:

复制代码
LOWER_UP

你就要怀疑:

复制代码
网线
交换机
网口
设备供电

而不是先查应用程序。


手工启用网卡

例如:

复制代码
sudo ip link set enp3s0 up

关闭:

复制代码
sudo ip link set enp3s0 down

这只是接口状态控制。

不是配置 IP。


临时设置静态 IP

例如:

复制代码
sudo ip addr add 192.168.1.10/24 dev enp3s0

注意这是:

复制代码
临时配置

重启后通常不会永久保留。

生产系统一般用:

复制代码
NetworkManager
netplan
systemd-networkd

等做持久配置。

ping 到底验证了什么?

例如:

复制代码
ping 192.168.1.20

如果通:

复制代码
说明 IP 层基本可达

但注意:

ping 通 ≠ UDP/TCP 应用一定正常。

因为 ping 用的是:

复制代码
ICMP

不是:

复制代码
TCP
UDP

所以只能说明:

复制代码
网络路径大体可达

不能证明:

复制代码
你的端口打开
程序在监听
防火墙没拦 UDP
应用协议正确

为什么 ping 不通,也不一定说明完全不通?

有些设备或防火墙:

复制代码
禁止 ICMP

但:

复制代码
TCP/UDP 业务端口仍然正常

所以:

复制代码
ping

只是网络排查工具之一。

不能作为唯一判断。


ss:查端口最重要

你前面已经见过:

复制代码
ss -ltnp

看 TCP 监听。

拆开:

复制代码
-l
= listening

-t
= TCP

-n
= 不解析名字

-p
= 显示进程

例如:

复制代码
LISTEN 0 128 0.0.0.0:5555 users:(("robot_server",pid=4321,fd=8))

说明:

复制代码
robot_server
PID 4321
fd 8
正在监听 TCP 5555

ss -lunp

其中:

复制代码
-u
= UDP

比如:

复制代码
0.0.0.0:7014

说明程序已经 bind 7014。

这对你的动捕 UDP 排障特别关键。


0.0.0.0 是什么意思?

如果服务绑定:

复制代码
0.0.0.0:7014

一般表示:

接受本机多个 IPv4 接口上发到 7014 的流量。

如果绑定:

复制代码
192.168.1.10:7014

那通常只监听这个地址。

所以:

复制代码
bind 地址

也是通信是否正常的重要因素。


127.0.0.1 是什么意思?

复制代码
127.0.0.1

是:

复制代码
localhost
回环地址

如果服务只绑定:

复制代码
127.0.0.1:5555

那么:

复制代码
本机程序可以访问
远端机器通常访问不了

这是一个非常常见的配置问题。


TCP 怎么简单测端口?

如果你想确认:

复制代码
192.168.1.20:5555

能不能建立 TCP 连接,可以用:

复制代码
nc -vz 192.168.1.20 5555

成功可能看到:

复制代码
succeeded

这说明:

复制代码
TCP 路径和服务监听基本正常

UDP 为什么不能像 TCP 那么简单验证?

因为 UDP:

复制代码
没有连接建立过程

你发一个包:

复制代码
不一定有回应

所以:

复制代码
"我 sendto 成功了"

只能说明:

本地内核接受了这个数据发送请求。

不代表:

复制代码
对端一定收到了

这就是 UDP 排查为什么更依赖抓包和应用层统计。


ethtool

例如:

复制代码
sudo ethtool enp3s0

可以看:

复制代码
Link detected
Speed
Duplex
Auto-negotiation

例如:

复制代码
Speed: 1000Mb/s
Duplex: Full
Link detected: yes

对于机器人有线网络很实用。


Full Duplex

简单理解:

复制代码
Full Duplex
= 可以同时收和发

现代以太网通常都是全双工。

如果协商异常:

复制代码
速度或双工不一致

可能导致:

复制代码
大量重传
吞吐下降
奇怪网络问题

比如服务:

复制代码
机器人电脑 192.168.1.10

如果你在本机测试:

复制代码
nc 127.0.0.1 5555

成功。

不能证明:

复制代码
远端 192.168.1.20

也能连接。

还需要测试:

复制代码
nc 192.168.1.10 5555

并从另一台机器实际测试。


一个典型 UDP 动捕排障流程

假设:

复制代码
动捕发送端
→ 192.168.10.20:7014

你的机器人:

复制代码
应该接收

第一步:

复制代码
ip addr

确认机器人网卡是不是:

复制代码
192.168.10.x/24

第二步:

复制代码
ip link

确认:

复制代码
UP
LOWER_UP

第三步:

复制代码
ss -lunp

确认:

复制代码
7014

真的被你的进程 bind。

第四步:

复制代码
strace -e trace=network -p PID

看:

复制代码
recvfrom

有没有数据返回。

这样就能逐层缩小问题。


TCP Connection refused

例如:

复制代码
connect(...)
→ Connection refused

通常意味着:

复制代码
目标 IP 可达
但目标端口上没有服务监听

或者明确被拒绝。

这和:

复制代码
Connection timed out

不一样。

复制代码
设备网络
→ EtherCAT / CAN / UDP 传感器

机器人内部通信
→ ROS2 / ZMQ

外部网络
→ SSH / Wi-Fi / 调试 / 远程监控
相关推荐
Dawson Zhu1 小时前
《Agentic Design Patterns》第 18 章导读:Guardrails 安全模式
人工智能·语言模型·架构·aigc·agi
探客木木夕1 小时前
结构超图重构神经网络模型:脉冲权重节点与四阶段闭环框架
人工智能·深度学习·神经网络
龙虾电话1 小时前
语音机器人打断不进去:VAD 双阈值迟滞里一个共用的 bool,和它引发的连锁误判
linux
honsor1 小时前
机房IP化改造首选:以太网温湿度传感器,PoE供电+Modbus TCP一网搞定
linux·运维·服务器
地理探险家1 小时前
从零实现图片自动分类:数据准备、模型训练与单图预测
人工智能·深度学习·机器学习·分类·数据挖掘·数据集
星创易联1 小时前
PLC数据采集与远程运维平台:从边缘计算到集中监控
大数据·运维·边缘计算
亲笔签1 小时前
“场景重庆”典型案例 | 数字笔迹技术赋能政务服务 打造可信电子签署创新场景
大数据·人工智能
马剑威(威哥爱编程)1 小时前
【共创稿事节】HarmonyOS 7 Agent 开发实战 04:云端 RAG 搭企业私有知识库问答
人工智能·华为·harmonyos
snhna1 小时前
DeerFlow 2.x 源码拆解:多智能体委派、Skills、沙箱与 Linux 部署实战
人工智能·python