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 / 调试 / 远程监控