10 内核开发-避免冲突和死锁-读写锁

10 内核开发-避免冲突和死锁-读写锁

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

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

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

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

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

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

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


1.定义


在 Linux 内核中,读写锁是一种同步原语,用于协调对共享资源的并发访问。它允许多个线程或进程同时获取读锁以读取共享资源,或者允许单个线程或进程获取写锁以独占访问并修改共享资源。

读写锁通常用于保护内核中的数据结构,这些数据结构可能同时被多个线程或进程访问和修改。例如,文件系统代码使用读写锁来保护文件系统中的数据结构,如 inode 和 dentry。


2.内涵


读写锁含义

 读锁:允许多个线程或进程同时获取,以读取共享资源。当持有读锁时,其他线程或进程也可以获取读锁,但不能获取写锁。
 写锁:允许单个线程或进程独占获取,以修改共享资源。当持有写锁时,其他线程或进程都不能获取读锁或写锁。

读写锁的优点

  1.  提高并发性:读写锁允许多个线程或进程同时读取共享资源,从而提高并发性。
  2.  防止数据竞争:读写锁通过防止多个线程或进程同时写入共享资源来防止数据竞争。
  3.  提高性能:与互斥锁相比,读写锁在读操作比写操作更频繁的情况下可以提高性能,因为多个线程或进程可以同时获取读锁。

读写锁的缺点

  1.  优先级反转:读写锁可能会导致优先级反转,其中低优先级线程或进程持有写锁,而高优先级线程或进程等待读锁。
  2.  死锁:如果线程或进程不正确地使用读写锁,可能会导致死锁。


IRQ 安全锁是一种可以在中断上下文中安全获取和释放的锁。内核读写锁API write_lock_irqsave()、write_unlock_irqrestore()、read_lock_irqsave() 和 read_unlock_irqrestore 函数是 Linux 内核提供的 IRQ 安全锁。


3.使用示例

(a)write_lock_irqsave

void write_lock_irqsave(struct rw_semaphore *lock, unsigned long *flags);
描述:

    获取给定读写信号量的写锁,并保存当前的中断标志。

参数:

    lock: 要获取写锁的读写信号量。
    flags: 保存当前中断标志的地址。
返回值:

    无。

行为:

    这个函数原子地获取给定读写信号量的写锁。它首先保存当前的中断标志到 flags 参数中,然后禁用中断,最后获取写锁。

注意:

    必须使用 write_unlock_irqrestore() 函数来释放写锁。

(b)write_unlock_irqrestore

void write_unlock_irqrestore(struct rw_semaphore *lock, unsigned long *flags);
描述:

    释放给定读写信号量的写锁,并恢复之前保存的中断标志。

参数:

    lock: 要释放写锁的读写信号量。
    flags: 之前保存的中断标志。
返回值:

    无。

行为:

    这个函数原子地释放给定读写信号量的写锁。它首先恢复之前保存的中断标志,然后释放写锁。

注意:

    这个函数必须与 write_lock_irqsave() 函数配对使用。

(c)read_lock_irqsave

void read_lock_irqsave(struct rw_semaphore *lock, unsigned long *flags);
描述:

    获取给定读写信号量的读锁,并保存当前的中断标志。

参数:

    lock: 要获取读锁的读写信号量。
    flags: 保存当前中断标志的地址。
返回值:

    无。

行为:

    这个函数原子地获取给定读写信号量的读锁。它首先保存当前的中断标志到 flags 参数中,然后禁用中断,最后获取读锁。

注意:

    必须使用 read_unlock_irqrestore() 函数来释放读锁。

(d)read_unlock_irqrestore

void read_unlock_irqrestore(struct rw_semaphore *lock, unsigned long *flags);
描述:

    释放给定读写信号量的读锁,并恢复之前保存的中断标志。

参数:
    
    lock: 要释放读锁的读写信号量。
    flags: 之前保存的中断标志。
返回值:

    无。

行为:

    这个函数原子地释放给定读写信号量的读锁。它首先恢复之前保存的中断标志,然后释放读锁。

注意:

    这个函数必须与 read_lock_irqsave() 函数配对使用。

IRQ 安全性

这四个函数都是 IRQ 安全的,这意味着它们可以在中断上下文中安全地调用。它们禁用中断以确保原子操作,然后在释放锁时恢复中断。

用法示例

以下示例展示了如何使用这些函数来保护一个共享数据结构:

struct rw_semaphore lock;

void write_to_data(struct data *data)
{
    unsigned long flags;

    write_lock_irqsave(&lock, &flags);

    // 写入数据

    write_unlock_irqrestore(&lock, &flags);
}

int read_from_data(struct data *data)
{
    unsigned long flags;
    int value;

    read_lock_irqsave(&lock, &flags);

    // 读取数据

    value = data->value;

    read_unlock_irqrestore(&lock, &flags);

    return value;
}


在这个示例中,write_to_data() 函数使用写锁保护对 data 结构的写入操作,而 read_from_data() 函数使用读锁保护对 data 结构的读取操作。


4.具体代码使用实践
#include <linux/module.h> 
#include <linux/printk.h> 
#include <linux/rwlock.h> 
 
static DEFINE_RWLOCK(myrwlock); 
 
static void example_read_lock(void) 
{ 
    unsigned long flags; 
 
    read_lock_irqsave(&myrwlock, flags); 
    pr_info("Read Locked\n"); 
 
    /* Read from something */ 
 
    read_unlock_irqrestore(&myrwlock, flags); 
    pr_info("Read Unlocked\n"); 
} 
 
