I need to solve a locking problem for this scenario:
我需要为这种情况解决锁定问题:
- A multi CPU system.
- All of the CPU's use a common (software) resource.
- Read only access to the resource is very common. (Processing of incoming network packets)
- Write access is a lot less frequent. (Pretty much configuration changes only).
多CPU系统。
所有CPU都使用通用(软件)资源。
只读访问资源非常常见。 (传入网络数据包的处理)
写访问频率低得多。 (仅限配置更改)。
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.
目前我使用read_lock_bh,write_lock_bh(自旋锁)机制。问题是CPU越多,我在编写器上下文中获得软锁定的次数就越多。
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:
所以问题是:
- Does the Linux spinlock mechanism give priority the reader/writer/none of them?
- 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?
Linux自旋锁机制是否优先考虑读者/作者/没有?
我可以使用更好的机制来避免我的场景中的那些软锁定,或者在使用我当前的解决方案时,可能是一种方法让我在编写器获得锁定时优先考虑它?
Thanks, Nir
3 个解决方案
#1
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.
以下是Essential Linux设备驱动程序的直接引用,可能是您正在寻找的内容。似乎最后处理RCU的部分可能是你感兴趣的部分。
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:
另一种专门的并发调节机制是自旋锁的读写器变体。如果关键部分的使用使得单独的线程可以读取或写入共享数据结构,但不同时执行这两种操作,则这些锁定是自然的。允许多个读取器线程同时在关键区域内。 Reader自旋锁定义如下:
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.
查看net / ipx / ipx_route.c中的IPX路由代码,了解读写器自旋锁的实际示例。名为ipx_routes_lock的读写器锁可以保护IPX路由表不被同时访问。需要查找路由表以转发数据包请求读取器锁的线程。需要在路由表中添加或删除条目的线程获取写入器锁。这提高了性能,因为路由表查找的实例通常远多于路由表更新。
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.
与常规自旋锁一样,读写器锁也具有相应的irq变体 - 即read_lock_irqsave(),read_lock_irqrestore(),write_lock_irqsave()和write_lock_irqrestore()。这些函数的语义类似于常规自旋锁的语义。
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:
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;
}
Writers protect critical regions using write_seqlock()
and write_sequnlock()
.
编写器使用write_seqlock()和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/*
.
2.6内核引入了另一种称为读取副本更新(RCU)的机制,当读者数量远远超过编写者时,可以提高性能。基本思想是读者线程可以在没有锁定的情况下执行。编写者线程更复杂。它们对数据结构的副本执行更新操作,并替换读者看到的指针。保留原始副本,直到所有CPU上的下一个上下文切换,以确保完成所有正在进行的读取操作。请注意,使用RCU比使用到目前为止讨论的原语更为复杂,只有在您确定它是适合该作业的工具时才应使用RCU。 RCU数据结构和接口函数在include / linux / rcupdate.h中定义。 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.
有关RCU用法示例,请查看fs / dcache.c。在Linux上,每个文件与目录条目信息(存储在名为dentry的结构中),元数据信息(存储在inode中)和实际数据(存储在数据块中)相关联。每次对文件进行操作时,都会解析文件路径中的组件,并获取相应的dentry。 dentries保存在名为dcache的数据结构中,以加快将来的操作。在任何时候,dcache查找的数量远远超过dcache更新,因此使用RCU原语来保护对dcache的引用。
#2
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.
这不是RCU旨在处理的用法案例吗?请参阅http://lwn.net/Articles/262464/以获得有关其使用的详细信息。
#3
If the work you do while holding the lock is small you can try a normal mutex, non reader-writer. It's more efficient.
如果您在握住锁定时所做的工作很小,您可以尝试使用普通的互斥锁,非读写器。它效率更高。
#1
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.
以下是Essential Linux设备驱动程序的直接引用,可能是您正在寻找的内容。似乎最后处理RCU的部分可能是你感兴趣的部分。
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:
另一种专门的并发调节机制是自旋锁的读写器变体。如果关键部分的使用使得单独的线程可以读取或写入共享数据结构,但不同时执行这两种操作,则这些锁定是自然的。允许多个读取器线程同时在关键区域内。 Reader自旋锁定义如下:
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.
查看net / ipx / ipx_route.c中的IPX路由代码,了解读写器自旋锁的实际示例。名为ipx_routes_lock的读写器锁可以保护IPX路由表不被同时访问。需要查找路由表以转发数据包请求读取器锁的线程。需要在路由表中添加或删除条目的线程获取写入器锁。这提高了性能,因为路由表查找的实例通常远多于路由表更新。
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.
与常规自旋锁一样,读写器锁也具有相应的irq变体 - 即read_lock_irqsave(),read_lock_irqrestore(),write_lock_irqsave()和write_lock_irqrestore()。这些函数的语义类似于常规自旋锁的语义。
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:
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;
}
Writers protect critical regions using write_seqlock()
and write_sequnlock()
.
编写器使用write_seqlock()和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/*
.
2.6内核引入了另一种称为读取副本更新(RCU)的机制,当读者数量远远超过编写者时,可以提高性能。基本思想是读者线程可以在没有锁定的情况下执行。编写者线程更复杂。它们对数据结构的副本执行更新操作,并替换读者看到的指针。保留原始副本,直到所有CPU上的下一个上下文切换,以确保完成所有正在进行的读取操作。请注意,使用RCU比使用到目前为止讨论的原语更为复杂,只有在您确定它是适合该作业的工具时才应使用RCU。 RCU数据结构和接口函数在include / linux / rcupdate.h中定义。 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.
有关RCU用法示例,请查看fs / dcache.c。在Linux上,每个文件与目录条目信息(存储在名为dentry的结构中),元数据信息(存储在inode中)和实际数据(存储在数据块中)相关联。每次对文件进行操作时,都会解析文件路径中的组件,并获取相应的dentry。 dentries保存在名为dcache的数据结构中,以加快将来的操作。在任何时候,dcache查找的数量远远超过dcache更新,因此使用RCU原语来保护对dcache的引用。
#2
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.
这不是RCU旨在处理的用法案例吗?请参阅http://lwn.net/Articles/262464/以获得有关其使用的详细信息。
#3
If the work you do while holding the lock is small you can try a normal mutex, non reader-writer. It's more efficient.
如果您在握住锁定时所做的工作很小,您可以尝试使用普通的互斥锁,非读写器。它效率更高。