【linux kernel 入门及渐进 2 -- linux 内核同步机制之spinlock】

1.1 spinlock 种类

1.1.1 spin_lock

很多时候我们并不能采用其他的锁,比如读写锁、互斥锁、信号量等。一方面这些锁会发生上下文切换,他的时间是不可预期的,对于一些简单的、极短的临界区完全是一种性能损耗;另一方面在中断上下文是不允许睡眠的,除了自旋锁以外的其他任何形式的锁都有可能导致睡眠或者进程切换,这是违背了中断的设计初衷,会发生不可预知的错误。基于两点,我们需要自旋锁,他是不可替代的。

1.1.2 spin_lock_irq

如果被保护的共享资源在软中断(包括 tasklet 和 timer)或进程上下文和硬中断上下文访问,那么在软中断或进程上下文访问期间,可能被硬中断打断,从而进入硬中断上下文对共享资源进行访问,因此,在进程或软中断上下文需要使用spin_lock_irqspin_unlock_irq 来保护对共享资源的访问。

当然,由于需要关闭中断,会导致系统的响应降低,而且还会执行更多的代码,可能会导致性能下降。

1.1.3 spin_lock_irqsave

当进 程在执行第一个 spin_lock_irq(&lock1) 时已经关闭了中断,然后继续获取第二个自旋锁 spin_lock_irq(&lock2)当 lock2 解锁后且 lock1 未解锁前这一段区域处于一种硬件中断开启的状态,这段代码仍然处于 lock1 的临界区,如果此时硬件中断也需要申请 lock1 锁会导致死锁,这是因为 spin_unlock_irq 在解锁时一定会将本地 CPU 的中断打开,从而导致硬件中断可以重新强制抢占 CPU,所以最好的方式就是 lock2 在锁之前保存当前的中断状态,在解锁时恢复当前的中断状态,这也就是 spin_lock_irqsave 需要存在的原因。自然地,这是一种更为线程安全的方案,但是他会带来比前面几种更加多的性能损耗。

在使用 spin_lock_irqspin_unlock_irq 的情况下,完全可以用 spin_lock_irqsavespin_unlock_irqrestore 取代,那具体应该使用哪一个也需要依情况而定,如果可以确信在对共享资源访问前中断是使能的,那么使用 spin_lock_irq 更好一些, 因为它比spin_lock_irqsave 要快一些,但是如果你不能确定是否中断使能,那么使用spin_lock_irqsavespin_unlock_irqrestore 更好,因为它将恢复访问共享资源前的中断标志而不是直接使能中断。

当然,有些情况下需要在访问共享资源时必须中断失效,而访问完后必须中断使能,这样的情形使用 spin_lock_irqspin_unlock_irq 最好。

1.1.4 spin_lock_bh

spin_lock_bh() 主要用于保护其他进程上下文访问软中断函数共享的变量, 因为spin_lock() 无法禁止软中断执行。

共享资源只在进程上下文访问和软中断上下文访问,那么当在进程上下文访问共享资源时,可能被软中断打断,从而可能进入软中断上下文来对被保护的共享资源访问,因此对于这种情况,对共享资源的访问必须使用 spin_lock_bhspin_unlock_bh 来保护。

当然使用 spin_lock_irqspin_unlock_irq 以及 spin_lock_irqsavespin_unlock_irqrestore 也可以,它们失效了本地硬中断,失效硬中断隐式地也失效了软中断。但是使用 spin_lock_bhspin_unlock_bh 是最恰当的,它比其他两个快。

如果被保护的共享资源只在进程上下文和 tasklet 或 timer 上下文访问,那么应该使用与上面情况相同的获得和释放锁的宏,因为 tasklet 和 timer 是用软中断实现的。

1.1.5 单 tasklet 和 timer场景

