一、嵌入式Linux开发基础
1. 嵌入式Linux系统架构
典型组成要素:
Bootloader (U-Boot) → Linux Kernel → Root Filesystem → Applications硬件抽象层 系统服务层 用户空间
2. 交叉编译工具链搭建
安装ARM工具链示例:
# Ubuntu/Debian
sudo apt install gcc-arm-linux-gnueabihf binutils-arm-linux-gnueabihf# 验证工具链
arm-linux-gnueabihf-gcc --version# 编译Hello World
arm-linux-gnueabihf-gcc -o hello hello.c
二、Linux设备驱动开发
1. 字符设备驱动框架
基础驱动代码:
#include <linux/module.h>
#include <linux/fs.h>#define DEVICE_NAME "mychardev"static int major_num;
static int device_open = 0;static int dev_open(struct inode *inode, struct file *file) {if (device_open) return -EBUSY;device_open++;return 0;
}static ssize_t dev_read(struct file *file, char __user *buf, size_t len, loff_t *offset) {const char *msg = "Hello from kernel!\n";size_t msg_len = strlen(msg);if (*offset >= msg_len) return 0;size_t to_copy = min(len, msg_len - *offset);if (copy_to_user(buf, msg + *offset, to_copy))return -EFAULT;*offset += to_copy;return to_copy;
}static struct file_operations fops = {.open = dev_open,.read = dev_read,
};static int __init chardev_init(void) {major_num = register_chrdev(0, DEVICE_NAME, &fops);if (major_num < 0) {printk(KERN_ALERT "Failed to register device\n");return major_num;}printk(KERN_INFO "Registered char device major %d\n", major_num);return 0;
}static void __exit chardev_exit(void) {unregister_chrdev(major_num, DEVICE_NAME);printk(KERN_INFO "Unregistered char device\n");
}module_init(chardev_init);
module_exit(chardev_exit);
MODULE_LICENSE("GPL");
2. 设备树(Device Tree)集成
DTS节点示例:
/ {compatible = "myboard,rev1";mydevice: mydevice@0x12340000 {compatible = "vendor,mydevice";reg = <0x12340000 0x1000>;interrupt-parent = <&gic>;interrupts = <0 23 4>; // SPI 23, level-sensitivestatus = "okay";};
};
驱动中解析DTS:
static int probe(struct platform_device *pdev) {struct resource *res;void __iomem *regs;res = platform_get_resource(pdev, IORESOURCE_MEM, 0);regs = devm_ioremap_resource(&pdev->dev, res);int irq = platform_get_irq(pdev, 0);if (irq < 0) return irq;const char *prop;if (!device_property_read_string(&pdev->dev, "compatible", &prop))printk(KERN_INFO "Device compatible: %s\n", prop);return 0;
}
三、用户空间与内核交互
1. sysfs接口实现
static ssize_t value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {return sprintf(buf, "%d\n", current_value);
}static ssize_t value_store(struct kobject *kobj,struct kobj_attribute *attr,const char *buf, size_t count) {int ret = kstrtoint(buf, 10, ¤t_value);if (ret < 0) return ret;return count;
}static struct kobj_attribute value_attr = __ATTR(value, 0664, value_show, value_store);static int __init sysfs_init(void) {struct kobject *kobj = kobject_create_and_add("mymodule", kernel_kobj);if (!kobj) return -ENOMEM;int err = sysfs_create_file(kobj, &value_attr.attr);if (err) {kobject_put(kobj);return err;}return 0;
}
2. ioctl接口设计
用户空间头文件:
// mydevice.h
#define MYDEVICE_IOC_MAGIC 'k'#define MYDEVICE_GET_VALUE _IOR(MYDEVICE_IOC_MAGIC, 0, int)
#define MYDEVICE_SET_VALUE _IOW(MYDEVICE_IOC_MAGIC, 1, int)#define MYDEVICE_IOC_MAXNR 1
驱动实现:
static long mydevice_ioctl(struct file *file, unsigned int cmd,unsigned long arg) {int err = 0, tmp;switch (cmd) {case MYDEVICE_GET_VALUE:if (copy_to_user((int __user *)arg, ¤t_value, sizeof(int)))return -EFAULT;break;case MYDEVICE_SET_VALUE:if (copy_from_user(&tmp, (int __user *)arg, sizeof(int)))return -EFAULT;current_value = tmp;break;default:return -ENOTTY;}return err;
}
四、中断处理与并发控制
1. 中断处理实现
static irqreturn_t my_interrupt_handler(int irq, void *dev_id) {struct my_device *dev = dev_id;u32 status = readl(dev->regs + REG_STATUS);if (!(status & INT_MASK))return IRQ_NONE;// 清除中断writel(status & INT_MASK, dev->regs + REG_STATUS);// 处理中断wake_up_interruptible(&dev->waitq);return IRQ_HANDLED;
}static int probe(struct platform_device *pdev) {int irq = platform_get_irq(pdev, 0);int ret = request_irq(irq, my_interrupt_handler,IRQF_SHARED, "mydevice", dev);if (ret) {dev_err(&pdev->dev, "Failed to request IRQ\n");return ret;}
}
2. 内核同步机制
自旋锁示例:
static DEFINE_SPINLOCK(data_lock);static ssize_t data_write(struct file *file, const char __user *buf,size_t len, loff_t *ppos) {unsigned long flags;spin_lock_irqsave(&data_lock, flags);// 临界区操作spin_unlock_irqrestore(&data_lock, flags);return len;
}
完成量示例:
static DECLARE_COMPLETION(comp);static int reader_thread(void *arg) {wait_for_completion(&comp);// 继续执行...return 0;
}static void trigger_operation(void) {// 启动操作...complete(&comp);
}
五、内存管理与DMA
1. 内核内存分配
// 小内存分配(kmalloc)
void *small_buf = kmalloc(256, GFP_KERNEL);
if (!small_buf) return -ENOMEM;
kfree(small_buf);// 大内存分配(vmalloc)
void *large_buf = vmalloc(8192);
if (!large_buf) return -ENOMEM;
vfree(large_buf);// 获取DMA内存
void *dma_buf = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL);
if (!dma_buf) return -ENOMEM;
dma_free_coherent(dev, size, dma_buf, dma_handle);
2. 用户空间内存映射
static int mmap(struct file *filp, struct vm_area_struct *vma) {unsigned long size = vma->vm_end - vma->vm_start;// 拒绝过大映射if (size > BUFFER_SIZE) return -EINVAL;// 映射内核内存到用户空间if (remap_pfn_range(vma, vma->vm_start,virt_to_phys(kernel_buffer) >> PAGE_SHIFT,size, vma->vm_page_prot))return -EAGAIN;return 0;
}
六、实战项目:GPIO驱动开发
1. GPIO子系统集成
#include <linux/gpio/consumer.h>struct my_gpio_device {struct gpio_desc *led_gpio;struct gpio_desc *button_gpio;int button_irq;
};static int gpio_probe(struct platform_device *pdev) {struct my_gpio_device *dev;dev->led_gpio = devm_gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);if (IS_ERR(dev->led_gpio))return PTR_ERR(dev->led_gpio);dev->button_gpio = devm_gpiod_get(&pdev->dev, "button", GPIOD_IN);if (IS_ERR(dev->button_gpio))return PTR_ERR(dev->button_gpio);dev->button_irq = gpiod_to_irq(dev->button_gpio);if (dev->button_irq < 0)return dev->button_irq;return request_irq(dev->button_irq, button_handler,IRQF_TRIGGER_RISING, "mybutton", dev);
}
2. LED控制类实现
static struct led_classdev my_led = {.name = "mydev:green",.brightness_set = led_set,.brightness_get = led_get,.default_trigger = "heartbeat",
};static void led_set(struct led_classdev *led_cdev,enum led_brightness value) {gpiod_set_value(dev->led_gpio, value ? 1 : 0);
}static int led_probe(struct platform_device *pdev) {int err = led_classdev_register(&pdev->dev, &my_led);if (err) {dev_err(&pdev->dev, "Failed to register LED\n");return err;}return 0;
}
七、调试与性能分析
1. 内核调试技术
printk级别控制:
# 查看当前日志级别
cat /proc/sys/kernel/printk# 动态调整级别
echo "6 4 1 7" > /proc/sys/kernel/printk# 内核配置持久化
kernel.printk = 6 4 1 7
动态调试:
# 启用特定文件的调试信息
echo "file drivers/mydriver/* +p" > /sys/kernel/debug/dynamic_debug/control# 查看当前调试设置
grep mydriver /sys/kernel/debug/dynamic_debug/control
2. Ftrace使用
# 启用函数跟踪
echo function > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_on# 跟踪特定函数
echo my_driver_* > /sys/kernel/debug/tracing/set_ftrace_filter# 捕获跟踪数据
cat /sys/kernel/debug/tracing/trace_pipe > trace.log
八、Bootloader定制
1. U-Boot环境变量
# 查看环境变量
printenv# 设置启动参数
setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait# 保存环境
saveenv# 自定义命令
setenv myboot 'fatload mmc 0:1 ${loadaddr} zImage; bootz ${loadaddr}'
2. 移植U-Boot
板级支持文件:
// board/myboard/myboard.c
int board_init(void) {/* 初始化GPIO */gpio_request(CONFIG_LED_GPIO, "sys_led");gpio_direction_output(CONFIG_LED_GPIO, 1);/* 设置DRAM参数 */gd->bd->bi_dram[0].start = PHYS_SDRAM_1;gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;return 0;
}int dram_init(void) {gd->ram_size = get_ram_size((long *)PHYS_SDRAM_1, PHYS_SDRAM_1_SIZE);return 0;
}
九、根文件系统构建
1. BusyBox定制
# 配置BusyBox
make menuconfig# 重要配置选项:
# - CONFIG_STATIC: 静态链接
# - CONFIG_FEATURE_INSTALLER: 支持安装
# - CONFIG_ASH: 启用shell
# - CONFIG_IFCONFIG: 网络工具# 编译安装
make -j4
make CONFIG_PREFIX=/myrootfs install
2. 最小根文件系统布局
/myrootfs
├── bin -> busybox
├── dev
│ ├── console
│ ├── null
│ └── ttyS0
├── etc
│ ├── inittab
│ └── passwd
├── lib
│ └── ld-linux-armhf.so.3
├── proc
├── sbin -> busybox
├── sys
└── usr
十、系统集成与部署
1. Yocto项目构建
本地配置示例:
# 初始化环境
git clone git://git.yoctoproject.org/poky
cd poky
source oe-init-build-env# 添加meta层
bitbake-layers add-layer ../meta-mylayer# 构建核心镜像
bitbake core-image-minimal# 定制软件包
echo 'IMAGE_INSTALL_append = " mypackage"' >> conf/local.conf
2. 固件更新机制
OTA更新流程:
// 更新脚本示例
#!/bin/sh# 验证签名
openssl dgst -verify public.pem -signature update.sig update.bin || exit 1# 解压更新包
tar xzf update.bin -C /tmp/update || exit 1# 执行预更新脚本
/tmp/update/pre-update.sh || exit 1# 更新内核
flash_erase /dev/mtd1 0 0
nandwrite -p /dev/mtd1 /tmp/update/zImage# 更新根文件系统
mke2fs /dev/mtdblock2
mount /dev/mtdblock2 /mnt
tar xzf /tmp/update/rootfs.tar.gz -C /mnt# 执行后更新脚本
/tmp/update/post-update.sh
总结与进阶
1. 嵌入式Linux开发技能栈
- 硬件接口:
- 寄存器编程
- 设备树配置
- 时钟/中断管理
- 内核开发:
- 驱动模型
- 内存管理
- 进程调度
- 系统集成:
- Bootloader定制
- 根文件系统构建
- 系统裁剪优化
- 调试技术:
- KGDB调试
- 性能分析
- 崩溃转储分析
2. 推荐学习路径
- 初级阶段:
- 通过LED驱动理解字符设备框架
- 学习procfs/sysfs接口创建
- 掌握设备树基本语法
- 中级阶段:
- 开发完整的SPI/I2C设备驱动
- 实现内核模块参数配置
- 学习DMA缓冲管理
- 高级阶段:
- 研究电源管理框架(PM)
- 开发自定义文件系统
- 优化启动时间与内存占用
- 专家方向:
- 参与主线内核开发
- 定制实时内核补丁(RT_PREEMPT)
- 开发安全增强模块(SELinux)