Linux kernel 如何在linux内核中的列表项上使用自旋锁?

Linux kernel 如何在linux内核中的列表项上使用自旋锁?,linux-kernel,spinlock,Linux Kernel,Spinlock,我正在为linux内核开发一个补丁。我得用几个 列表,我必须保护它们,防止在 多核机。我尝试使用旋转锁来实现这个目标,但是 有些事我不明白。我必须锁定一个文件的条目 列表(我使用的是链接列表的linux默认实现)和 流程调用syscall以删除 由于某些原因而锁定的同一元素的列表 实际上正在对其进行修改。如果我插入一个旋转锁 在列表条目中,如果一个进程试图删除它,会发生什么 当有人在上面旋转时??我应该锁定整个列表吗? 我正在寻找一段代码,可以解释如何处理这个问题 情况 例如,这段代码不应该工作

我正在为linux内核开发一个补丁。我得用几个 列表,我必须保护它们,防止在 多核机。我尝试使用旋转锁来实现这个目标,但是 有些事我不明白。我必须锁定一个文件的条目 列表(我使用的是链接列表的linux默认实现)和 流程调用syscall以删除 由于某些原因而锁定的同一元素的列表 实际上正在对其进行修改。如果我插入一个旋转锁 在列表条目中,如果一个进程试图删除它,会发生什么 当有人在上面旋转时??我应该锁定整个列表吗? 我正在寻找一段代码,可以解释如何处理这个问题 情况

例如,这段代码不应该工作(请参阅本文最后一行的注释) 代码):


你能帮我吗???

在你移除物品之前,不要释放
列表锁

您可能会遇到一个稍微尴尬的过程:

  • 获取列表锁(这将阻止其他传入线程)
  • 获取项目锁,释放项目锁(这确保所有早期线程都已完成)
  • 删除项目
  • 释放列表锁
  • 变体:对列表锁使用读写器锁

    试图修改列表项的线程获得读卡器锁;这允许多个线程并行操作列表

    试图删除列表项的线程获得writer锁;这将等待所有读卡器退出并阻止它们,直到您释放它。在这种情况下,您仍然必须保持列表锁,直到删除完该项


    通过这种方式,您可以避免上面的步骤2。这在概念上似乎更清晰,因为您不需要解释看起来毫无意义的锁/释放。

    您几乎肯定不应该使用自旋锁,除非存在来自硬IRQ上下文的并发访问。改用互斥锁


    对列表最简单的选择就是在操作时锁定整个列表。不要担心每项锁,除非并且直到您发现列表锁上有足够的争用,您需要它(在这种情况下,您可能希望改用RCU)。

    您的列表头不需要是
    struct lista
    ,它应该只是
    struct list\u头。请注意,您一直在使用
    &lista.list
    ,它应该是一个名为“list”的
    列表头。例如,请参见
    drivers/pci/msi.c
    中的代码,注意
    dev->msi_list
    只是一个
    列表头
    ,而不是
    struct msi_desc

    您不能安全地放下列表锁,然后抓住光标锁。有可能在您放弃列表锁之后,但在您获得光标锁之前,其他人进来并释放了您的光标。你可以摆弄锁,但这很容易出错


    对于整个列表,您几乎肯定只需要一个锁,而不需要每项锁。列表锁应该是互斥锁,除非您需要从中断上下文操作列表。

    如果您不处理必须使用自旋锁的设备和/或内核的关键部分(因为它禁用抢占和中断(根据请求)),然后Y 2使用自旋锁,这不一定会关闭你的抢占和中断。
    使用列表项上的信号量或互斥量看起来是更好的解决方案。

    这是我想做的,但我正在寻找一种不同的解决方案来提高并发性。R/W锁对于我的代码来说不是一个好的解决方案,因为每个系统调用都会修改公共共享列表。读/写锁实际上并不意味着每个进行编写的人都必须使用writer锁。您可以让线程在仅持有读卡器锁的情况下修改列表——将读卡器锁视为“同时允许多个线程”,将写卡器锁视为“仅允许一个线程”。只要您的删除项代码不经常执行(需要通过写卡器锁进行独占访问),其他线程可以并行运行。如果必须频繁运行删除项例程,则可能需要其他解决方案。也许不要立即释放它们,但把它们放在一边待会儿用?我必须查看信号量,以了解性能是否有显著改善。然而,RCU似乎几乎毫无用处,因为我总是访问列表来修改/添加/删除某些内容。您有什么样的工作负载?您主要是修改现有条目,还是添加和删除现有条目?
       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
            ...
        }
    }