如果被保护的共享资源只在一个 tasklettimer 上下文访问,那么不需要任何自旋锁保护,因为同一个 tasklet 或 timer 只能在一个 CPU 上运行,即使是在 SMP环境下也是如此。实际上 tasklet 在调用 tasklet_schedule 标记其需要被调度时已经把该 tasklet 绑定到当前 CPU,因此同一 个 tasklet 决不可能同时在其他 CPU上运行。

timer 也是在其被使用 add_timer 添加到 timer 队列中时已经被帮定到当前CPU,所以同一个 timer 绝不可能运行在其他 CPU 上。当然同一个 tasklet 有两个实例同时运行在同一个CPU就更不可能了。

1.1.6 多 tasklet 和 timer 场景

如果被保护的共享资源只在两个或多个 tasklet 或 timer 上下文访问,那么对共享资源的访问仅需要用 spin_lockspin_unlock 来保护,不必使用 _bh 版本,因为当 tasklet 或 timer 运行时,不可能有其他 tasklet 或 timer 在当前CPU上运行。

不同的软中断能够同时在不同的CPU上运行。

1.1.7 单软中断场景

如果被保护的共享资源只在一个软中断(tasklet 和 timer 除外)上下文访问,那么这个共享资源需要用 spin_lockspin_unlock 来保护,因为同样的软中断可以同时在不同的 CPU 上运行。

1.1.8 lock 小结

spin_lock 为了阻止在 不同CPU 上的执行单元对共享资源的同时访问以及不同进程上下文互相抢占导致的对共享资源的非同步访问,

spin_lock_irqspin_lock_bh 是为了阻止在 同一CPU 上软中断或中断对共享资源的非同步访问。

推荐阅读
https://zhuanlan.zhihu.com/p/371721341
https://blog.youkuaiyun.com/dai_xiangjun/article/details/122297475

