📜  读取复制更新 (RCU)

📅  最后修改于: 2022-05-13 01:56:11.408000             🧑  作者: Mango

读取复制更新 (RCU)

Linux内核中广泛使用的一种锁原语是读复制更新(RCU)锁。它是一种同步机制,于 2002 年 10 月添加到 Linux 内核中。它通过使读取与更新同时发生而取得了改进。它支持多个读取器和单个更新器之间的并发。 RCU 的读端原语中没有开销。它是最安全的数据结构之一,旨在在同时访问期间安全运行,因为它有效地使用了缓存线和内存。

读复制更新 (CPU)锁有一个无锁读关键部分。 RCU 锁适用于写入器与无锁读取器兼容的工作负载。读临界区不允许抢占。

考虑以下代码:

struct Node
{
    
  int key, val;       
    
  /* pointer to the "next" and "prev"(previous) node */         
  struct Node *next, *prev;    
  
}
  
/* Searches for a given key node in a list li and, 
 returns the value corresponding to that key*/
int search(struct Node *li, int key)  
{
    int ret= -1;
    preempt_disable(); 
  
    /* Disabling the preempt */ 
    while(li!=NULL)
    {
     
      /* The required key is found */
      if(li->key == key)   
      {
          
        /* Assigning "ret" the value of that particular key */
        ret = li->val;     
        break;
        
      }
       
     /* moving on to the next list value */
     li=li->next;       
      
     }
  
    /* Enabling the interrupt which was disabled earlier */
    preempt_enable();  
  
    /* Return the value of the key that is found */
    return ret;       
  
}
  
/* Deletes a given node */
void delete (struct Node *node)     
{
   
  /* Take a spin lock on the given node */ 
  spin_lock(&lock);              
  node->next->prev = node->prev;   
  node->prev->next = node->next;
  
  /* Unlock the lock on the node, 
   because the deletion has been done */
  spin_unlock(&lock);            
  free(node); 
  
}

上面代码中next(即node->prev->next = node->next)的删除更新是原子的,因为存储操作是原子的。如果同时执行删除,则搜索例程将根据 next 是否更新来查看新列表或旧列表。此外,删除操作可以与搜索操作同时执行,因为对搜索很重要的更新是链表节点的下一个字段的更新。

在读复制更新方案中,空闲操作被延迟,直到更新程序 100% 确定其他线程没有对将被删除的对象的引用。由于读关键部分不允许抢占,因此调用 RCU 计划表明特定内核没有执行读关键部分。



在 RCU 方案中,写入器调用rcu_free而不是free .rcu_free 确保所有读取器在调用 rcu_free 后至少从其临界区退出一次。此检查确保其他线程没有对将要删除的对象的活动引用。

rcu_free调用wait_for_rcu 时,它会在每个核心上强制处于静止状态。wait_for_rcu 在所有核心上调度当前线程以强制执行挂起空闲的安全点。

的优点和缺点:
RCU 锁不能用于像树这样的数据结构,因为树搜索可能依赖于不能以原子方式完成的多个更新。

读复制更新锁的一个缺点是它需要禁用抢占,因为它们不能在用户模式下使用