什么是特定场景的最佳Linux内核锁定机制

Nir*_*Nir 6 concurrency linux-kernel mutual-exclusion

我需要为这种情况解决锁定问题:

  1. 一个多CPU系统.
  2. 所有CPU都使用通用(软件)资源.
  3. 只读访问资源非常常见.(传入网络数据包的处理)
  4. 写访问频率低得多.(仅限配置更改).

目前我使用的read_lock_bh,write_lock_bh(自旋锁)机制.问题是CPU越多,我在编写器上下文中获得的软锁定就越多.

我阅读了本书中的并发章节,但是在使用自旋锁时,无法理解读者或作者是否会获得优先权.

所以问题是:

  1. Linux自旋锁机制是否优先考虑读者/作者/没有人?
  2. 有没有一种更好的机制可以用来避免我的场景中的那些软锁定,或者在使用我当前的解决方案时,可能是一种让我在编写器获得锁定时优先考虑的方法?

谢谢,Nir

Rob*_*nes 6

以下是Essential Linux设备驱动程序的直接引用,可能是您正在寻找的内容.似乎最后处理RCU的部分可能是你感兴趣的部分.

读者 - 作家锁

另一种专门的并发调节机制是自旋锁的读写器变体.如果关键部分的使用使得单独的线程可以读取或写入共享数据结构,但不同时执行这两种操作,则这些锁定是自然的.允许多个读取器线程同时在关键区域内.Reader自旋锁的定义如下:

rwlock_t myrwlock = RW_LOCK_UNLOCKED;

read_lock(&myrwlock);             /* Acquire reader lock */
/* ... Critical Region ... */
read_unlock(&myrwlock);           /* Release lock */
Run Code Online (Sandbox Code Playgroud)

但是,如果编写器线程进入临界区,则不允许其他读取器或写入器线程进入.要使用编写器自旋锁,您可以这样写:

rwlock_t myrwlock = RW_LOCK_UNLOCKED;

write_lock(&myrwlock);            /* Acquire writer lock */
/* ... Critical Region ... */
write_unlock(&myrwlock); /* Release lock */
Run Code Online (Sandbox Code Playgroud)

查看存在的IPX路由代码,以net/ipx/ipx_route.c获得读写器自旋锁的实际示例.被称为读写器锁的ipx_routes_lock保护IPX路由表不能同时访问.需要查找路由表以转发数据包请求读取器锁的线程.需要在路由表中添加或删除条目的线程获取写入器锁.这提高了性能,因为路由表查找的实例通常远多于路由表更新.

像普通自旋锁,读写锁也有相应的IRQ变型,即read_lock_irqsave(), read_lock_irqrestore(),write_lock_irqsave(),和write_lock_irqrestore().这些函数的语义类似于常规自旋锁的语义.

2.6内核中引入的序列锁或序列锁是读写器锁,其中编写者比读者更受青睐.如果对远远超出读取的变量进行写操作,则此操作非常有用.一个例子是jiffies_64本章前面讨论过的 变量.作者线程不等待可能在关键部分内的读者.因此,读者线程可能会发现他们在关键部分内的输入失败并可能需要重试:

u64 get_jiffies_64(void) /* Defined in kernel/time.c */
{
   unsigned long seq;
   u64 ret;
   do {
      seq = read_seqbegin(&xtime_lock);
      ret = jiffies_64;
   } while (read_seqretry(&xtime_lock, seq));
   return ret;
}
Run Code Online (Sandbox Code Playgroud)

作家使用write_seqlock()和保护关键区域write_sequnlock().

2.6内核引入了另一种称为读取副本更新(RCU)的机制,当读者数量远远超过编写者时,可以提高性能.基本思想是读者线程可以在不锁定的情况下执行.编写器线程更复杂.它们对数据结构的副本执行更新操作,并替换读者看到的指针.保留原始副本,直到所有CPU上的下一个上下文切换,以确保完成所有正在进行的读取操作.请注意,使用RCU比使用到目前为止讨论的原语更为复杂,只有在您确定它是正确的工具时才应使用RCU.RCU数据结构和接口函数在中定义include/linux/rcupdate.h.有充足的文档 Documentation/RCU/*.

有关RCU用法示例,请查看fs/dcache.c.在Linux上,每个文件都与目录条目信息(存储在名为dentry的结构中),元数据信息(存储在inode中)和实际数据(存储在数据块中)相关联.每次对文件进行操作时,都会解析文件路径中的组件,并获取相应的dentry.dentries保存在名为dcache的数据结构中,以加快将来的操作.在任何时候,dcache查找的数量远远超过dcache更新,因此使用RCU原语来保护对dcache的引用.