Linux 内核驱动基础 API 实战指南:从模块装载到设备资源管理
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
本篇技术指南以 Linux 内核源码树(本仓库GitHub_Trending/li/linux)中的 Documentation/driver-api/basics.rst 为骨架,系统梳理内核驱动开发最常用、最基础的一批内核 API:模块入口/出口、设备 ID 表、延迟与调度、时间与定时器、高精度定时器、等待队列、内核线程、引用计数、原子操作、kobject、内核工具函数以及设备资源管理(devres)。读者读完本文后,可以建立起驱动开发者日常打交道的"基础工具包"全景视图,并知道每个 API 族的确切头文件、实现文件与典型用法,从而在编写或阅读驱动代码时快速定位、正确使用这些基础设施。
basics.rst本身是一份通过kernel-doc指令自动提取源码注释生成的 API 索引,本文在完整继承其 12 个主题分类的基础上,逐一深入对应的头文件与实现文件,补充接口语义、边界条件与实战注意事项。
一、驱动入口与出口(Driver Entry and Exit points)
任何可加载内核模块或内建驱动的生命周期都从入口函数开始、以出口函数结束。这两个端点定义在 include/linux/module.h。
在非模块编译(MODULE未定义)的情况下,module_init() 与 module_exit() 被定义为:
#define module_init(x) __initcall(x); #define module_exit(x) __exitcall(x);其语义为:
- module_init(x):若驱动静态编译进内核,该函数会在内核启动阶段的
do_initcalls()中被调用;若编译为可加载模块,则在insmod/modprobe插入时被调用。每个模块只能有一个入口函数。 - module_exit(x):当驱动作为模块时,出口函数会与
cleanup_module()挂钩,在rmmod卸载时被调用;若驱动静态编译进内核,module_exit()不产生任何效果。同样只能有一个。
当编译为可加载模块(#ifdef MODULE分支,见 include/linux/module.h#L131-L144)时,这两个宏会生成init_module()/cleanup_module()的别名,并通过___ADDRESSABLE让符号在模块加载阶段可见。同文件还把所有 initcall 级别(early_initcall、core_initcall、subsys_initcall、device_initcall、late_initcall等)在模块场景下统一折叠为module_init,因为可加载模块的初始化时机由加载动作决定,不需要 initcall 级别。
一个最简驱动骨架:
#include <linux/module.h> #include <linux/init.h> static int __init mydrv_init(void) { /* 初始化硬件、注册设备、申请资源等 */ return 0; } static void __exit mydrv_exit(void) { /* 反向释放资源 */ } module_init(mydrv_init); module_exit(mydrv_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name <you@example.com>"); MODULE_DESCRIPTION("A minimal example driver");module.h还提供了大量MODULE_*元信息宏,它们会被写入模块的.modinfo段,供modinfo命令查询:
| 宏 | 作用 | 定义位置 |
|---|---|---|
MODULE_LICENSE(_license) | 声明许可证,"GPL"、"Dual BSD/GPL"、"Proprietary"等;影响非自由模块能否绑定EXPORT_SYMBOL_GPL符号 | include/linux/module.h#L230 |
MODULE_AUTHOR(_author) | 作者信息,可多次声明 | include/linux/module.h#L236 |
MODULE_DESCRIPTION(_description) | 模块功能描述 | include/linux/module.h#L239 |
MODULE_ALIAS(_alias) | 为模块增加别名,供自动加载匹配 | include/linux/module.h#L163 |
MODULE_SOFTDEP(_softdep) | 声明软依赖,如"pre: module-foo module-bar post: module-baz" | include/linux/module.h#L168 |
MODULE_WEAKDEP(_weakdep) | 声明弱依赖,如"module-foo" | include/linux/module.h#L174 |
MODULE_VERSION(_version) | 版本号,格式[<epoch>:]<version>[-<extra-version>],自动生成srcversion校验和 | include/linux/module.h#L276-L295 |
MODULE_FIRMWARE(_firmware) | 声明模块所需固件文件名,多个固件需多次声明 | include/linux/module.h#L300 |
MODULE_DEVICE_TABLE(type, name) | 导出设备 ID 表,供modprobe自动加载使用 | include/linux/module.h#L255-L257 |
关于许可证,module.h注释明确指出:MODULE_LICENSE中"GPL v2"与"GPL"的区分是历史遗留,对模块加载而言 "only/or later" 的区别完全无关紧要;其唯一实际用途是让modinfo显示许可信息、让社区忽略专有模块的 bug 报告,以及拒绝非自由模块绑定EXPORT_SYMBOL_GPL导出的符号(见 include/linux/module.h#L204-L229)。
二、驱动设备 ID 表(Driver device table)
设备 ID 表是驱动与总线设备"对号入座"的桥梁,集中定义在 include/linux/mod_devicetable.h。该头文件本身是一张"总目录",通过 include 引入device-id/子目录下按总线/子系统划分的几十个设备 ID 结构定义,例如 ACPI、AMBA、PCI、USB、I2C、SPI、SDIO、OF(设备树)、platform 总线、virtio 等(见 include/linux/mod_devicetable.h#L15-L70)。
该头文件顶部有一句重要约束:"Device tables which are exported to userspace via scripts/mod/file2alias.c. You must keep that file in sync with this header."——设备表结构与 scripts/mod/file2alias.c 的解析逻辑必须保持同步,因为构建系统正是通过它把设备表转换成模块别名(modalias),从而让udev/modprobe能在热插拔时按设备 ID 自动加载对应驱动。
basics.rst对本节使用了:no-identifiers: pci_device_id选项,即从文档中排除pci_device_id的 kernel-doc 注释(原因是在其它文档中已有专门介绍)。该结构定义于 include/linux/device-id/pci.h#L36-L42:
struct pci_device_id { __u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/ __u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */ __u32 class, class_mask; /* (class,subclass,prog-if) triplet */ kernel_ulong_t driver_data; /* Data private to the driver */ __u32 override_only; };要点:
- 每个字段都可用
PCI_ANY_ID(定义为~0,见 include/linux/device-id/pci.h#L10)作为通配。 class/class_mask用于按设备类别匹配,大多数驱动靠 vendor/device 就足够,无需指定。driver_data的最佳实践是作为"等价设备类型静态列表"的下标使用,而不是直接当作指针。override_only仅当dev->driver_override指定本驱动时才匹配。
典型用法:定义一个struct pci_device_id数组,配合MODULE_DEVICE_TABLE(pci, id_table)导出,并在struct pci_driver中引用。MODULE_DEVICE_TABLE的实现(include/linux/module.h#L246-L257)为设备表生成一个命名形如__mod_device_table__kmod_<modname>__<type>__<name>的别名符号,file2alias.c依靠__kmod_与__作为分隔符解析出设备信息。
三、延迟与调度例程(Delaying and scheduling routines)
本节覆盖调度器与同步原语,kernel-doc 引用的文件包括 include/linux/sched.h、kernel/sched/core.c、kernel/sched/cpupri.c、kernel/sched/fair.c 以及 include/linux/completion.h。
3.1 任务状态与进程标志
include/linux/sched.h中定义了大量任务状态与进程标志:
task_state_index()/task_state_to_char()系列用于把task_struct的状态映射为字符(R/S/D/T等),这正是ps输出各状态字母的来源(见 include/linux/sched.h#L1744-L1747)。PF_*进程标志位用于描述进程特性,例如PF_KTHREAD(内核线程)、PF_WQ_WORKER(工作队列 worker)、PF_IDLE(idle 线程)、PF_MEMALLOC(为释放内存而分配内存)等,完整列表见 include/linux/sched.h#L1795-L1828。PFA_*(per-process atomic flags)通过task_set_*/task_clear_*/task_*内联函数以原子位操作访问,例如task_set_no_new_privs()等(见 include/linux/sched.h#L1870-L1913)。
3.2 主动让出与延迟
驱动代码中常见的让出 CPU 或延迟手段包括schedule()、cond_resched()、msleep()等。msleep()等睡眠式延迟基于定时器实现,会让出 CPU;而udelay()/ndelay()为忙等待,只适合原子上下文中的极短延迟(这是驱动开发中必须区分的关键点)。
3.3 完成量(Completions)
完成量是驱动开发最常用的"一个线程等另一个线程干完活"的同步原语,定义在 include/linux/completion.h。其数据结构(include/linux/completion.h#L26-L29):
struct completion { unsigned int done; struct swait_queue_head wait; };- 声明与初始化:静态声明用
DECLARE_COMPLETION(work)(include/linux/completion.h#L52-L53);栈上自动变量用DECLARE_COMPLETION_ONSTACK(work)(锁依赖检查需要非常量初始化,见 include/linux/completion.h#L60-L75);动态分配用init_completion()(include/linux/completion.h#L84-L88),复用前可用reinit_completion()(尤其在调用过complete_all()之后必须重新初始化,见 include/linux/completion.h#L97-L100)。 - 等待侧(全部在 include/linux/completion.h#L102-L116 声明):
| 函数 | 行为 |
|---|---|
wait_for_completion() | 不可中断等待 |
wait_for_completion_interruptible() | 可被信号中断,返回-ERESTARTSYS |
wait_for_completion_killable() | 仅可被致命信号中断 |
wait_for_completion_timeout()/wait_for_completion_io_timeout() | 带超时(单位 jiffies),返回剩余 jiffies,0 表示超时 |
wait_for_completion_interruptible_timeout()/killable_timeout() | 可中断 + 超时组合 |
try_wait_for_completion() | 非阻塞探测 |
completion_done() | 查询是否已完成 |
- 完成侧:
complete()(唤醒一个等待者)、complete_all()(唤醒所有等待者,唤醒后必须reinit_completion()才能复用)、complete_on_current_cpu()(在当前 CPU 上执行完成回调),见 include/linux/completion.h#L118-L120。
调度器其余部分(kernel/sched/core.c的导出 API、cpupri.c的 CPU 优先级、fair.c的 CFS 调度)通过:export:与:internal:选项收录进本节文档,供深入阅读调度子系统时查阅。
四、时间与定时器例程(Time and timer routines)
本节以 include/linux/jiffies.h、kernel/time/time.c、kernel/time/timer.c 为对象。
4.1 jiffies 与 HZ
jiffies 是内核"心跳"计数器,其频率由HZ决定。jiffies.h顶部注释以 SunOS(100 Hz)、Ultrix(256 Hz)、OSF/1(1024 Hz)为例说明该概念的来源(见 include/linux/jiffies.h#L16-L22),并通过SHIFT_HZ把HZ折算为 2 的幂,以避免乘法运算(include/linux/jiffies.h#L23-L45)。
两个核心变量(include/linux/jiffies.h#L81-L82):
extern u64 __cacheline_aligned_in_smp jiffies_64; extern unsigned long volatile __cacheline_aligned_in_smp jiffies;- 在 32 位系统上,64 位的
jiffies_64读取不是原子的,必须通过get_jiffies_64()采样;在 64 位系统上它退化为直接返回jiffies(include/linux/jiffies.h#L84-L99)。
4.2 时间比较宏:处理回绕
jiffies 是无符号长整型,长时间运行必然回绕。jiffies.h明确建议(include/linux/jiffies.h#L101-L110):
These inlines deal with timer wrapping correctly. You are strongly encouraged to use them: 1. Because people otherwise forget. 2. Because if the timer wrap changes in future you won't have to alter your driver code.
典型宏:
#define time_after(a,b) ((long)((b) - (a)) < 0) #define time_before(a,b) time_after(b,a)time_after(a, b)通过有符号比较判断a是否在b之后,天然规避回绕问题(include/linux/jiffies.h#L112-L126)。同族的time_after_eq、time_before_eq、time_in_range等用于各类超时判断。
4.3 时间转换与动态定时器
kernel/time/time.c提供msecs_to_jiffies()、usecs_to_jiffies()、jiffies_to_msecs()、jiffies_to_usecs()等转换函数,驱动代码中应始终使用这些转换而非手工乘除,以保证在不同HZ配置下行为一致。kernel/time/timer.c实现传统动态定时器(struct timer_list):init_timer()/timer_setup()初始化、mod_timer()/add_timer()启动、del_timer()/del_timer_sync()删除。注意del_timer_sync()在多处理器环境下确保定时器回调不再并发运行。
五、高分辨率定时器(High-resolution timers)
高精度定时器(hrtimer)提供纳秒级精度的定时能力,文档对象为 include/linux/ktime.h、include/linux/hrtimer.h 与 kernel/time/hrtimer.c。
- ktime 类型:
ktime.h定义了内核时间表示ktime_t(通常为 64 位纳秒),并提供ktime_set()、ktime_add()、ktime_sub()、ktime_to_ns()、ktime_get()等操作。驱动中凡是需要精确时间戳或时间运算的地方都应使用 ktime 系列。 - hrtimer 对象:
include/linux/hrtimer.h提供hrtimer_init()、hrtimer_start()、hrtimer_cancel()、hrtimer_try_to_cancel()等接口,回调函数在软中断(HRTIMER_MODE_REL/HRTIMER_MODE_ABS决定相对/绝对超时)上下文中执行。与struct timer_list不同,hrtimer 不基于 jiffies tick,而是基于高精度时钟源,因此不受HZ粒度限制。 - 底层实现位于 kernel/time/hrtimer.c,其
:export:部分导出了供模块使用的核心 API。
选择建议:需要"节拍粒度"级别的延迟用timer_list/msleep;需要精确到纳秒或毫秒以下级别、或对唤醒延迟敏感的场景用 hrtimer;中断处理中的精确延迟用ktime配合忙等待。
六、等待队列与唤醒事件(Wait queues and Wake events)
等待队列是内核"睡眠-唤醒"机制的基础,文档对象为 include/linux/wait.h 与 kernel/sched/wait.c。
核心用法:
DECLARE_WAIT_QUEUE_HEAD(wq); /* 静态声明等待队列头 */ /* 等待侧:在条件不满足时睡眠 */ wait_event(wq, condition); /* 不可中断 */ wait_event_interruptible(wq, condition); /* 可中断 */ wait_event_timeout(wq, condition, timeout);/* 带超时 */ wait_event_interruptible_timeout(wq, condition, timeout); /* 唤醒侧 */ wake_up(&wq); /* 唤醒一个等待者 */ wake_up_all(&wq); /* 唤醒全部 */ wake_up_interruptible(&wq); /* 唤醒可中断睡眠者 */关键语义:
wait_event*宏在进入睡眠前会先检查条件,条件为真时直接返回,避免"丢失唤醒"竞态;睡眠期间条件变化必须配合唤醒调用。- 返回值为 0 表示条件满足;
wait_event_timeout返回剩余 jiffies,超时返回 0;wait_event_interruptible被信号中断返回-ERESTARTSYS。 kernel/sched/wait.c提供__wake_up、prepare_to_wait、finish_wait等底层原语,wait_event宏在展开时最终调用这些实现。驱动开发者通常只使用宏,无需直接触碰底层函数。
在驱动中的典型场景:设备中断到来时wake_up_interruptible(&wq),读取线程在wait_event_interruptible上等待数据可用,从而实现"中断驱动 + 进程上下文消费"的标准模型。
七、内部函数(Internal Functions)
本节涵盖进程退出、信号处理与内核线程,文档对象为 kernel/exit.c、kernel/signal.c、include/linux/kthread.h 与 kernel/kthread.c。
7.1 进程退出与信号
- kernel/exit.c(
:internal:)包含do_exit()、complete_and_exit()等退出路径内部函数。驱动中如果需要让内核线程退出,通常通过kthread_stop()或让线程函数自行返回实现,而不是直接调用退出路径。 - kernel/signal.c(
:internal:)包含信号发送/递送的核心实现,如send_sig()、force_sig()等。驱动可以在需要时给进程发送信号通知事件。
7.2 内核线程(kthread)
include/linux/kthread.h与kernel/kthread.c是创建内核线程的标准接口:
#include <linux/kthread.h> struct task_struct *thread; thread = kthread_create(threadfn, data, "name%d", nr); /* 创建但不运行 */ wake_up_process(thread); /* 手动启动 */ /* 或者创建并立即运行: */ thread = kthread_run(threadfn, data, "name%d", nr); /* 线程函数原型 */ static int threadfn(void *data) { ... return 0; } /* 停止线程(从其它上下文调用) */ kthread_stop(thread);注意事项:
kthread_create只是创建任务并置为暂停状态,必须wake_up_process()或使用kthread_run才会真正开始执行。kthread_stop()会设置kthread_should_stop()标志并等待线程退出;线程函数内应周期性检查kthread_should_stop()以便及时退出。- 内核线程没有用户态地址空间,只能调用内核 API,不能访问用户内存。
八、引用计数(Reference counting)
引用计数用于管理共享对象的生命周期,本节对象为 include/linux/refcount.h、lib/refcount.c、include/linux/percpu-refcount.h 与 lib/percpu-refcount.c。
8.1 refcount_t:带饱和语义的引用计数
refcount_t是atomic_t的引用计数专用变体,接口与atomic_t对齐但只提供引用计数应使用的少量函数(见 include/linux/refcount.h#L3-L6)。核心 API:
- 初始化:
refcount_set()、refcount_read();静态初始化REFCOUNT_INIT(n)。 - 增加:
refcount_inc()、refcount_inc_not_zero()(返回是否成功增加)、refcount_add()、refcount_add_not_zero()。 - 减少:
refcount_dec()、refcount_dec_and_test()(减到 0 返回 true,可安全释放对象)、refcount_dec_if_one()、refcount_sub_and_test()、refcount_dec_not_one()。
饱和语义(Saturation semantics)是它与裸atomic_t的本质区别:计数器在到达REFCOUNT_SATURATED(0xc000_0000)后即饱和,不再变化,从而避免回绕导致"虚假的 use-after-free"。设计上把REFCOUNT_SATURATED放在 0 与INT_MAX中间偏高位(见 include/linux/refcount.h#L8-L58 中的数值示意),配合PID_MAX_LIMIT限制,使饱和计数无法在实战中逃逸出饱和区间。
内存序(Memory ordering):相对于普通atomic_t有所放松(include/linux/refcount.h#L60-L80):
- 增操作完全 relaxed,不提供排序——因为"获取对象引用"这个动作本身(锁获取或 RCU 依赖加载)已提供了所需排序;
inc_not_zero()提供控制依赖,确保只有在确实拿到引用后才可能修改对象;- 减操作提供 release 语义,保证此前所有读写先行于对象释放,并提供控制依赖将释放动作排序在后。
8.2 percpu-refcount:高并发场景的按 CPU 引用计数
include/linux/percpu-refcount.h 与 lib/percpu-refcount.c 实现struct percpu_ref:引用计数按 CPU 分布(percpu_ref_init、percpu_ref_get、percpu_ref_put),避免多 CPU 高频增删引用时的缓存行竞争;切换到原子模式后(percpu_ref_kill/percpu_ref_switch_to_atomic)可安全执行释放逻辑,配合percpu_ref_exit清理。它适合块设备、网络等每 CPU 热点极高的对象生命周期管理。
九、原子操作(Atomics)
原子操作是并发安全的最小积木,本节 kernel-doc 对象为:
- include/linux/atomic/atomic-instrumented.h:由脚本生成的"带 KASAN/KCSAN 插桩"的原子操作封装,所有
atomic_*/atomic64_*操作在这里统一加插桩,用于在运行时检测越界访问与数据竞争。 - include/linux/atomic/atomic-arch-fallback.h:架构原子操作的回退实现,在没有对应架构原语时提供通用(通常基于
cmpxchg循环)实现。 - include/linux/atomic/atomic-long.h:
atomic_long_t,按BITS_PER_LONG自动映射到atomic_t或atomic64_t,让代码在 32/64 位平台上使用统一类型。
常用操作:atomic_read/atomic_set、atomic_inc/atomic_dec、atomic_add/atomic_sub、atomic_cmpxchg、atomic_xchg,以及带内存序后缀的变体(_acquire/_release/_relaxed)。注意区分用途:计数场景优先用refcount_t(第八节),位图场景用set_bit/clear_bit/test_and_set_bit,通用计数器才用atomic_t。
十、内核对象操作(Kernel objects manipulation)
kobject 是内核设备模型(device model)与 sysfs 的基石,本节文档对象为 lib/kobject.c 与 lib/kobject_uevent.c(均以:export:导出 API)。
- kobject 基础:
kobject_init()/kobject_init_and_add()初始化并注册;kobject_add()添加到 sysfs 层次;kobject_get()/kobject_put()管理引用计数,kobject_put使计数归零时触发release回调(kobject 必须内嵌在宿主结构中,release中释放宿主内存);kobject_del()从层次中移除。 - kset:同类 kobject 的集合容器,提供目录层次与共享属性,
kset_create_and_add()/kset_unregister()管理生命周期。 - uevent:
lib/kobject_uevent.c实现用户空间事件通知机制。kobject_uevent()/kobject_uevent_env()向udev/systemd-udevd广播KOBJ_ADD、KOBJ_REMOVE、KOBJ_CHANGE等事件,携带环境变量,驱动可通过uevent回调为事件附加自定义属性。这是设备热插拔事件进入用户空间的通道。
十一、内核工具函数(Kernel utility functions)
本节集合了驱动开发中高频使用的一组头文件级工具与核心子系统导出函数:
| 工具 | 文件 | 说明 |
|---|---|---|
ARRAY_SIZE() | include/linux/array_size.h | 编译期计算数组元素个数 |
container_of() | include/linux/container_of.h | 由结构体成员指针反推宿主结构体指针,是内核"面向对象风格"(嵌入子结构 + 回调)的核心宏 |
kstrto*()系列 | include/linux/kstrtox.h | 字符串转整数(kstrtoul/kstrtol在文档中通过:no-identifiers:排除,因其有专门文档);返回严格错误码,优于旧式simple_strtoul |
offsetof()/sizeof()等 | include/linux/stddef.h | 标准定义 |
find_closest()等 | include/linux/util_macros.h | 实用宏,如查找最接近的匹配值 |
lower_32_bits()/upper_32_bits()等 | include/linux/wordpart.h | 对 64 位值进行 32 位高低拆分/组合、字节序换算等 |
| printk 相关导出 | kernel/printk/printk.c | 内核日志基础设施;文档通过:no-identifiers: printk排除printk本体(有独立文档) |
| panic 相关导出 | kernel/panic.c | panic()、panic_on_oops等内核致命错误处理路径 |
其中container_of()尤其值得强调:驱动中大量回调(如struct file_operations、struct device_driver、各类ops结构)都只传成员/上下文指针,通过container_of即可还原出完整的驱动私有结构,这是理解内核代码风格的必备工具。
十二、设备资源管理(Device Resource Management)
最后也是驱动生命周期管理最实用的部分——devres(managed device resources),实现于 drivers/base/devres.c(devm_*API 全部以:export:导出)。devres 的核心思想:资源随设备生命周期自动释放。
驱动的probe()中使用devm_*系列分配资源,则在remove()或设备解绑时,内核会自动释放这些资源,无需(也不应)手动释放,从而避免资源泄漏和错误路径遗漏:
#include <linux/device.h> static int my_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct my_priv *priv; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); /* 自动释放 */ if (!priv) return -ENOMEM; /* 更多 devm_ 资源:ioremap、IRQ、时钟、GPIO、DMA 等 */ devm_ioremap(dev, res->start, resource_size(res)); devm_request_irq(dev, irq, my_isr, 0, "mydrv", priv); return 0; }devres.c 中导出的常用 API(完整列表见 drivers/base/devres.c 的:export:部分)包括:
- 内存类:
devm_kmalloc、devm_kzalloc、devm_krealloc、devm_kfree - 映射类:
devm_ioremap、devm_ioremap_resource、devm_memremap - 中断类:
devm_request_irq、devm_request_threaded_irq、devm_free_irq - 其它:
devm_clk_get、devm_gpiod_get、devm_platform_ioremap_resource、devm_fwnode_get_*等(后几类分散在对应子系统源码中,如drivers/clk/clkdev.c、drivers/gpio/gpiolib.c)
使用 devres 后,probe()失败路径可以简化为一连串goto变成"全部交给框架";remove()通常只需做非 devm 资源(如unregister_*类)的清理,或直接为空。这是现代驱动编写的推荐实践。
总结
Documentation/driver-api/basics.rst本质上是内核为驱动开发者铺设的一张"基础 API 地图",本文沿其 12 个主题分类逐层深入:
- 模块入口/出口(
module_init/module_exit与MODULE_*元信息)定义于 include/linux/module.h; - 设备 ID 表结构集中于 include/linux/mod_devicetable.h 及
device-id/子目录; - 调度与同步涉及 include/linux/sched.h、kernel/sched/ 与 include/linux/completion.h;
- 时间与定时器以 include/linux/jiffies.h、kernel/time/ 为核心;
- 高精度定时器见 include/linux/hrtimer.h 与 kernel/time/hrtimer.c;
- 等待队列见 include/linux/wait.h 与 kernel/sched/wait.c;
- 内核线程见 include/linux/kthread.h 与 kernel/kthread.c;
- 引用计数见 include/linux/refcount.h 与 include/linux/percpu-refcount.h;
- 原子操作见 include/linux/atomic/;
- kobject 见 lib/kobject.c 与 lib/kobject_uevent.c;
- 工具函数散布于
include/linux/各头文件; - 设备资源管理(devres)实现在 drivers/base/devres.c。
编写驱动时,可将本文作为"索引 + 速查":遇到同步问题查 completion/等待队列,遇到时间问题查 jiffies/hrtimer,遇到生命周期问题查 refcount/devres,再回到各源码文件的 kernel-doc 注释获取每个函数参数的权威说明。
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考