static void example_write_lock(void) 
{ 
    unsigned long flags; 
 
    write_lock_irqsave(&myrwlock, flags); 
    pr_info("Write Locked\n"); 
 
    /* Write to something */ 
 
    write_unlock_irqrestore(&myrwlock, flags); 
    pr_info("Write Unlocked\n"); 
} 
 
static int __init example_rwlock_init(void) 
{ 
    pr_info("example_rwlock started\n"); 
 
    example_read_lock(); 
    example_write_lock(); 
 
    return 0; 
} 
 
static void __exit example_rwlock_exit(void) 
{ 
    pr_info("example_rwlock exit\n"); 
} 
 
module_init(example_rwlock_init); 
module_exit(example_rwlock_exit); 
 
MODULE_DESCRIPTION("Read/Write locks example"); 
MODULE_LICENSE("GPL");

5.注意事项
  1. 不要在中断处理程序中休眠:在持有 IRQ 安全锁时不得休眠。这可能会导致死锁或系统不稳定。
  2. 避免长时间持有锁:长时间持有锁会降低并发性并导致性能问题。
  3. 注意死锁:不正确地使用锁可能会导致死锁。始终确保以正确的顺序获取和释放锁。
  4. 小心优先级反转:优先级反转可能是一个问题,尤其是在嵌套锁的情况下。

其他注意事项:

  1. 这四个函数都禁用中断。在某些情况下,这可能会导致性能下降。
  2. 如果可能,应使用非 IRQ 安全的锁,因为它们通常具有更好的性能。

Linux 内核还提供了其他类型的锁,例如自旋锁和读者-写者自旋锁。这些锁在某些情况下可能更合适。

遵循这些注意事项可以帮助您更有效和安全地使用 write_lock_irqsave()、write_unlock_irqrestore()、read_lock_irqsave() 和 read_unlock_irqrestore() 函数。

6.最佳实践
  1. 仅在需要时使用 IRQ 安全锁:只有在需要在中断上下文中保护共享数据时才使用 write_lock_irqsave()、write_unlock_irqrestore()、read_lock_irqsave() 和 read_unlock_irqrestore() 函数。在非中断上下文中,应使用非 IRQ 安全的锁(如 write_lock() 和 read_lock())。
  2. 尽可能缩小临界区:在持有锁时,应将代码保持在最小限度。这将最大限度地减少持有锁的时间,从而提高并发性。
  3. 避免嵌套锁:嵌套锁可能会导致死锁。如果需要在同一代码路径中获取多个锁,请尝试使用分层锁定策略。
  4. 正确地配对锁调用:始终确保使用 write_unlock_irqrestore() 函数释放 write_lock_irqsave() 获取的写锁,并使用 read_unlock_irqrestore() 函数释放 read_lock_irqsave() 获取的读锁。
  5. 考虑优先级反转:如果高优先级线程持有读锁,而低优先级线程持有写锁,可能会发生优先级反转。为了缓解这种情况,可以考虑使用自旋锁或读者-写者自旋锁。
7.总结

正确使用 write_lock_irqsave(), write_unlock_irqrestore(), read_lock_irqsave() 和 read_unlock_irqrestore()的最佳实践及注意事项。

最佳实践 注意事项
读写锁

仅在需要在中断上下文中保护共享数据时使用这些函数。


尽可能缩小临界区。


避免嵌套锁。


正确配对锁调用(即,使用 write_unlock_irqrestore() 释放 write_lock_irqsave() 获取的锁,使用 read_unlock_irqrestore() 释放 read_lock_irqsave() 获取的锁)。


考虑优先级反转。

不要在中断处理程序中休眠。


避免长时间持有锁。


注意死锁。


小心优先级反转。



 

相关推荐

  1. 10 内核开发-避免冲突-

    2024-04-26 22:44:02       17 阅读
  2. 以及如何避免

    2024-04-26 22:44:02       16 阅读
  3. 问题,4个必要条件+避免

    2024-04-26 22:44:02       45 阅读
  4. linux 内核检测

    2024-04-26 22:44:02       26 阅读
  5. C++

    2024-04-26 22:44:02       22 阅读
  6. Mysql间隙避免最佳实践

    2024-04-26 22:44:02       10 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-26 22:44:02       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-26 22:44:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-26 22:44:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-26 22:44:02       20 阅读

热门阅读

  1. 如何看懂财报 - 财报分析与关键指标

    2024-04-26 22:44:02       19 阅读
  2. 巴西游戏市场海外营销洞察

    2024-04-26 22:44:02       19 阅读
  3. Ubuntu22.04.4 - Redis - 笔记

    2024-04-26 22:44:02       12 阅读
  4. 探索PostegreSQL与MySQL的区别

    2024-04-26 22:44:02       15 阅读
  5. openfeign整合sentinel进行降级

    2024-04-26 22:44:02       16 阅读
  6. 如何实现百万级数据从Excel导入到数据库

    2024-04-26 22:44:02       14 阅读
  7. 字符串简单运算(BigDecimal相关运算)

    2024-04-26 22:44:02       19 阅读
  8. Swift 中如何四舍五入

    2024-04-26 22:44:02       16 阅读
  9. linux文件相关命令

    2024-04-26 22:44:02       15 阅读
  10. MR混合现实实训系统为农学情景实训教学演练

    2024-04-26 22:44:02       14 阅读
  11. Anagrams

    2024-04-26 22:44:02       13 阅读
  12. 每天一个数据分析题(二百八十二)

    2024-04-26 22:44:02       14 阅读
  13. leetCode58. 最后一个单词的长度

    2024-04-26 22:44:02       14 阅读
  14. VL2 异步复位的串联T触发器

    2024-04-26 22:44:02       14 阅读