作为一名在嵌入式领域摸爬滚打十年的老鸟,我深知Linux系统移植和驱动开发是每个嵌入式工程师的必修课。今天要分享的这套练习题,正是我当年入门时反复练习过的经典题型,涵盖了从基础命令到内核编译的核心知识点。不同于普通教程的泛泛而谈,我会结合真实项目经验,逐题解析背后的技术逻辑和常见陷阱。
以常见的ARM Cortex-A9开发板为例,使用USB转串口工具连接时,需要注意:
bash复制# minicom基本配置命令
sudo apt install minicom
sudo minicom -s
# 进入Serial port setup后配置:
# Serial Device: /dev/ttyUSB0
# Bps/Par/Bits: 115200 8N1
# Hardware Flow Control: No
安装arm-linux-gnueabihf工具链后,必须验证其ABI兼容性:
bash复制arm-linux-gnueabihf-gcc -v
# 检查输出中是否有--with-arch=armv7-a --with-fpu=vfpv3-d16
重要提示:若开发板使用Cortex-M系列,需改用arm-none-eabi工具链,两者不可混用
练习题中涉及的SUID权限,在实际项目中常用于:
设置SUID的正确方式:
bash复制chmod u+s /bin/my_program # 正确
chmod 4755 /bin/my_program # 正确但易混淆
chmod +s /bin/my_program # 危险!会同时设置SUID和SGID
在嵌入式系统中,硬链接能节省inode但有限制:
bash复制ln /mnt/nand/rootfs/busybox /bin/ls # 跨文件系统失败!
ln -s /mnt/nand/rootfs/busybox /bin/ls # 软链接可跨文件系统
实测数据:在JFFS2文件系统上创建1000个硬链接,inode使用量不变,而软链接会消耗额外inode。
针对嵌入式设备的内核配置要点:
makefile复制CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_DRIVER=y
CONFIG_DEBUG_GPIO=n # 生产环境必须关闭!
遇到"Error: DTC returns 1"时,按以下步骤排查:
dts复制/ {
serial0: serial@101f0000 { ... }; // 正确
serial1: serial@101f0000 { ... }; // 地址重复!
};
bash复制dtc -I dts -O dtb -o test.dtb test.dts
现代Linux驱动推荐使用cdev接口:
c复制static struct file_operations my_fops = {
.owner = THIS_MODULE,
.read = my_read,
.write = my_write,
.open = my_open,
.release = my_release
};
static int __init my_init(void)
{
alloc_chrdev_region(&devno, 0, 1, "mydev");
cdev_init(&my_cdev, &my_fops);
cdev_add(&my_cdev, devno, 1);
return 0;
}
易错点:忘记设置.owner会导致模块卸载时kernel panic
延时任务的处理方案对比:
| 方案 | 精度 | 休眠特性 | 适用场景 |
|---|---|---|---|
| 内核线程 | 高 | 可深度休眠 | 硬件轮询 |
| 工作队列 | 中 | 不可休眠 | 小任务调度 |
| 定时器 | 低 | 不可休眠 | 超时处理 |
实测案例:在i.MX6ULL上,内核线程的调度延迟<50μs,而工作队列可能达到200μs。
当内核崩溃时,按以下顺序排查:
bash复制arm-linux-gnueabihf-objdump -d vmlinux | grep -A 20 "80123456"
在资源受限的嵌入式系统中,推荐kmemleak而非valgrind:
bash复制echo scan > /sys/kernel/debug/kmemleak # 手动触发检测
cat /sys/kernel/debug/kmemleak # 查看报告
关键配置项:
makefile复制CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000 # 根据内存调整
当/etc/init.d/rcS未执行时,按步骤检查:
bash复制file rcS # 查看文件类型
dos2unix rcS # 转换格式
当多个驱动争用GPIO时,推荐使用gpiod接口:
c复制// 申请GPIO
struct gpio_desc *desc = gpiod_get(dev, "led", GPIOD_OUT_LOW);
// 配置为输出
gpiod_direction_output(desc, 1);
// 释放GPIO
gpiod_put(desc);
相比旧的gpio_request,gpiod支持:
将内核启动从15s优化到3s的实测方法:
makefile复制CONFIG_PRINTK_TIME=n # 关闭时间戳
CONFIG_CC_OPTIMIZE_FOR_SIZE=n # 启用O2优化
bash复制mke2fs -O ^has_journal /dev/mmcblk0p2 # 禁用ext4日志
bash复制/etc/init.d/network start &
/etc/init.d/ssh start &
wait # 关键服务同步点
使用smem分析进程内存:
bash复制smem -P "busybox" -t -k # 监控busybox内存
输出解读:
在256MB RAM的设备上,USS应控制在总内存的70%以内。
可靠的双备份升级流程:
code复制/dev/mmcblk0p1: Bootloader
/dev/mmcblk0p2: Kernel_A
/dev/mmcblk0p3: Rootfs_A
/dev/mmcblk0p4: Kernel_B
/dev/mmcblk0p5: Rootfs_B
bash复制fw_setenv bootpart 2:3 # 切换到A系统
fw_setenv bootpart 4:5 # 切换到B系统
c复制if (crc32(fw_buf) != expected_crc) {
rollback_firmware();
}
硬件看门狗配置要点:
c复制static struct watchdog_info ident = {
.identity = "My Watchdog",
.firmware_version = 1,
.options = WDIOF_KEEPALIVEPING,
};
static struct file_operations wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = wdt_write,
.unlocked_ioctl = wdt_ioctl,
.open = wdt_open,
.release = wdt_release,
};
喂狗策略建议:
掌握基础后,建议按以下顺序深入:
推荐实操项目:
记得每次修改内核配置后,保存一份.config副本:
bash复制make savedefconfig
cp defconfig arch/arm/configs/my_defconfig