09 内核开发-避免冲突和死锁-spinlock

08 内核开发-避免冲突和死锁-spinlock

课程简介:
Linux内核开发入门是一门旨在帮助学习者从最基本的知识开始学习Linux内核开发的入门课程。该课程旨在为对Linux内核开发感兴趣的初学者提供一个扎实的基础,让他们能够理解和参与到Linux内核的开发过程中。

课程特点:
1. 入门级别:该课程专注于为初学者提供Linux内核开发的入门知识。无论你是否具有编程或操作系统的背景,该课程都将从最基本的概念和技术开始,逐步引导学习者深入了解Linux内核开发的核心原理。

2. 系统化学习:课程内容经过系统化的安排,涵盖了Linux内核的基础知识、内核模块编程、设备驱动程序开发等关键主题。学习者将逐步了解Linux内核的结构、功能和工作原理,并学习如何编写和调试内核模块和设备驱动程序。

3. 实践导向:该课程强调实践,通过丰富的实例和编程练习,帮助学习者将理论知识应用到实际的Linux内核开发中。学习者将有机会编写简单的内核模块和设备驱动程序,并通过实际的测试和调试来加深对Linux内核开发的理解。

4. 配套资源:为了帮助学习者更好地掌握课程内容,该课程提供了丰富的配套资源,包括教学文档、示例代码、实验指导和参考资料等。学习者可以根据自己的学习进度和需求,灵活地利用这些资源进行学习和实践。

无论你是计算机科学专业的学生、软件工程师还是对Linux内核开发感兴趣的爱好者,Linux内核开发入门课程都将为你提供一个扎实的学习平台,帮助你掌握Linux内核开发的基础知识,为进一步深入研究和应用Linux内核打下坚实的基础。

这一讲,主要分享如何在内核开模块开发中如何使用 spinlock 避免冲突和死锁。

1.定义

自旋锁是一种轻量级锁,用于保护临界区,即必须互斥访问的代码段。它们通过忙循环的方式工作,这意味着当锁被获取时,它会一直旋转,直到锁被释放。

2.内涵

自旋锁通过原子指令来操作一个标志位来实现。当一个线程需要获取锁时,它会不断检查标志位,直到它变为可用状态。一旦标志位变为可用,线程会立即设置标志位以指示它已获取锁。

与互斥锁的区别,与互斥锁相比,自旋锁具有以下特点:

  1. 更轻量级:自旋锁不会引起线程阻塞,因为它只进行忙循环而不是进入睡眠状态。
  2. 更快的争用:在低争用的情况下,自旋锁比互斥锁更有效率。


然而,自旋锁也有以下缺点:

  1. CPU 消耗:在高争用的情况下,自旋锁会导致 CPU 消耗过大,因为线程会不断进行忙循环。
  2. 不公平:自旋锁可能会导致优先级较低的线程无限期地等待,因为优先级较高的线程会在忙循环中反复获取锁。

3.使用示例

static spinlock_t sl_dynamic;  #定义 
spin_lock_init()
spin_lock_irqsave():获取自旋锁。
spin_unlock_irqrestore():释放自旋锁。


spin_lock_init()

功能:初始化一个自旋锁。

参数:

lock:要初始化的自旋锁。
描述:

spin_lock_init() 初始化自旋锁 lock 为未锁定的状态。它必须在使用自旋锁之前调用。

spin_lock_irqsave()

功能:获取自旋锁并禁用中断。

参数:

lock:要获取的自旋锁。
flags:一个指针,用于存储被禁用的中断标志。
描述:

spin_lock_irqsave() 获取自旋锁 lock 并禁用中断。它在获取锁之前将当前的中断标志存储在 flags 中。这确保了在临界区内不会发生中断。

spin_unlock_irqrestore()
功能:释放自旋锁并恢复中断。
参数:
lock:要释放的自旋锁。
flags:先前由 spin_lock_irqsave() 存储的中断标志。

描述:
spin_unlock_irqrestore() 释放自旋锁 lock 并恢复先前由 spin_lock_irqsave() 禁用的中断。它使用 flags 参数来恢复中断标志。
某个使用使用场景

spin_lock_irqsave() 和 spin_unlock_irqrestore() 通常一起使用来保护临界区,其中需要禁用中断以确保独占访问。示例以下是一个使用 spin_lock_irqsave() 和 spin_unlock_irqrestore() 来保护共享计数器的示例:

static DEFINE_SPINLOCK(my_spinlock);
static int shared_counter;

void increment_counter(void)
{
    unsigned long flags;

    // 获取自旋锁并禁用中断
    spin_lock_irqsave(&my_spinlock, flags);

    // 在临界区内执行操作
    shared_counter++;

    // 释放自旋锁并恢复中断
    spin_unlock_irqrestore(&my_spinlock, flags);
}