<think>嗯,用户想了解Linux驱动开发的学习路线,我得好好理清楚思路。首先,用户可能是刚开始接触Linux驱动开发,或者是已经有了一些编程基础,想转行到这个领域。需要从基础开始讲起,逐步深入,确保他能跟上。 首先,Linux驱动开发需要哪些基础知识呢?操作系统原理、C语言应该是必须的。可能还需要了解一些硬件相关的知识,比如处理器架构、内存管理之类的。我得先列出这些基础知识,让用户打好基础。 然后,Linux系统本身的了解也很重要。比如内核的组成、模块化机制,还有编译和调试的方法。用户可能需要学习如何编译内核,如何编写简单的模块,这部分可以通过一些例子来讲解,比如经典的Hello World模块。 接下来是驱动开发的核心部分,字符设备、平台设备、设备树这些概念。用户需要理解这些驱动类型的工作原理,以及如何实际操作。比如,如何注册字符设备,如何使用设备树来描述硬件。这部分可能需要结合代码示例,但用户现在可能还不需要写代码,所以先解释概念。 另外,同步与互斥机制、中断处理、DMA这些高级主题也是必须的。用户可能会遇到多线程环境下的资源竞争问题,或者需要处理硬件中断,这些都需要详细说明。不过要分阶段,先掌握基础再深入高级内容。 调试和优化技巧也很关键,没有这些在实际开发中会遇到很多困难。比如如何使用printk和动态调试,还有性能优化的方法。用户需要知道如何排查问题,提高驱动效率。 最后,推荐一些学习资源和项目实践,帮助用户巩固知识。书籍、在线文档、社区论坛都是不错的资源。实际项目比如LED驱动、传感器驱动,能让他动手实践,加深理解。 可能用户还想知道学习的大致时间安排,或者有哪些常见的陷阱需要避免。不过目前的问题主要是学习路线,所以先按步骤来,确保每个阶段都有明确的目标和内容。还要注意语言要口语化,避免太技术化的术语,让用户容易理解。 总之,结构应该是先基础,再系统学习,然后核心开发,高级主题,最后调试和项目实践。这样用户能循序渐进,逐步掌握Linux驱动开发的各个方面。需要检查是否有遗漏的重要知识点,比如设备树现在在嵌入式系统中很常用,必须涵盖进去。另外,驱动模型和sysfs这类用户空间接口也很重要,应该提到。</think>Linux驱动开发学习路线可以分为以下阶段,结合理论与实践逐步深入: --- ### **一、基础准备阶段(1-3个月)** 1. **C语言基础** - 重点掌握:指针、结构体、内存管理(malloc/free)、位操作、函数指针 - 推荐书籍:《C和指针》《C Primer Plus》 2. **操作系统原理** - 理解进程/线程、内存管理、中断、I/O机制、内核态与用户态 - 推荐学习:《操作系统导论》《深入理解计算机系统》 3. **Linux系统基础** - 熟练使用Linux命令行(gcc/gdb/makefile) - 掌握Shell脚本编写 - 学习内核源码目录结构(如`drivers/`, `include/`, `arch/`) --- ### **二、Linux内核入门2-4个月)** 1. **内核编译与模块开发** - 实践内核编译(配置选项、交叉编译) - 编写第一个内核模块(Hello World) - 学习模块加载/卸载(`insmod/rmmod`) 2. **内核关键机制** - 理解系统调用、Proc文件系统、Sysfs - 掌握内核日志(`printk`)与动态调试(`dmesg`) 3. **设备驱动模型** - 学习字符设备驱动(`cdev`)、平台设备驱动(`platform_device/platform_driver`) - 掌握设备树(Device Tree)语法与使用 --- ### **三、驱动开发核心(3-6个月)** 1. **硬件交互基础** - 内存映射(`ioremap`)、寄存器操作、GPIO控制 - 中断处理(`request_irq`)、延时机制(`mdelay`) 2. **典型驱动开发** - **字符设备**:按键、LED、ADC - **块设备**:RAMDisk模拟 - **网络设备**:虚拟网卡驱动 - **输入子系统**:触摸屏、键盘驱动 3. **同步与并发** - 自旋锁(`spinlock`)、互斥锁(`mutex`)、信号量(`semaphore`) - 原子操作(`atomic_t`)、等待队列(`wait_queue`) --- ### **四、进阶技术(3-6个月)** 1. **内核调试与优化** - 使用`ftrace`、`kprobe`分析性能 - 内存泄漏检测(`kmemleak`)、死锁排查 2. **高级驱动技术** - DMA传输(`dma_alloc_coherent`) - 内核线程(`kthread`)、工作队列(`workqueue`) - 电源管理(`PM Framework`) 3. **实战项目** - 实现SPI/I2C设备驱动(如传感器) - 开发USB摄像头驱动(结合V4L2框架) - 移植开源驱动(如WiFi模块) --- ### **五、学习资源推荐** 1. **书籍** -Linux设备驱动程序》(经典必读) -Linux内核设计与实现》 - 《精通Linux设备驱动程序开发》 2. **在线文档** - 内核文档(`Documentation/`目录) - [Linux Kernel Module Programming Guide](https://tldp.org/LDP/lkmpg/2.6/html/) 3. **社区与工具** - 订阅[LKML](https://lore.kernel.org/)邮件列表 - 使用QEMU模拟器调试驱动 - 参与开源项目(如Raspberry Pi驱动开发) --- ### **六、避坑指南** 1. **硬件依赖问题**:优先选择支持良好的开发板(如树莓派、BeagleBone)。 2. **内核版本兼容性**:注意API变化(如`register_chrdev` vs `cdev_init`)。 3. **调试技巧**:善用`CONFIG_DEBUG_KERNEL`和`objdump`反汇编。 --- **学习路线总结**: 基础 → 内核机制 → 驱动开发 → 调试优化 → 项目实战 建议每天投入2-3小时,配合开发板实践,1年左右可达到中级水平。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

主公讲 ARM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值