针对特定场景最好的Linux内核锁定机制是什么

发布于 2024-07-23 06:48:23 字数 543 浏览 8 评论 0原文

我需要解决这个场景的锁定问题:

  1. 多CPU系统。
  2. 所有 CPU 都使用公共(软件)资源。
  3. 对资源的只读访问非常常见。 (处理传入的网络数据包)
  4. 写访问的频率要低得多。 (仅进行了大部分配置更改)。

目前我使用read_lock_bhwrite_lock_bh(自旋锁)机制。 问题是 CPU 越多,在编写器上下文中软锁定就越多。

我读了这本书中的并发章节, 但不太明白使用自旋锁时,读者还是作者会获得优先权。

所以问题是:

  1. Linux 自旋锁机制是否优先考虑读者/作者/他们都不优先?
  2. 我是否可以使用更好的机制来避免我的场景中的软锁定,或者在使用我当前的解决方案时,我可以在编写器尝试获取锁时优先考虑它?

谢谢, 尼尔

I need to solve a locking problem for this scenario:

  1. A multi CPU system.
  2. All of the CPU's use a common (software) resource.
  3. Read only access to the resource is very common. (Processing of incoming network packets)
  4. Write access is a lot less frequent. (Pretty much configuration changes only).

Currently I use the read_lock_bh, write_lock_bh (spinlocks) mechanism.
The problem is that the more CPU's, the more I get soft lockups in a writer context.

I read the concurrency chapter in this book,
But couldn't quite understand whether the reader or the writer will get priority when using spin locks.

So the questions are:

  1. Does the Linux spinlock mechanism give priority the reader/writer/none of them?
  2. Is there a better mechanism I can use in order to avoid those soft lockups in my scenario, or maybe a way for me to give priority to the writer whenever it tries to obtain the lock, while using my current solution?

Thanks,
Nir

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

北方的巷 2024-07-30 06:48:23

以下是 Essential Linux Device Drivers 的直接引用,可能正是您正在寻找的内容。 看来最后处理 RCU 的部分可能是您感兴趣的。

读写锁

另一种专门的并发调节机制是自旋锁的读写变体。 如果使用一个
关键部分是这样的,单独的线程可以读取或写入共享数据结构,但不这样做
这两把锁都是天生的一对。 允许多个读取器线程同时进入临界区域。
读取器自旋锁定义如下:

rwlock_t myrwlock = RW_LOCK_UNLOCKED;

read_lock(&myrwlock);             /* Acquire reader lock */
/* ... Critical Region ... */
read_unlock(&myrwlock);           /* Release lock */

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

rwlock_t myrwlock = RW_LOCK_UNLOCKED;

write_lock(&myrwlock);            /* Acquire writer lock */
/* ... Critical Region ... */
write_unlock(&myrwlock); /* Release lock */

查看 net/ipx/ipx_route.c 中的 IPX 路由代码,了解读写器自旋锁的真实示例。 A
名为 ipx_routes_lock 的读写器锁可保护 IPX 路由表免遭同时访问。 线程数
需要查找路由表来转发数据包请求读卡器锁。 需要添加或添加的线程
从路由表中删除条目获取写入器锁。 这可以提高性能,因为通常有
路由表查找的实例远多于路由表更新的实例。

与常规自旋锁一样,读写锁也有相应的 irq 变体,即 read_lock_irqsave(),
read_lock_irqrestore()write_lock_irqsave()write_lock_irqrestore()。 这些的语义
功能与常规自旋锁类似。

序列锁或 seqlocks,在 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;
}

编写器使用 write_seqlock() 和 write_sequnlock() 保护关键区域。