此示例使用 spin_lock_irqsave() 获取自旋锁并禁用中断,然后在临界区内递增 shared_counter。
然后它使用 spin_unlock_irqrestore() 释放自旋锁并恢复中断。

4.具体代码实践


/****** hello.c ******/

#include <linux/init.h> 
#include <linux/module.h> 
#include <linux/printk.h> 
#include <linux/spinlock.h> 
 
static DEFINE_SPINLOCK(sl_static); 
static spinlock_t sl_dynamic; 
 
static void example_spinlock_static(void) 
{ 
    unsigned long flags; 
 
    spin_lock_irqsave(&sl_static, flags); 
    pr_info("Locked static spinlock\n"); 
 
    /* Do something or other safely. Because this uses 100% CPU time, this 
     * code should take no more than a few milliseconds to run. 
     * 适合时间不太长的任务
     */ 
    spin_unlock_irqrestore(&sl_static, flags); 
    pr_info("Unlocked static spinlock\n"); 
} 
 
static void example_spinlock_dynamic(void) 
{ 
    unsigned long flags; 
 
    spin_lock_init(&sl_dynamic); 
    spin_lock_irqsave(&sl_dynamic, flags); 
    pr_info("Locked dynamic spinlock\n"); 
 
    /* Do something or other safely. Because this uses 100% CPU time, this 
     * code should take no more than a few milliseconds to run. 
     * 适合时间不太长的任务
     */ 
    spin_unlock_irqrestore(&sl_dynamic, flags); 
    pr_info("Unlocked dynamic spinlock\n"); 
} 
 

static int __init example_spinlock_init(void) 
{ 
    pr_info("demo spinlock started\n"); 
    example_spinlock_static(); 
    example_spinlock_dynamic(); 
 
    return 0; 
} 
 
static void __exit example_spinlock_exit(void) 
{ 
    pr_info("demo spinlock exit\n"); 
} 
 
module_init(example_spinlock_init); 
module_exit(example_spinlock_exit); 
 
MODULE_DESCRIPTION("Spinlock demo"); 
MODULE_LICENSE("GPL");


使用 make编译,sudo insmod hello.ko 安装模块后, 通过dmesg 查看日这日志信息

5.注意事项

Linux 内核中自旋锁 spinlock 的注意事项:

  1. 避免长时间持有自旋锁:自旋锁在持有时会消耗大量 CPU,因此应避免在临界区内进行长时间操作。
  2. 避免嵌套自旋锁:在持有自旋锁时获取另一个自旋锁可能会导致死锁。
  3. 优先使用自旋锁来保护小而经常被访问的临界区:自旋锁在争用率低时非常有效,但争用率高时效率会下降。
  4. 考虑使用其他同步原语,例如互斥锁或读写锁,来保护大或不频繁访问的临界区:这些同步原语在高争用情况下比自旋锁更有效率。

其他注意事项:

  1. 自旋锁在多处理器系统上比在单处理器系统上更有效率,因为线程可以在不同的处理器上同时旋转。
  2. 使用自旋锁时,应禁用中断,以避免中断处理程序获取锁并导致死锁。
  3. 自旋锁可以与原子操作结合使用,以实现更精细的同步。
6.最佳实践
  1. 仅在绝对必要时使用自旋锁:考虑使用其他同步原语,例如互斥锁或读写锁,这些原语在高争用情况下更有效率。
  2. 保持临界区尽可能小:这将减少自旋锁被持有的时间,从而提高整体性能。
  3. 避免在自旋锁的临界区内进行阻塞操作:这将导致 CPU 浪费,因为线程会一直旋转而不会释放锁。
  4. 使用自旋锁来保护共享数据结构时,要小心死锁:确保自旋锁的获取顺序不会导致循环等待。
  5. 使用自旋锁来保护中断处理程序时,要小心优先级反转:确保自旋锁不会被优先级较低的线程长时间持有,从而导致优先级较高的线程饥饿。

7.总结

自旋锁只在争用率低的情况下比互斥锁更有效率。如果临界区经常被争用,那么互斥锁通常是更好的选择。

优点自缺点
自旋锁
低开销:自旋锁比互斥锁和其他同步原语具有更低的开销,因为它们不会导致线程被阻塞。
快速响应:自旋锁具有更快的响应时间,因为它们不会导致线程被阻塞。
CPU 消耗:如果自旋锁被长时间持有,则会消耗大量 CPU,因为线程会一直旋转而不会释放锁。
优先级反转:如果自旋锁被优先级较低的线程长时间持有,则会导致优先级较高的线程饥饿。

  • 10
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值