跳转至

第 1 章 Linux 与 Ubuntu 系统地图

机器人上位机的操作系统几乎清一色是 Ubuntu。装完系统、进入桌面就开始干活之前,有几张底图必须先画清楚:Linux 是内核而不是操作系统;Ubuntu 的发行版号与内核号分两条编号线;用户态程序进内核的唯一常规入口是系统调用;目录不是随便分的,FHS 约束每个顶层目录的语义;版本自查有三把各管一段的尺子;终端本身也是一类设备。这张地图决定后续排障能不能定位到具体一层——"网络不通"是内核路由还是 userspace NetworkManager、"GUI 起不来"是驱动还是显示服务器、"服务起不来"是配置还是终端环境,都要求先把这几层分开看。

1. Linux 是内核,不是操作系统

严格意义上 "Linux" 只指 Linus Torvalds 1991 年发起维护的内核(kernel),它负责进程调度、内存管理、文件系统、设备驱动与系统调用(system call,用户态进入内核态的唯一常规入口)。

一个可被日常使用的操作系统还需要一整套用户态组件:C 标准库(glibc 或 musl)、核心工具集(GNU coreutils,ls / cp / cat 这类)、Shell(Bash、Dash)、初始化系统(systemd)、包管理器(apt / dpkg)、桌面与图形栈(X.Org 或 Wayland、GNOME)等。这些组件大多来自 GNU 项目,所以严格称 "GNU/Linux"。

发行版(distribution,distro)做的是"组装与集成":挑选一组用户态组件、绑定一个内核版本、按目标场景调整默认配置、提供持续更新的包仓库。Debian、Ubuntu、Fedora、Arch 都是发行版;它们共用同一个 Linux 内核主线,差别在用户态组件与运维策略。

Ubuntu 的层级关系:

层 组件 由谁维护
内核 Linux kernel 上游 Linus 树 + Canonical 补丁(linux-* 系列包)
系统库 glibc、OpenSSL、libstdc++ Debian 上游 + Canonical 打包
初始化与服务 systemd systemd 项目 + Canonical 默认配置
包管理 apt / dpkg Debian 项目(Ubuntu 沿用)
桌面环境 GNOME / KDE 等 上游项目 + Ubuntu 默认 GNOME
安装器与云镜像 subiquity / cloud-init Canonical

Canonical(Ubuntu 的商业母公司)从 Debian 拉取上游、按 LTS 冻结包版本并回迁安全补丁(backport),再发布桌面版、服务器版、Core、Cloud Image 等多种形态。这解释了一个常见现象:LTS 冻结的是包版本,不是安全边界——同一个包名在 22.04 与 24.04 上的版本可能差着两三代上游。

内核、用户态与发行版的分层关系:

flowchart TB
    distro["发行版:挑选用户态组件 + 绑定内核 + 提供包仓库"]
    subgraph user["用户态(GNU 系组件为主)"]
        libc["C 标准库 glibc"]
        core["GNU coreutils"]
        init["systemd"]
        pkg["apt / dpkg"]
        gui["X.Org / Wayland + GNOME"]
    end
    kernel["Linux 内核<br/>调度 / 内存 / 驱动 / 系统调用"]
    hw[("硬件")]
    distro --> user
    user -->|"系统调用:唯一常规入口"| kernel
    kernel --> hw

2. Ubuntu 版本编号与内核策略

Ubuntu 的"版本"是两套独立编号在同时走:发行版号与内核号。混谈这两条线会导致大量"明明 Ubuntu 一样、行为却不同"的困惑。

2.1 发行版:数字版本 + 字母代号

发行版每半年发布一次,命名规则 YY.MM:2022 年 4 月即 22.04、2024 年 10 月即 24.10。每个版本还配一个"形容词 + 动物"代号,首字母按 a→b→c 递增:

  • 22.04 Jammy Jellyfish(LTS)
  • 24.04 Noble Numbat(LTS)
  • 24.10 Oracular Oriole
  • 25.04 Plucky Puffin(本板块基线外)

