尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
03-Linux 内核与内核编程
3.1 Linux 内核的发展与演变版本时间标志性变化对驱动的意义1.01994单核、无模块化框架驱动必须编译进内核2.01996SMP 支持并发问题正式出现2.21999更完善的 VFS/网络栈驱动分层开始成形2.42001引入 kobject、sysfs 雏形设备模型起步2.62003统一设备模型、sysfs、kobject、platform_device、中断/工作队列重写原书主要对比的基线3.x/4.x2011–2016设备树普及、devm_*、runtime PM、blk-mq 引入原书4.0所处时代5.x2019–2021移除大量旧 API、timer_setup、probe_new、io_uring旧书代码开始大面积失效6.x2022–2024Rust 支持、blk-mq 统一、tasklet 退出主流、-f外部模块构建迁移的分水岭7.2.5本手册基线设备模型头文件拆分include/linux/device*、device-id/目录、DWARF 模块版本号CONFIG_GENDWARFKSYMS、更严格的类型与并发检查本学习手册结论4.0 到 7.2.5 不是小修小补而是API 大迁移 架构收敛。旧书里的示例代码大部分不能直接编译但设计思想完全没过时分层、分离、抽象、并发控制、资源生命周期。3.2 2.6 之后的内核特点延续到 7.2.5统一的设备模型struct device/struct device_driver/struct bus_type/struct class四件套任何设备都能在 sysfs 里找到位置。一切皆文件 一切皆属性设备在/sys中有属性文件udev 依据 uevent 自动创建设备节点。设备树/ACPI 描述硬件ARM 板级文件arch/arm/mach-*/board-*.c几乎全部删除改为 DTS。资源托管devresdevm_kzalloc、devm_ioremap_resource、devm_request_irq等让 probe 的错误路径大幅简化。并发模型成熟RCU 大范围使用、per-CPU 变量、无锁数据结构、READ_ONCE/WRITE_ONCE、KCSAN 检测数据竞争。电源管理内建runtime PM genpd OPP clock/regulator 联动。构建系统现代化Kbuild 支持 out-of-tree 构建、Clang/LLVM、Rust、可复现构建、SBOM、DWARF 符号版本。调试与验证内建KASAN/KCSAN/UBSAN/lockdep/KUnit/ftrace/perf/eBPF。3.3 Linux 内核的组成3.3.1 内核源代码目录结构以 7.2.5 源码树为例只列驱动工程师最常打交道的目录内容驱动工程师的关注点arch/体系结构相关代码arch/x86/、arch/arm64/设备树在arch/*/boot/dts/block/块层blk-mq写块设备驱动必读crypto/加密算法与框架硬件加解密驱动drivers/驱动主战场base/、char/、block/、net/、i2c/、spi/、usb/、gpio/、tty/、mmc/、iio/fs/文件系统与 VFS字符设备如何被打开、chrdev与 VFS 的衔接include/头文件include/linux/、include/uapi/、include/linux/device/、include/linux/device-id/7.2.5 新增拆分init/内核启动main.c的start_kernel()kernel/核心子系统irq/、time/、locking/、trace/、power/、printk/lib/通用库kfifo、crc、idr、iomap、kunitmm/内存管理kmalloc/vmalloc/页分配、KASANnet/网络协议栈网络设备驱动如何与协议栈交互rust/Rust 支持7.2.5 中 Rust 可写部分驱动与模块scripts/构建与工具脚本checkpatch.pl、decode_stacktrace.sh、faddr2line、gdb/Documentation/文档权威资料driver-api/、devicetree/bindings/、kbuild/、process/samples/示例代码samples/kfifo/、samples/trace_events/等tools/用户空间工具tools/testing/selftests/、tools/perf3.3.2 内核的组成部分从启动到运行的视角从功能视角进程调度、内存管理、VFS、网络栈、设备模型、电源管理、安全LSM、调试设施。3.3.3 内核空间与用户空间维度用户空间内核空间地址空间每个进程独立0 ~ TASK_SIZE所有进程共享位于高地址权限受限RING 3 / EL0完全权限RING 0 / EL1崩溃影响只杀进程内核 oops / panic内存分配mallockmalloc/vmalloc/页分配器指针校验不需要必须校验用户指针copy_from_user可睡眠随时取决于上下文中断里不能睡接口系统调用系统调用实现、EXPORT_SYMBOL四类数据拷贝必须背下来#include linux/uaccess.h /* 内核 ← 用户 */ if (copy_from_user(kbuf, ubuf, len)) return -EFAULT; /* 内核 → 用户 */ if (copy_to_user(ubuf, kbuf, len)) return -EFAULT; /* 简单标量 */ if (get_user(val, (int __user *)arg)) return -EFAULT; if (put_user(val, (int __user *)arg)) return -EFAULT;上下文规则7.2.5 依然严格执行上下文可睡眠?可用锁可用分配进程上下文可以mutex/spinlock/rcuGFP_KERNEL软中断 / tasklet不可以spinlock/rcuGFP_ATOMIC硬中断上半部不可以spinlock/rcuGFP_ATOMICthreaded IRQ 线程可以mutex/spinlockGFP_KERNEL3.4 内核的编译及加载3.4.1 内核的编译推荐做法out-of-tree 构建绝不污染源码树。export KSRC/home/works/study/linux-7.2.5 export KBUILD/home/works/study/build/linux-7.2.5-x86_64 mkdir -p $KBUILD # 1) 生成默认配置x86_64 通用 PC 配置QEMU 可直接启动 make -C $KSRC O$KBUILD x86_64_defconfig # 2) 叠加 QEMU 客户机常用选项内核自带片段避免手工勾选遗漏 make -C $KSRC O$KBUILD kvm_guest.config make -C $KSRC O$KBUILD debug.config # 调试实验时使用见第 21 章 # 3) 交互式调整 make -C $KSRC O$KBUILD menuconfig # 4) 编译并行度 CPU 核数 make -C $KSRC O$KBUILD -j$(nproc) # 5) 产物 ls $KBUILD/arch/x86/boot/bzImage # 可被 QEMU -kernel 直接加载 ls $KBUILD/vmlinux # 带调试信息的 ELF供 GDB 使用关于Documentation/admin-guide/README.rst本源码树中该文件的标题仍是 “Linux kernel release 6.x”属于文档陈旧未随版本号更新。判断版本请永远以顶层Makefile为准head -4 $KSRC/Makefile # VERSION7 PATCHLEVEL2 SUBLEVEL5用 Clang/LLVM 编译7.2.5 官方支持make -C $KSRC O$KBUILD LLVM1 x86_64_defconfig make -C $KSRC O$KBUILD LLVM1 -j$(nproc)交叉编译aarch64用于设备树实验export KBUILD_ARM64/home/works/study/build/linux-7.2.5-arm64 make -C $KSRC O$KBUILD_ARM64 ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- defconfig make -C $KSRC O$KBUILD_ARM64 ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- -j$(nproc)可复现构建发布或对照实验时有用参考Documentation/kbuild/reproducible-builds.rst关键是固定KBUILD_BUILD_TIMESTAMP、KBUILD_BUILD_USER、KBUILD_BUILD_HOST、KBUILD_BUILD_VERSION。常用make目标make help可列出全部目标用途x86_64_defconfig/defconfig生成默认配置menuconfig/nconfig/oldconfig配置界面 / 更新旧配置savedefconfig/olddefconfig导出最小配置 / 用默认值补齐新选项modules/modules_install只编译模块 / 安装模块dtbs编译设备树ARM/ARM64/RISC-Vclean/mrproper/distclean清理产物mrproper会删配置coccicheck语义补丁检查checkstack/cscope/tags代码分析辅助rustavailable检查 Rust 工具链是否可用kernel.sbom生成 SBOM软件物料清单3.4.2 Kconfig 和 MakefileKconfig 定义能不能选# drivers/misc/hello/Kconfig config HELLO_DRIVER tristate Hello world driver for QEMU experiments depends on OF || COMPILE_TEST depends on m # 本模块只能编译为模块示例 select GPIOLIB # 选中本选项即自动打开 GPIOLIB default m help A minimal character driver used by the study manual. If unsure, say N.tristate的三个状态对应y编进内核、m编译成模块、n不编译。depends on m是让选项只能为m的写法i2c-stub就是这样。Makefile 定义编译什么# drivers/misc/hello/Makefile obj-$(CONFIG_HELLO_DRIVER) hello_driver.o # 多文件模块的写法 # obj-$(CONFIG_HELLO_DRIVER) hello_driver.o # hello_driver-y : core.o irq.o fileops.o把新驱动加入内核树在drivers/misc/hello/里放 Kconfig 与 Makefile然后在drivers/misc/Kconfig中source drivers/misc/hello/Kconfig在drivers/misc/Makefile中obj-y hello/。内核 Kconfig 语言的关键点详见Documentation/kbuild/kconfig-language.rstdepends on表示可见性依赖select表示强制打开容易造成依赖环与不满足依赖谨慎使用现代内核更推荐imply。depends on X || COMPILE_TEST是让驱动在非目标架构上也能编译检查的标准写法。用make olddefconfig处理新版本增加了新选项的常见问题。3.4.3 内核的引导x86_64 QEMU 的引导链本手册主要路径QEMU 固件/直接加载 -kernel → bzImage 解压 → arch/x86/boot/compressed/ 进入 64 位模式 → arch/x86/kernel/head_64.S → x86_64_start_kernel() → x86_64_start_reservations() → start_kernel() → rest_init() → kernel_init() → do_initcalls() → 若 initramfs 里有 /init 则执行它否则尝试 root 指定的根文件系统ARM64 QEMUvirt的引导链QEMU 加载 Image DTB跳转到内核 → arch/arm64/kernel/head.S → start_kernel() → 解析 DTBsetup_arch → unflatten_device_tree → 枚举 platform_deviceof_platform_default_populate_init → 驱动 probeinitramfsinitial ramdisk的作用内核启动早期就能提供一个最小的根文件系统用来加载真正的根文件系统或本手册场景直接进入 shell。制作方法见第 22 章。内核命令行参数写错一个就可能启动失败务必掌握consolettyS0,115200 串口控制台QEMU -nographic 必须 earlycon 早期控制台用于调试启动崩溃 loglevel7 打印所有级别 nokaslr 关闭内核地址随机化GDB 调试必须 oopspanic panic1 出错立即重启方便自动化实验 init/bin/sh 直接进 shell跳过正常 init root/dev/vda rw 根文件系统 initcall_debug 打印每个 initcall 的耗时分析 probe 顺序 ignore_loglevel 忽略 loglevel全部打印3.5 Linux 下的 C 编程特点3.5.1 Linux 编码风格内核编码风格Documentation/process/coding-style.rst是强制的缩进用Tab宽度 8不要用空格缩进。一行不超过 80 列现代内核允许少量超长但不要随意。大括号风格KR函数开括号换行控制语句开括号不换行。命名局部变量短i、ret、tmp全局/函数名清晰globalmem_read。函数尽量短一个函数做一件事。注释用/* ... */不要用////仅在文档示例与少量场景允许。switch与case对齐规则、if后必须有空格等细节见文档。禁止在头文件中用typedef隐藏结构体指针。# 提交前必须通过 scripts/checkpatch.pl --no-tree --strict -f drivers/misc/hello/hello_driver.c # 对整个补丁检查 scripts/checkpatch.pl --no-tree 0001-my-driver.patch3.5.2 GNU C 与 ANSI C内核使用 GNU C 扩展-stdgnu11及部分 gnu17 特性。必须掌握的扩展扩展示例用途语句表达式({ int a 1; a 1; })宏里返回值如blk_mq_alloc_disk()、kfifo_alloc()typeof/__typeof__typeof(x) tmp x;泛型宏min、max、container_of指定初始化器struct foo f { .a 1, .b 2 };现代内核要求用指定初始化器checkpatch会检查范围扩展...case 1 ... 5:区间匹配条件表达式省略中项a ?: b简化默认值__attribute____must_check、__printf(2,3)、__packed编译期检查与内存布局asm goto内联汇编跳转底层原子操作__auto_type类型推导少数新代码使用cleanup属性guard(mutex)(lock);7.2.5 中的自动清理机制include/linux/cleanup.h7.2.5 新增值得掌握的习惯——cleanup.h的自动清理#include linux/cleanup.h int demo(struct my_dev *d) { guard(mutex)(d-lock); /* 出作用域自动 unlock */ struct foo *p __free(kfree) kzalloc(sizeof(*p), GFP_KERNEL); if (!p) return -ENOMEM; /* 出作用域自动 kfree(p) */ return 0; }3.5.3do { } while (0)语句宏定义中用它把多条语句包成一个整体避免if/else悬挂问题#define MYDBG(fmt, args...) do { \ if (debug) \ pr_info(mydrv: fmt, ##args); \ } while (0) /* 若不用 do{}while(0) * if (cond) MYDBG(x); else ...; → 会被展开成两条语句编译错误 */7.2.5 的补充多语句宏也可以直接使用static inline函数或者用guard()/scoped_guard()替代手工加解锁的宏。3.5.4goto语句内核中goto的唯一正当用途是集中错误处理static int my_probe(struct platform_device *pdev) { int ret; priv devm_kzalloc(pdev-dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv-base devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv-base)) return PTR_ERR(priv-base); priv-irq platform_get_irq(pdev, 0); if (priv-irq 0) return priv-irq; ret devm_request_irq(pdev-dev, priv-irq, my_isr, 0, dev_name(pdev-dev), priv); if (ret) goto err_irq; ret my_hw_init(priv); if (ret) goto err_irq; return 0; err_irq: my_hw_shutdown(priv); return ret; }现代写法优先考虑devm_*很多情况下连goto都不需要。但只要涉及非托管资源自定义链表、kthread、自建 workqueue、clk的非 devm 版本就必须保留清晰的错误回滚标签。3.6 工具链组件作用检查命令编译器生成目标代码gcc --version、clang --version交叉编译器编译目标架构aarch64-linux-gnu-gcc --version汇编/链接as、ldLLVM 下为llvm-as、ld.lldld --version调试器内核/用户态调试gdb --version、gdb-multiarch二进制工具查看符号/段/反汇编nm、objdump、readelf、objcopypahole查看结构体布局生成 BTFpahole --versiondtc设备树编译dtc --versionbpftooleBPF 工具bpftool versionperf性能分析perf --versionb4邮件列表补丁工作流b4 --version常见工具链不匹配问题ERROR: modpost: xxx [.../foo.ko] undefined!→ 通常是因为模块用了未EXPORT_SYMBOL的函数或模块与内核配置不一致。*** No rule to make target arch/x86/...→ 忘了O或ARCH导致在错误的目录/架构下构建。3.7 实验室建设原书讨论的实验室在 7.2.5 时代可以完全虚拟化原书建议现代等价方案说明开发板 串口线QEMU-nographic/-serial mon:stdio完全替代且可脚本化JTAG 仿真器QEMU gdbstub-s -S见第 21 章示波器/逻辑分析仪tracepoint ftrace QEMU trace见第 2 章交叉编译主机同一台 x86_64 CROSS_COMPILE或容器化版本管理git源码树可能不含.git可自行git init或用 tarball便于对比修改实验记录脚本化每次实验用固定命令 dmesg输出存档建议建立experiments/目录建议的目录布局3.8 串口工具宿主侧常用工具# picocom推荐轻量 sudo apt install -y picocom picocom -b 115200 /dev/ttyUSB0 # minicom sudo apt install -y minicom minicom -D /dev/ttyUSB0 -b 115200 # screen screen /dev/ttyUSB0 115200 # 用 socat 模拟一对虚拟串口没有硬件时 sudo apt install -y socat socat -d -d pty,raw,echo0 pty,raw,echo0 # 会输出 /dev/pts/N 与 /dev/pts/M一个给 QEMU一个给自己QEMU 的串口用法本手册的主要串口工具-nographic # 把串口与 QEMU monitor 都接到当前终端 -serial mon:stdio # 图形模式下把串口接到标准输入输出 -serial file:/tmp/s.log # 串口输出写文件 -serial pty # 分配一个伪终端QEMU 打印其路径可用 picocom 连接 -chardev socket,ids0,hostlocalhost,port4444,serveron,waitoff \ -serial chardev:s0 # 通过 TCP 访问串口便于脚本化guest 侧# 调整串口参数 stty -F /dev/ttyS0 115200 cs8 -cstopb -parenb # 查看串口驱动状态 cat /proc/tty/driver/serial # 直接往串口写数据 echo hello /dev/ttyS03.9 总结4.0 → 7.2.5 的演进主线是设备树取代板级文件、devm 取代手工回滚、blk-mq 统一块层、threaded IRQ 取代 tasklet、调试设施体系化。内核构建必须掌握 out-of-tree 构建、defconfig/片段配置、menuconfig、交叉编译与 LLVM 构建判断版本以顶层Makefile为准。Kconfig 管能不能选Kbuild 管编译什么tristate/depends on/select的语义必须清楚。内核 C 语言的核心约束是风格强制、GNU 扩展大量使用、错误路径必须回滚、上下文决定能不能睡眠。工具链、实验室与串口都可以完全虚拟化QEMU 交叉编译 tracepoint 就构成一套完整的驱动开发环境。
RELATED

