如何在 Linux 内核中的列表条目上使用自旋锁?
我正在为 Linux 内核开发一个补丁。我必须使用几个 列表,我必须保护它们免受并发修改 多核机器。我正在尝试使用自旋锁来实现这个目标,但是 有件事我无法理解。我必须锁定 a 的条目 列表(我正在使用链表的linux默认实现)和它 进程可能会调用系统调用来删除其中的一个元素 列表中的同一个元素由于某些原因而被锁定 实际上正在对其进行修改。如果我插入一个自旋锁 在列表条目内,如果进程设法删除它会发生什么 当有人对其进行自旋锁定时?我应该锁定整个列表吗? 我正在寻找一段代码来解释如何处理这个问题 情况。
例如,此代码不应该工作(请参阅最后一行的注释 代码):
struct lista{
int c;
spinlock_t lock;
struct list_head;
}
spinlock_t list_lock;
struct lista lista;
//INSERT
struct lista* cursor;
struct lista* new = (struct lista*) kmalloc(sizeof(struct lista),GFP_KERNEL);
/*do something*/
spin_lock(&list_lock); //Lock on the whole list
list_for_each_entry(cursor,&lista.list,list){
if (cursor->c == something ){
...
spin_unlock(&list_lock) //unlock
spin_lock(&cursor->lock) // Lock on list entry
list_add(&new->list, &lista.list);
spin_unlock(&cursor->lock) // unlock of the list entry
...
}
}
//REMOVAL
struct lista* cursor;
spin_lock(&list_lock);
list_for_each_entry(cursor,&lista.list,list){
if (cursor->c == something ){
...
spin_unlock(&list_lock) //unlock
spin_lock(&cursor->lock) // Lock on list entry
list_del(&cursor.list,&lista.list);
spin_unlock(&cursor->lock) // unlock of the list entry
kfree(cursor); //WHEN THE ENTRY IS FREED SOMEONE COULD HAVE TAKEN THE LOCK SINCE IT IS UNLOCKED
...
}
}
你能帮我吗?
I'm developing a patch for the linux kernel. I have to use several
lists and I have to protect'em against concurrent modification on a
multicore machine. I'm trying to use spinlocks for this goal, but
there's something I can't understand. I have to lock the entries of a
list (I'm using linux default implementation of linked lists) and it
can happen that a process invokes a syscall to remove one element of
the list while the same element which is locked because some
modification is actually being made on it. If I insert a spinlock
inside the list entry, what happens if a process manage to remove it
while someone is spinlocking on it?? Should I lock the entire list?
I'm looking for a piece of code that can explain how to do handle this
situation.
For example, this code shouldn't work (see comment on the last line of
code):
struct lista{
int c;
spinlock_t lock;
struct list_head;
}
spinlock_t list_lock;
struct lista lista;
//INSERT
struct lista* cursor;
struct lista* new = (struct lista*) kmalloc(sizeof(struct lista),GFP_KERNEL);
/*do something*/
spin_lock(&list_lock); //Lock on the whole list
list_for_each_entry(cursor,&lista.list,list){
if (cursor->c == something ){
...
spin_unlock(&list_lock) //unlock
spin_lock(&cursor->lock) // Lock on list entry
list_add(&new->list, &lista.list);
spin_unlock(&cursor->lock) // unlock of the list entry
...
}
}
//REMOVAL
struct lista* cursor;
spin_lock(&list_lock);
list_for_each_entry(cursor,&lista.list,list){
if (cursor->c == something ){
...
spin_unlock(&list_lock) //unlock
spin_lock(&cursor->lock) // Lock on list entry
list_del(&cursor.list,&lista.list);
spin_unlock(&cursor->lock) // unlock of the list entry
kfree(cursor); //WHEN THE ENTRY IS FREED SOMEONE COULD HAVE TAKEN THE LOCK SINCE IT IS UNLOCKED
...
}
}
Can you help me??
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
几乎可以肯定,您根本不应该使用自旋锁,除非存在来自硬 IRQ 上下文的并发访问。请改用互斥体。
对于列表来说,最简单的选择就是在操作时锁定整个列表。不要担心每个项目的锁,除非您发现列表锁上有足够的争用,您需要它(在这种情况下,无论如何,您可能想考虑使用 RCU)。
You almost certainly shouldn't be using spinlocks at all, unless there's concurrent access from hard IRQ context. Use mutexes instead.
The easiest option for your list is just to lock the entire list while you operate on it. Don't worry about per-item locks unless and until you find that there's sufficient contention on the list lock that you need it (and in that case, you probably want to look at using RCU instead, anyway).
您的列表头不需要是
struct lista
,它应该只是一个struct list_head
。请注意,您继续使用&lista.list
,它应该只是一个名为“list”或其他名称的list_head
。例如,请参阅drivers/pci/msi.c
中的代码,请注意dev->msi_list
只是一个list_head
,而不是<代码>struct msi_desc。您无法安全地删除列表锁然后获取光标锁。有可能在您删除列表锁定之后但在获得光标锁定之前,其他人进来并释放了您的光标。您可以玩弄这些锁,但这非常很容易出错。
您几乎肯定只想要整个列表的一把锁,而不需要针对每个项目的锁。列表锁应该是互斥锁,除非您需要从中断上下文中操作列表。
Your list head doesn't need to be a
struct lista
, it should just be astruct list_head
. Notice that you keep using&lista.list
, that should just be alist_head
called "list" or something. See for example the code indrivers/pci/msi.c
, notice thatdev->msi_list
is just alist_head
, not astruct msi_desc
.You can't safely drop the list lock and then grab the cursor lock. It's possible that after you dropped the list lock but before you get the cursor lock someone else came in and free'd your cursor. You can juggle the locks, but that is very easy to get wrong.
You almost definitely just want one lock, for the whole list, and no per-item locks. And the list lock should be a
mutex
unless you need to manipulate the list from interrupt context.如果您不处理必须使用自旋锁的设备和/或内核关键部分(因为它禁用抢占和中断(根据请求)),则 Y 2 使用自旋锁,这将不必要地关闭您的抢占和中断。中断。
在列表和列表中也使用信号量或互斥体不列出项目看起来更好的解决方案。
If u are not dealing with devices and or the critical sections of kernel where spin lock is a must(as it disables preemption and interrupt (on request)), then Y 2 use spin locks which will un-necessarily close off ur preemption & interrupts.
Using semaphore or mutex that too on list & not list item looks better solution.
在删除完该项目之前,请勿释放
list_lock
。您可能会遇到以下稍微尴尬的过程:
变体:使用读写锁作为列表锁。
想要修改列表项的线程会获取读者锁;这允许多个线程并行操作列表。
想要删除列表项的线程会获取写入器锁;这会等待所有读者退出并阻止它们,直到您释放它。在这种情况下,您仍然必须保持列表锁定,直到完成删除该项目为止,
这样您就可以避免上面的步骤 2。这在概念上似乎更清晰,因为您不需要解释看起来毫无意义的锁定/释放。
Don't release
list_lock
until you're done removing the item.You might end up with the slightly awkward procedure of:
Variation: use a reader-writer lock for the list lock.
Threads looking to modify list items take the reader lock; this allows multiple threads to operate on the list in parallel.
Threads looking to remove list items take the writer lock; this waits for all readers to exit and blocks them until you release it. In this case you still have to hold the list lock until you're done removing the item,
In this way you can avoid step 2 above. This might seem conceptually clearer, as you don't need to explain the pointless-looking lock/release.