LTS(Long Term Support)每两年 4 月发布一次,桌面侧默认支持 5 年、服务器侧通过 Ubuntu Pro 可延至 10 年。本板块以 22.04 LTS(jammy)为主基线、24.04 LTS(noble)为对照基线,与 ROS 2 Humble、Jazzy 两个 LTS 配对同步。

2.2 GA 内核与 HWE 内核

同一 Ubuntu 版本可以换内核,这是理解"uname -r 与发行版号不一一对应"的关键:

  • GA 内核(General Availability):随该版本发布时锁定的内核版本,整个支持周期只打安全补丁、不升特性。
  • HWE 内核(Hardware Enablement):每半年从更新的 Ubuntu 版本 backport 一份内核,用于新硬件(GPU、网卡、笔记本平台)开箱可用。
Ubuntu GA 内核 HWE 内核滚动范围
22.04 5.15 5.17 → 5.19 → 6.2 → 6.5 → 6.8(终点即 24.04 GA)
24.04 6.8 6.11(滚动至 24.10 GA)

linux-image-generic 装 GA、linux-image-generic-hwe-22.04 装 HWE。双基线纪律在正文里意味着:写任何与内核版本相关的结论(PREEMPT_RT 支持、eBPF BTF 可用性、SocketCAN 行为)都要同时注明 Ubuntu 版本与 uname -r 输出,只写"22.04"不够。

2.3 内核变体包

Ubuntu 的内核包不止一个:

包名 语义 与本板块的关系
linux-image-generic GA 通用内核 默认安装
linux-image-generic-hwe-22.04 HWE 通用内核 桌面版默认拉取
linux-image-lowlatency 低延迟调度、非硬实时 「实时内核与 CPU 调优」章前对照
linux-image-rt / linux-image-rt-generic PREEMPT_RT 补丁版 22.04 起该变体的厂商支持形态属外部证据,未核实前只写机制不写安装路径

坑:发行版号和内核号是两条编号线

Ubuntu 22.04 说的是发行版,5.15 说的是内核;HWE 线还能让 22.04 跑更新的内核。查问题前先分清要查的是哪一条编号。

3. 用户态与内核态的分界

Linux 通过 CPU 的特权级(x86 上 ring 3 与 ring 0)把内存与指令划成两个世界:

  • 用户态:应用程序、Shell、systemd、Python、ROS 2 节点、apt 进程都在这里。
  • 内核态:内核代码、中断服务、调度器、虚拟文件系统层(VFS)与 dentry 缓存。

用户态程序进内核的唯一常规入口是系统调用(syscall 指令触发;read / write / openat / execve 都是)。异常与中断也会陷入内核,但那不是程序主动发起。C 标准库的 read(2) 一类 POSIX API 只是 syscall 的薄封装——观察一个程序"在跟内核要什么",最直接的手段就是追 syscall。

## 用 strace 记录 `ls /tmp` 到底向内核请求了什么(-c 只汇总调用次数)
strace -c ls /tmp

Ubuntu 22.04(内核 5.15)上典型输出片段(/tmp 内容不同会影响 getdents64 次数,重点是类别与 errors 计数):

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 44.43    0.000319         106         3           getdents64   # 批量读目录项
 15.32    0.000110           2        43        36 openat       # 试开共享库与目录,多数 miss
 13.09    0.000094           4        19           mmap         # 建立进程地址空间与映射共享库
  9.19    0.000066           1        38        27 newfstatat   # 逐文件取元信息
  2.65    0.000019           3         5           read         # 从文件系统读
  1.39    0.000010           1         6           write        # 输出到 stdout
  0.14    0.000001           1         1           execve       # 装载 ls ELF
------ ----------- ----------- --------- --------- ----------------
100.00    0.000718           4       155        70 total

155 次调用里 70 次带 error——openat 与 newfstatat 的高命中率 miss 就是动态链接器 ld.so 逐目录探索共享库的痕迹(/etc/ld.so.cache 未命中时回退到默认目录逐一尝试)。一行 ls 尚且要 155 次 syscall,一个 ROS 2 节点的每次收发、每帧图像落盘都跑在这条链上。