相关推荐

Java基础学习笔记 09、IO流—对象序列化

Java基础学习笔记 09、IO流—对象序列化

文章目录 前言 一、认识序列化 二、实现序列化 1、实现序列化要求及说明 2、实例程序 自定义类准备 ①序列化对象 ②解序列化 三、深入了解序列化 序列化过程 解序列化过程 四、序列化相关问题 参考资料 前言 本文是付费专栏 《Java后端开发从入门到进阶》 的配套文章,所属阶…

📅 2026/10/7 20:28:44
维恩波特Vairnport 策略服务费推出的长期意义

维恩波特Vairnport 策略服务费推出的长期意义

从持续技术投入到长期价值循环,Vairnport 加速构建可持续平台发展机制随着全球金融市场不断数字化、智能化,金融科技平台之间的竞争正在从单纯的用户规模与市场扩张,逐渐转向技术能力、系统稳定性、风险管理能力以及长期服务能力的综合竞争。…

📅 2026/10/7 20:28:44
企业怎样利用 Amazon Bedrock 选用并调用不同版本的 ChatGPT 模型?

企业怎样利用 Amazon Bedrock 选用并调用不同版本的 ChatGPT 模型?

企业怎样利用 Amazon Bedrock 选用并调用不同版本的 ChatGPT 模型?避免把模型版本写死在业务逻辑里企业正式接入 OpenAI ChatGPT 系列模型之后,很快就会意识到,相比于 “选择哪一款模型”,更长久的挑战来自模型版本的持续迭代。 当…