2.6 内核引入了另一种名为读-复制更新 (RCU) 的机制,它的性能得到了改进
当读者数量远多于作者时的表现
。 基本思想是读取器线程可以在没有
锁定。 编写器线程更加复杂。 他们对数据结构的副本执行更新操作
替换读者看到的指针。 原始副本将一直保留到所有 CPU 上的下一次上下文切换为止
确保完成所有正在进行的读取操作。 请注意,使用 RCU 比使用
到目前为止讨论的原语,只有在您确定它是适合该工作的工具时才应使用。 远程控制单元数据
结构体和接口函数在 include/linux/rcupdate.h 中定义。 有足够的文档
文档/RCU/*

有关 RCU 使用示例,请查看 fs/dcache.c。 在 Linux 上,每个文件都与目录条目关联
信息(存储在称为 dentry 的结构中)、元数据信息(存储在 inode 中)和实际数据
(存储在数据块中)。 每次对文件进行操作时,都会解析文件路径中的组成部分,并将
获得相应的dentry。 目录项被缓存在称为 dcache 的数据结构中,以
加快未来的运营。 任何时候,dcache查找的次数都远远多于dcache更新的次数,所以
对 dcache 的引用使用 RCU 原语进行保护。

Here's a direct quote from Essential Linux Device Drivers which might be what you're looking for. It seems the part dealing with RCU at the end may be what you're interested in.

Reader-Writer Locks

Another specialized concurrency regulation mechanism is a reader-writer variant of spinlocks. If the usage of a
critical section is such that separate threads either read from or write to a shared data structure, but don't do
both, these locks are a natural fit. Multiple reader threads are allowed inside a critical region simultaneously.
Reader spinlocks are defined as follows:

rwlock_t myrwlock = RW_LOCK_UNLOCKED;

read_lock(&myrwlock);             /* Acquire reader lock */
/* ... Critical Region ... */
read_unlock(&myrwlock);           /* Release lock */

However, if a writer thread enters a critical section, other reader or writer threads are not allowed inside. To use
writer spinlocks, you would write this:

rwlock_t myrwlock = RW_LOCK_UNLOCKED;

write_lock(&myrwlock);            /* Acquire writer lock */
/* ... Critical Region ... */
write_unlock(&myrwlock); /* Release lock */

Look at the IPX routing code present in net/ipx/ipx_route.c for a real-life example of a reader-writer spinlock. A
reader-writer lock called ipx_routes_lock protects the IPX routing table from simultaneous access. Threads
that need to look up the routing table to forward packets request reader locks. Threads that need to add or
delete entries from the routing table acquire writer locks. This improves performance because there are usually
far more instances of routing table lookups than routing table updates.

Like regular spinlocks, reader-writer locks also have corresponding irq variants—namely, read_lock_irqsave(),
read_lock_irqrestore(), write_lock_irqsave(), and write_lock_irqrestore(). The semantics of these
functions are similar to those of regular spinlocks.

Sequence locks or seqlocks, introduced in the 2.6 kernel, are reader-writer locks where writers are favored over
readers. This is useful if write operations on a variable far outnumber read accesses. An example is the
jiffies_64 variable discussed earlier in this chapter. Writer threads do not wait for readers who may be inside
a critical section. Because of this, reader threads may discover that their entry inside a critical section has failed
and may need to retry:

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;
}

Writers protect critical regions using write_seqlock() and write_sequnlock().

The 2.6 kernel introduced another mechanism called Read-Copy Update (RCU), which yields improved
performance when readers far outnumber writers
. The basic idea is that reader threads can execute without
locking. Writer threads are more complex. They perform update operations on a copy of the data structure and
replace the pointer that readers see. The original copy is maintained until the next context switch on all CPUs to
ensure completion of all ongoing read operations. Be aware that using RCU is more involved than using the
primitives discussed thus far and should be used only if you are sure that it's the right tool for the job. RCU data
structures and interface functions are defined in include/linux/rcupdate.h. There is ample documentation in
Documentation/RCU/*.

For an RCU usage example, look at fs/dcache.c. On Linux, each file is associated with directory entry
information (stored in a structure called dentry), metadata information (stored in an inode), and actual data
(stored in data blocks). Each time you operate on a file, the components in the file path are parsed, and the
corresponding dentries are obtained. The dentries are kept cached in a data structure called the dcache, to
speed up future operations. At any time, the number of dcache lookups is much more than dcache updates, so
references to the dcache are protected using RCU primitives.

写下不归期 2024-07-30 06:48:23

这不是 RCU 旨在处理的用例吗? 请参阅 http://lwn.net/Articles/262464/ 了解有关其使用的精彩文章。

Isn't this the sort of usage case RCU is designed to handle? See http://lwn.net/Articles/262464/ for a good write up on it's use.

つ可否回来 2024-07-30 06:48:23

如果您在持有锁时所做的工作很小,您可以尝试普通的互斥锁,非读写器。 效率更高。

If the work you do while holding the lock is small you can try a normal mutex, non reader-writer. It's more efficient.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文