控制回路的实时性抖动往往就出在这里:用户态一次 malloc 触发缺页(page fault 走内核态)、printf 走 write 阻塞在磁盘、序列化分配大 buffer 被 OOM killer 干掉——能读 strace 与 /proc/<pid>/smaps,才能把"抖"定位到具体的用户态调用。strace 本身在第 3 章「调试三条切入线」再展开;mlockall、CPU 亲和性与优先级这条链在第 7 章补完。

4. 文件系统布局与 FHS

Linux 的目录树不是随便分的。FHS(Filesystem Hierarchy Standard,现行 3.0)约束每个顶层目录承担什么、哪些可只读挂载、哪些可跨主机共享、哪些重启必须清空。Ubuntu 是 FHS 的合规实现;下表按机器人上位机的实际用途整理:

目录 语义 机器人上位机场景
/usr 主机间可共享、可只读;含绝大部分二进制与库 系统升级与回滚的操作对象;/usr/bin/python3 在这里
/bin /sbin Ubuntu 18.04 起是 /usr/bin /usr/sbin 的软链(usrmerge) 老脚本里的 /bin/bash 实际解析到 /usr/bin/bash
/etc 主机特定配置,不放二进制 .service 单元(/etc/systemd/system/)、udev rules、apt 源、sshd_config
/var 可变数据;跨主机不可共享 日志(/var/log)、apt 缓存、rosbag 落盘、模型权重
/opt 第三方包独立安装 Jetson SDK Manager 与商业软件常落这里
/dev 设备节点(字符 / 块设备);tmpfs,重启重建 /dev/ttyUSB0 /dev/video0 /dev/can0;权限在第 4 章、生成规则在第 9 章
/proc procfs,内核视图,不占磁盘;进程与内核参数 /proc/<pid>/status /proc/interrupts /proc/version;观测入口在第 8 章
/sys sysfs,内核对象树;设备驱动与属性 /sys/bus/usb/devices/ /sys/class/net/;udev 规则基于这里
/run 运行时数据,tmpfs,重启清空 systemd socket、PID 文件、/run/user/<uid>
/tmp 临时文件,可被 systemd-tmpfiles 定期清理 不放长期 bag 与模型;重启或几天后可能消失
/home 用户数据 用户级 .bashrc、.vimrc、conda 环境目录

三条常见误判:

  • /tmp 当万能临时区:systemd-tmpfiles-clean.timer 默认十天未访问的文件会被清;长期数据放 /var/tmp 或 /var/lib 更稳。
  • /var/log 永远不占地方:journal 与 syslog 在无人维护时会写满磁盘(第 10 章的 journalctl --vacuum 与 logrotate 处理这条)。
  • /etc 改了立刻生效:systemd 需要 daemon-reload、NetworkManager 需要 reload、udev rules 需要 udevadm control --reload——每一处都有自己的生效动作,第 4、6、9 章各自说明。

5. 版本自查的三把尺子

现场问"这台机器什么版本",不同命令回答的是不同的问题:内核、发行版、编译信息各在一处。

## 三把尺子各查各的:
uname -r                       # 内核版本与架构(不查发行版)
lsb_release -a                 # 发行版全称与代号(依赖 lsb-release 包)
cat /etc/os-release            # 标准化字段(systemd 提供,最稳)
  • uname -a 回答内核本身。输出 Linux <hostname> 5.15.0-119-generic #129-Ubuntu SMP ... x86_64:5.15.0 是主线内核、-119 是 Ubuntu 打的补丁序号、generic 是内核变体(对到 GA / HWE / -rt)。查是不是 -rt 或 -lowlatency 都靠这里。
  • lsb_release -a 回答发行版:Distributor / Description / Release / Codename 四项。精简系统(Docker 镜像、板端最小 rootfs)常常没装 lsb-release,命令直接 not found;此时退回 /etc/os-release。
  • /etc/os-release 是 systemd 定义的标准文件(systemd.os-release(5)),字段稳定、脚本可 source:
