动手写 Linux 内核模块与驱动从 hello 到 platform 总线srvD本篇基于 srvDphase6真实实操。我们写了三个内核模块第一个模块hellomod、字符设备globalmem、以及一套 platform 总线设备/驱动亲眼看到insmod/rmmod时的内核日志、/dev下的字符设备节点以及 platform 总线撮合设备与驱动时probe的调用时机。1. 环境准备驱动编译依赖当前运行内核的头文件$uname-aLinux ecs-bee4-c6af-00046.8.0-106-generic#106-Ubuntu SMP PREEMPT_DYNAMIC x86_64$apt-getinstall-ybuild-essential kmodlinux-headers-$(uname-r)$ls/lib/modules/$(uname-r)/build/|head-8每个模块用标准Makefilemake -C $(KDIR) M$(PWD) modules调用内核构建系统。2. 第一个内核模块 hellomod模块源码里用module_param导出参数who和timesinit里打印加载信息exit里打印卸载信息。$cd/root/lab/phase6/hellomodmakemake-C/lib/modules/6.8.0-106-generic/buildM/root/lab/phase6/hellomod modules CC[M]/root/lab/phase6/hellomod/hellomod.o LD[M]/root/lab/phase6/hellomod/hellomod.ko $ insmod hellomod.kowho嵌入式学员times2$dmesg|tail-4[9496.886426]hellomod: 模块被加载了!(init)[9496.886431]hellomod: Hello, 嵌入式学员!(1/2)[9496.886433]hellomod: Hello, 嵌入式学员!(2/2)通过sysfs还能读出模块参数当前值$cat/sys/module/hellomod/parameters/who 嵌入式学员 $cat/sys/module/hellomod/parameters/times2卸载时内核日志显示干净的退出$ rmmod hellomoddmesg|tail-2[9497.397667]hellomod: 模块被卸载了!(exit)小提示编译时有一条 “the compiler differs from the one used to build the kernel” 只是信息性提示本机 gcc 与构建内核的 gcc 小版本一致不影响加载。3. 字符设备驱动 globalmemglobalmem是一个经典字符设备申请设备号、实现read/write、cdev_add后在/dev下生成节点。$ insmod globalmem.kodmesg|tail-3[9498.481937]globalmem: 申请到设备号major239minor0[9498.482029]globalmem: 驱动加载完成, /dev/globalmem 就绪 $ls-l/dev/globalmem crw-------1root root239,0Aug1418:36 /dev/globalmem $grepglobalmem /proc/devices239globalmem用户态直接当文件读写验证字符设备的顺序读写语义$echoHello 字符设备驱动! 这是用户态写给内核驱动的数据/dev/globalmem $cat/dev/globalmem Hello 字符设备驱动!这是用户态写给内核驱动的数据 $printf第二次写入覆盖前面的内容/dev/globalmemcat/dev/globalmem 第二次写入覆盖前面的内容globalmem内部用一块内核缓冲区后一次写覆盖前一次——这就是最简字符设备的样子。卸载后/dev/globalmem节点随之消失。4. platform 总线设备与驱动如何撮合platform 总线是嵌入式 SoC 上挂设备GPIO、I2C、SPI 控制器等的标准模型。我们的platdemo包含platdev.ko注册一个名叫demo-led的平台设备和platdrv.ko注册匹配该设备的驱动。场景一先注册设备后注册驱动——驱动注册时总线立即撮合调用probe$ insmod platdev.koinsmod platdrv.kodmesg|tail-5[9500.643310]platdrv:probe 成功!匹配到设备 demo-led[9500.643313]platdrv: 平台数据:namedemo-led-1gpio66[9500.643315]platdrv:(真实驱动会在这里完成 GPIO 申请/时钟使能等硬件初始化)场景二反过来先注册驱动后注册设备——设备注册时才触发probe$ insmod platdrv.koinsmod platdev.kodmesg|tail-4[9501.188115]platdrv:probe 成功!匹配到设备 demo-led[9501.188120]platdrv: 平台数据:namedemo-led-1gpio66两种顺序都能probe成功且gpio66平台数据正确传递——这正是 platform 总线设备树/板文件描述资源驱动匹配后取用的核心机制。卸载时remove被调用资源释放$ rmmod platdrv;rmmod platdev platdrv:remove: 释放设备 demo-led 的资源 platdev: 平台设备 demo-led 已注销5. 小结对象关键 API观察点模块module_init/exit、module_paramdmesg看加载/卸载日志/sys/module/name/parameters看参数字符设备register_chrdev/cdev_add/dev下生成节点/proc/devices列主设备号platformplatform_device/platform_driver、probe设备与驱动谁后到probe就在谁注册时触发写驱动的乐趣在于你写的代码跑在内核态一个insmod就能让dmesg吐出你自己的日志/dev下多出一个你定义的设备。系列收官四台服务器的实验到此全部跑通并验证srvAShell/文件IO多文件 Makefile、静态/动态库、forkexecvp迷你 Shell ✅srvB进程/线程/IPC管道/FIFO/消息队列、共享内存竞态、互斥锁/条件变量、select/epoll✅srvC交叉编译/QEMUARM 与 x86 双启动踩平软链接、BINFMT、内核架构三道坑 ✅srvD内核模块/驱动hellomod、globalmem 字符设备、platform 总线 probe ✅所有命令输出均来自真实远程实操记录transcripts/*.log可复现、可审计。