📅 2026/10/7 20:28:44
MORE NEWS

更多资讯

📰

核心语法—数据容器

一 、概述什么是数据容器?根据这三种标准,将数据容器分为五类二、列表list1、介绍列表是数据容器的一类,是一次性可以存储多个数据(元素)的特点:可以存储不同类型的元素,元素有序、可以重复、元…

📰

Personal Agent 六路争锋:默认委托权 = 入口密度 × 上下文深度 × 授权精细度 × 可撤回性

【摘要】2026年Personal Agent赛道竞争已从模型能力转向委托关系设计。本文基于经典委托-代理理论与全球行业公开规范,提出默认委托权入口密度上下文深度授权精细度可撤回性评估公式,以4个品类判定条件、5个产品分析维度拆解6条主流路线,交付…

📰

终端安全事后追溯能力解析:本地审计模块设计思路与落地实践

前言 终端安全防护体系可以简单分为三层:事前策略拦截风险、事中实时告警处置、事后审计追溯取证。 在实际项目实施我遇到过不少真实情况:有些单位部署完终端安全系统之后,只配置管控拦截策略,审计模块全部保持默认,结…

📰

微客AI助手安全复盘:94 个备份文件在公网上裸奔——一次拦截规则被绕过的教训

起因:一次例行的密级扫描接手一个网站没多久,做了一次例行的安全自检:扫描 webroot 目录下有没有不该对外暴露的文件——备份文件、编辑器临时文件、环境变量样本之类。扫描器给了个扎眼的数字:94 个 .bak 文件可以直接通过公网 U…

📰

AI应用开发成本控制:大模型API调用优化实战指南

1. 这不是省钱技巧,是AI应用落地的生存基本功“AI应用开发成本控制”这八个字,最近半年在我们团队晨会里出现频率比“需求评审”还高。不是因为大家突然爱算账了,而是去年Q3上线的三个AI功能模块,光大模型API调用费就吃掉了当季技…

📰

一行doctype竟让页面崩塌?真相揭秘!

很多前端新手写代码都有一个通病&#xff0c;复制模板只抄HTML结构&#xff0c;却完全忽略最顶部的 <!DOCTYPE html> 声明。不少人觉得这行代码毫无作用&#xff0c;看着像多余的注释&#xff0c;删掉也不影响页面展示&#xff0c;平时写demo、练习代码经常直接省略。但在…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