## 直接 source 到当前 shell,读标准化变量
. /etc/os-release
echo "$ID $VERSION_ID $VERSION_CODENAME"
## 输出:ubuntu 22.04 jammy

判据:排障要一次问三处。只 lsb_release 会漏内核变体,只 uname 会漏发行版代号,只 os-release 看不到 PREEMPT_RT 补丁标记。CI 脚本与工单模板里三行都要贴。

判据:三把尺子各查一段

lsb_release//etc/os-release 查发行版,uname -r 查运行内核,apt list --installed 'linux-image*' 查装了哪些内核——三段合起来才是一次完整自查。

6. tty、pts 与终端环境

Linux 把"终端"也当成设备,出现在 /dev 下。机器人上位机经常无显示器、SSH 进来干活、tmux 会话里跑脚本——搞清楚自己在哪种终端上,能解释不少"为什么这个命令在这里行为不一样"。

  • tty(teletype):最初指物理电传机;内核把虚拟控制台与物理串口都归一为 tty 设备。虚拟控制台是 /dev/tty1…/dev/tty6(Alt+F1…F6 切换),物理串口是 /dev/ttyS0(主板 COM)、/dev/ttyAMA0(ARM 板载)、/dev/ttyUSB0(USB 转串口,第 9 章)。
  • pts(pseudo-terminal slave):伪终端,成对出现——一个 master 在内核侧(由 sshd / xterm / tmux 持有),一个 slave 是用户 shell 打开的 /dev/pts/N。SSH 登录后拿到的不是物理 tty 而是 pts;容器里 docker exec -it 也是 pts。
  • $TERM:告诉 TUI 程序当前终端能力(xterm-256color / screen-256color / tmux-256color)。程序据此查 terminfo 数据库决定怎么画颜色、光标移动;CI 环境常见 TERM=dumb。

现场查一行搞定:

tty                       # 当前 stdin 绑到的终端设备:/dev/pts/2 或 /dev/tty1
echo "$TERM"              # 终端类型字符串
loginctl session-status   # 当前登录会话:tty / x11 / wayland / ssh

systemd 用 getty@.service 与 serial-getty@.service 模板在每个 tty 上拉起登录提示;板端无显示器时关掉不需要的 tty、只在串口或 SSH 上留一个入口,是常见部署动作(第 10 章 systemd 部署范式)。

7. 要点

  • "Linux 是内核不是操作系统"——真正在跑的 Ubuntu 由内核 + 系统库 + 服务层 + 应用层若干层组合而成,每层由不同上游维护;出问题时先问"落在哪一层"再谈具体工具。
  • 发行版号与内核号是两条线:22.04 GA 内核 5.15、HWE 内核可滚到 6.8;同名 Ubuntu 下不同机器可能跑不同内核,写结论必须两处都注明。
  • 用户态进内核走系统调用:strace 是能读的最小窗口;能把"服务抖一下"定位到 mmap 缺页或 write 阻塞,才谈得上第 7 章实时调优与第 8 章观测工具链。
  • FHS 约束目录语义:/usr 可只读、/etc 主机特定、/var 可变但会写满、/dev 重启重建、/proc /sys 不占磁盘、/tmp 会被 systemd-tmpfiles 清;长期 bag 不放 /tmp。
  • 版本自查三把尺子:uname -a(内核与变体)、lsb_release -a(发行版)、/etc/os-release(标准化字段)各查各的;排障一次贴三处。
  • 终端也是设备:tty(物理与虚拟控制台)与 pts(SSH / 容器)不是一回事,$TERM 决定 TUI 行为;无显示器部署要在 systemd getty 模板层做减法。

参考文献与延伸阅读

  • Ubuntu. Server 文档. 文档
  • systemd. 手册与文档. 文档
  • Linux Kernel. 管理指南. 文档