4、Linux系统基础:嵌入式Linux开发必备知识回顾
嵌入式Linux开发与纯应用开发或服务器运维有着本质区别:开发者面对的是资源受限的硬件平台、交叉编译工具链、设备树(Device Tree)、内核模块加载以及底层设备驱动交互。在瑞芯微RK系列平台(如RK3568、RK3588)上,无论是进行系统移植、BSP开发还是上层应用优化,扎实的Linux基础都是不可或缺的。本章将系统梳理嵌入式Linux开发中必须掌握的命令行操作、文件系统结构、进程管理机制以及设备驱动模型,帮助开发者建立从“应用层”到“内核态”的完整知识链路。
上图展示了嵌入式Linux开发的核心架构。从宏观视角看,开发者始终在“用户空间-内核空间-硬件层”三者之间穿梭。用户空间的应用程序通过系统调用(如open、read、ioctl)陷入内核,内核中的虚拟文件系统(VFS)将请求路由到具体的设备驱动,驱动最终操作硬件寄存器完成数据收发。理解这条链路,是后续进行RK平台外设调试、性能优化和驱动开发的基础。
4.1 命令行与Shell:嵌入式开发的“第一语言”
核心原理:BusyBox与交叉编译环境下的Shell差异
在PC上我们习惯使用bash或zsh,但在嵌入式目标板上(尤其是基于Buildroot或Yocto构建的系统),默认Shell往往是BusyBox提供的ash。BusyBox是一个精简的Unix工具集,它将常用的ls、cp、ps、mount等命令整合进一个单一可执行文件中,以节省存储空间。这意味着某些命令的选项可能被裁剪(例如ps可能不支持-e,而只支持ps aux)。此外,开发环境与目标环境的架构不同(x86_64 vs aarch64),因此必须使用交叉编译器(如aarch64-linux-gnu-gcc)编译程序,并通过NFS、TFTP或串口传输到目标板执行。
操作流程:从PC到目标板的调试会话
- 建立连接:使用串口(
minicom或picocom)或网络SSH登录目标板。 - 文件传输:通过
scp或tftp将交叉编译好的可执行文件拷贝到目标板/tmp目录。 - 权限调整:执行
chmod +x app赋予执行权限。 - 运行与调试:前台运行
./app,或使用gdb附加调试。
# 在PC上交叉编译
aarch64-linux-gnu-gcc -o hello hello.c
# 拷贝到目标板(假设IP为192.168.1.100)
scp hello root@192.168.1.100:/tmp/
# 在目标板上执行
chmod +x /tmp/hello
/tmp/hello
案例拆解:排查“命令找不到”问题
背景:在RK3568板卡上运行一个脚本,提示./test.sh: line 3: xxd: not found。分析:目标板BusyBox未包含xxd命令。此时不应盲目安装,而应检查脚本中xxd的具体用途。若仅用于十六进制转储,可用od -A x -t x1z替代。操作:修改脚本,将xxd替换为od命令,重新执行。复盘:嵌入式环境命令集有限,编写脚本时应优先使用POSIX标准命令,避免依赖PC特有工具。
mount -t nfs 192.168.1.10:/work /mnt),这样无需反复拷贝文件,直接运行NFS目录下的可执行文件,极大提升调试效率。4.2 文件系统:从Flash布局到目录结构
核心原理:嵌入式文件系统的分层与只读/可写分区
嵌入式Linux的文件系统并非单一实体,而是由多个分区组成,每个分区可能有不同的格式和挂载属性。典型的RK平台分区包括:uboot(引导加载器)、boot(内核与DTB)、rootfs(根文件系统)、userdata(用户数据)。根文件系统通常使用ext4或squashfs(只读压缩格式),而userdata分区使用ext4或f2fs以支持读写。理解/proc和/sys这两个虚拟文件系统至关重要:/proc输出内核内部数据结构(如/proc/cpuinfo、/proc/interrupts),/sys则提供设备驱动模型的视图(如/sys/class/gpio)。
| 目录 | 用途 | 嵌入式注意事项 |
|---|---|---|
| /bin, /sbin | 基本命令与系统管理命令 | 通常为BusyBox符号链接,不可随意删除 |
| /dev | 设备节点 | 使用devtmpfs自动创建,也可手动mknod |
| /proc | 内核参数与进程信息 | 只读挂载,用于动态调试 |
| /sys | 设备驱动模型 | 可读写部分属性,用于运行时配置 |
| /etc | 配置文件 | 需持久化,通常放在可写分区或overlayfs |
| /var | 日志与临时文件 | 链接到/tmp或tmpfs,重启丢失 |
操作流程:查看与修改分区挂载
# 查看当前挂载情况
mount
# 查看分区表
cat /proc/partitions
# 重新挂载根文件系统为读写(开发阶段)
mount -o remount,rw /
# 挂载一个ext4分区到/data
mount /dev/mmcblk0p5 /data
常见误区:误改只读分区导致系统无法启动
初学者常试图直接修改/etc下的文件,但根文件系统可能是只读挂载的。正确做法是:在开发阶段将根文件系统挂载为可写(mount -o remount,rw /),或者使用overlayfs将修改层叠加到只读层之上。若直接强制写入,可能导致文件系统损坏,进而无法启动。总结:操作前先确认挂载属性,修改关键文件前备份原文件。
/data或/tmp分区。4.3 进程管理:理解调度、优先级与守护进程
核心原理:Linux进程模型与嵌入式实时性
Linux是分时操作系统,默认调度器为CFS(完全公平调度器)。但在嵌入式场景中,我们往往需要保证某些关键任务的实时性。RK平台支持PREEMPT_RT补丁,可将内核配置为实时内核。进程管理的关键概念包括:进程优先级(nice值,范围-20到19)、调度策略(SCHED_OTHER, SCHED_FIFO, SCHED_RR)以及进程状态(运行、睡眠、停止、僵尸)。在嵌入式调试中,ps、top、kill是最常用的命令。
操作流程:定位CPU占用率过高的进程
- 执行
top命令,按P键按CPU使用率排序。 - 记录高占用进程的PID。
- 使用
cat /proc/PID/status查看进程详细信息。 - 若为异常进程,使用
kill -9 PID强制终止。
# 查看所有进程(包含内核线程)
ps aux
# 动态监视进程状态
top -d 1
# 查看特定进程的线程
ps -T -p PID
案例拆解:RK3588多核CPU负载均衡问题
背景:在RK3588(8核,4×A76+4×A55)上运行视频编解码任务,发现A76核心负载100%,而A55核心空闲。分析:默认CFS调度器可能未将任务迁移到空闲核心,或者任务绑定了CPU亲和性。操作:使用taskset -pc 0-3 PID将进程绑定到A76集群,或使用sched_setaffinity系统调用。结果:通过合理分配任务到不同核心,整体吞吐量提升20%。复盘:在异构多核平台上,必须考虑CPU的容量差异,必要时手动设置亲和性或使用cgroup进行资源隔离。
kill -l列出所有信号;kill -s SIGKILL PID发送特定信号;nohup使进程忽略挂断信号,常用于后台启动服务。4.4 设备驱动:从模块加载到与硬件交互
核心原理:Linux设备驱动模型与字符设备
设备驱动是内核与硬件之间的桥梁。在RK平台上,驱动开发主要涉及三类设备:字符设备(如串口、GPIO)、块设备(如eMMC)、网络设备(如以太网MAC)。驱动通过file_operations结构体向用户空间提供open、read、write、ioctl等接口。设备树(Device Tree)是描述硬件信息的配置文件,内核通过解析DTB(Device Tree Blob)来匹配驱动与设备节点。
操作流程:加载/卸载内核模块
# 加载模块
insmod my_driver.ko
# 查看已加载模块
lsmod
# 查看内核日志
dmesg | tail -20
# 卸载模块
rmmod my_driver
案例拆解:编写一个简单的GPIO字符设备驱动
目标:控制RK3568上的一个LED灯。步骤:1. 在设备树中定义GPIO节点(gpio-leds);2. 编写驱动,在probe函数中获取GPIO资源并注册字符设备;3. 实现write回调,根据用户传入的0/1值控制GPIO电平。关键代码:
static ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) {
char kbuf[4];
if (copy_from_user(kbuf, buf, count)) return -EFAULT;
if (kbuf[0] == '1') gpiod_set_value(led_gpio, 1);
else gpiod_set_value(led_gpio, 0);
return count;
}
验证:在用户空间执行echo 1 > /dev/led0,观察LED点亮。复盘:驱动开发的核心在于理解设备树与驱动模型的匹配机制,以及正确使用内核API(如gpiod_get、devm_request_irq)。
ioremap将物理地址映射为虚拟地址。此外,中断处理函数中不能调用可能导致睡眠的函数(如kmalloc),应使用tasklet或workqueue。4.5 本章总结与实战要点
嵌入式Linux开发是一个系统工程,本章回顾的命令行、文件系统、进程管理和设备驱动知识是后续所有RK平台开发工作的基石。在实际项目中,建议开发者养成以下习惯:第一,始终使用交叉编译工具链,并注意目标板与PC的架构差异;第二,熟悉/proc和/sys文件系统,它们是内核与用户空间交互的窗口;第三,在调试驱动时,充分利用dmesg和ftrace跟踪内核行为;第四,理解设备树是RK平台硬件初始化的关键,任何外设的添加或修改都离不开对DTB的调整。掌握这些基础,你将能够自信地面对RK3568、RK3588等复杂SoC的BSP开发与系统优化任务。