【RK3588 Android12】RCU机制

1. 概述

RCU (Read-Copy Update) 是Linux内核中一种高效的同步机制,特别适合读多写少的场景。本文介绍RCU的原理、使用方法及在RK3588驱动开发中的应用。

1.1 RCU特点

  • 无锁读取:读者无需加锁,性能极高
  • 延迟释放:等待所有读者完成后才释放旧数据
  • 适用场景:读多写少(读写比例 > 10:1)
  • 零开销读:读操作几乎无性能损耗

1.2 RCU vs 其他锁机制

机制 读性能 写性能 适用场景
RCU 极高 较低 读多写少
读写锁 中等 中等 读写均衡
自旋锁 中等 临界区短
互斥锁 可睡眠场景

2. RCU原理

2.1 基本思想

复制代码
读者:
1. rcu_read_lock()      // 标记进入读临界区
2. 访问共享数据          // 无锁读取
3. rcu_read_unlock()    // 标记离开读临界区

写者:
1. 复制旧数据
2. 修改副本
3. 替换指针(原子操作)
4. 等待所有读者完成(宽限期)
5. 释放旧数据

2.2 宽限期(Grace Period)

复制代码
时间线:
T0: 写者开始更新
T1: 写者替换指针
T2: 读者A进入临界区(看到旧数据)
T3: 读者B进入临界区(看到新数据)
T4: 读者A离开临界区
T5: 宽限期结束,释放旧数据

2.3 内存屏障

RCU依赖内存屏障保证顺序:

  • rcu_assign_pointer(): 写屏障
  • rcu_dereference(): 读屏障

3. RCU API详解

3.1 读者API

复制代码
// 进入RCU读临界区
rcu_read_lock();

// 访问RCU保护的指针
ptr = rcu_dereference(global_ptr);

// 离开RCU读临界区
rcu_read_unlock();

注意事项

  • 读临界区内不能睡眠
  • 读临界区应尽量短
  • 可以嵌套使用

3.2 写者API

复制代码
// 分配新数据
new_data = kmalloc(sizeof(*new_data), GFP_KERNEL);

// 复制并修改
memcpy(new_data, old_data, sizeof(*new_data));
new_data->value = new_value;

// 原子替换指针
rcu_assign_pointer(global_ptr, new_data);

// 等待宽限期
synchronize_rcu();

// 释放旧数据
kfree(old_data);

3.3 常用API

复制代码
// 读者API
rcu_read_lock()           // 进入读临界区
rcu_read_unlock()         // 离开读临界区
rcu_dereference(p)        // 读取RCU保护的指针

// 写者API
rcu_assign_pointer(p, v)  // 原子更新指针
synchronize_rcu()         // 同步等待宽限期
call_rcu(head, func)      // 异步等待宽限期

// 链表API
list_for_each_entry_rcu() // RCU遍历链表
list_add_rcu()            // RCU添加节点
list_del_rcu()            // RCU删除节点

4. RCU链表示例

4.1 数据结构定义

复制代码
// 设备信息结构
struct device_info {
    int id;
    char name[32];
    int status;
    struct list_head list;
    struct rcu_head rcu;
};

// 全局链表头
static LIST_HEAD(device_list);
static DEFINE_SPINLOCK(device_lock);  // 保护写操作

4.2 读操作(无锁)

复制代码
struct device_info *find_device_rcu(int id)
{
    struct device_info *dev;
    
    rcu_read_lock();
    
    list_for_each_entry_rcu(dev, &device_list, list) {
        if (dev->id == id) {
            rcu_read_unlock();
            return dev;
        }
    }
    
    rcu_read_unlock();
    return NULL;
}

// 遍历所有设备
void print_all_devices(void)
{
    struct device_info *dev;
    
    rcu_read_lock();
    
    list_for_each_entry_rcu(dev, &device_list, list) {
        printk("Device %d: %s (status=%d)\n",
               dev->id, dev->name, dev->status);
    }
    
    rcu_read_unlock();
}

4.3 写操作(需要锁)

复制代码
// 添加设备
int add_device_rcu(int id, const char *name)
{
    struct device_info *dev;
    
    dev = kmalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev)
        return -ENOMEM;
    
    dev->id = id;
    strncpy(dev->name, name, sizeof(dev->name) - 1);
    dev->status = 1;
    
    spin_lock(&device_lock);
    list_add_rcu(&dev->list, &device_list);
    spin_unlock(&device_lock);
    
    return 0;
}

// RCU回调函数
static void device_free_rcu(struct rcu_head *head)
{
    struct device_info *dev = container_of(head, struct device_info, rcu);
    kfree(dev);
}

// 删除设备
int remove_device_rcu(int id)
{
    struct device_info *dev;
    
    spin_lock(&device_lock);
    
    list_for_each_entry(dev, &device_list, list) {
        if (dev->id == id) {
            list_del_rcu(&dev->list);
            spin_unlock(&device_lock);
            
            // 异步释放
            call_rcu(&dev->rcu, device_free_rcu);
            return 0;
        }
    }
    
    spin_unlock(&device_lock);
    return -ENOENT;
}

// 更新设备(Copy-Update)
int update_device_rcu(int id, int new_status)
{
    struct device_info *old_dev, *new_dev;
    
    spin_lock(&device_lock);
    
    list_for_each_entry(old_dev, &device_list, list) {
        if (old_dev->id == id) {
            // 分配新节点
            new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC);
            if (!new_dev) {
                spin_unlock(&device_lock);
                return -ENOMEM;
            }
            
            // 复制并修改
            memcpy(new_dev, old_dev, sizeof(*new_dev));
            new_dev->status = new_status;
            
            // 替换节点
            list_replace_rcu(&old_dev->list, &new_dev->list);
            
            spin_unlock(&device_lock);
            
            // 异步释放旧节点
            call_rcu(&old_dev->rcu, device_free_rcu);
            return 0;
        }
    }
    
    spin_unlock(&device_lock);
    return -ENOENT;
}

5. RCU指针示例

5.1 简单指针保护

复制代码
struct config {
    int value1;
    int value2;
    char name[32];
};

static struct config __rcu *global_config;

// 读取配置(无锁)
int get_config_value(void)
{
    struct config *cfg;
    int value;
    
    rcu_read_lock();
    cfg = rcu_dereference(global_config);
    if (cfg)
        value = cfg->value1;
    else
        value = -1;
    rcu_read_unlock();
    
    return value;
}

// 更新配置
int update_config(int v1, int v2, const char *name)
{
    struct config *old_cfg, *new_cfg;
    
    new_cfg = kmalloc(sizeof(*new_cfg), GFP_KERNEL);
    if (!new_cfg)
        return -ENOMEM;
    
    new_cfg->value1 = v1;
    new_cfg->value2 = v2;
    strncpy(new_cfg->name, name, sizeof(new_cfg->name) - 1);
    
    old_cfg = global_config;
    rcu_assign_pointer(global_config, new_cfg);
    
    synchronize_rcu();
    kfree(old_cfg);
    
    return 0;
}

5.2 数组保护

复制代码
#define MAX_DEVICES 256

struct device_entry {
    int id;
    char name[32];
};

static struct device_entry __rcu *device_array[MAX_DEVICES];

// 读取设备
struct device_entry *get_device(int index)
{
    struct device_entry *dev;
    
    if (index >= MAX_DEVICES)
        return NULL;
    
    rcu_read_lock();
    dev = rcu_dereference(device_array[index]);
    rcu_read_unlock();
    
    return dev;
}

// 更新设备
int set_device(int index, int id, const char *name)
{
    struct device_entry *old_dev, *new_dev;
    
    if (index >= MAX_DEVICES)
        return -EINVAL;
    
    new_dev = kmalloc(sizeof(*new_dev), GFP_KERNEL);
    if (!new_dev)
        return -ENOMEM;
    
    new_dev->id = id;
    strncpy(new_dev->name, name, sizeof(new_dev->name) - 1);
    
    old_dev = device_array[index];
    rcu_assign_pointer(device_array[index], new_dev);
    
    synchronize_rcu();
    kfree(old_dev);
    
    return 0;
}

6. 完整驱动示例

6.1 设备管理驱动

复制代码
// rcu_device.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/rcupdate.h>
#include <linux/spinlock.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>

struct device_node {
    int id;
    char name[32];
    int value;
    struct list_head list;
    struct rcu_head rcu;
};

static LIST_HEAD(device_list);
static DEFINE_SPINLOCK(list_lock);

// RCU回调
static void device_free_rcu(struct rcu_head *head)
{
    struct device_node *node = container_of(head, struct device_node, rcu);
    pr_info("RCU: Freeing device %d\n", node->id);
    kfree(node);
}

// 添加设备
static int device_add(int id, const char *name, int value)
{
    struct device_node *node;
    
    node = kmalloc(sizeof(*node), GFP_KERNEL);
    if (!node)
        return -ENOMEM;
    
    node->id = id;
    strncpy(node->name, name, sizeof(node->name) - 1);
    node->value = value;
    
    spin_lock(&list_lock);
    list_add_tail_rcu(&node->list, &device_list);
    spin_unlock(&list_lock);
    
    pr_info("RCU: Added device %d: %s\n", id, name);
    return 0;
}

// 删除设备
static int device_remove(int id)
{
    struct device_node *node;
    
    spin_lock(&list_lock);
    
    list_for_each_entry(node, &device_list, list) {
        if (node->id == id) {
            list_del_rcu(&node->list);
            spin_unlock(&list_lock);
            
            call_rcu(&node->rcu, device_free_rcu);
            pr_info("RCU: Removed device %d\n", id);
            return 0;
        }
    }
    
    spin_unlock(&list_lock);
    return -ENOENT;
}

// 查找设备
static struct device_node *device_find(int id)
{
    struct device_node *node;
    
    rcu_read_lock();
    
    list_for_each_entry_rcu(node, &device_list, list) {
        if (node->id == id) {
            rcu_read_unlock();
            return node;
        }
    }
    
    rcu_read_unlock();
    return NULL;
}

// proc文件显示
static int devices_show(struct seq_file *m, void *v)
{
    struct device_node *node;
    
    seq_printf(m, "ID\tName\t\tValue\n");
    seq_printf(m, "--------------------------------\n");
    
    rcu_read_lock();
    
    list_for_each_entry_rcu(node, &device_list, list) {
        seq_printf(m, "%d\t%-16s%d\n", node->id, node->name, node->value);
    }
    
    rcu_read_unlock();
    
    return 0;
}

static int devices_open(struct inode *inode, struct file *file)
{
    return single_open(file, devices_show, NULL);
}

static const struct proc_ops devices_fops = {
    .proc_open = devices_open,
    .proc_read = seq_read,
    .proc_lseek = seq_lseek,
    .proc_release = single_release,
};

static int __init rcu_device_init(void)
{
    // 创建proc文件
    proc_create("rcu_devices", 0, NULL, &devices_fops);
    
    // 添加测试设备
    device_add(1, "device1", 100);
    device_add(2, "device2", 200);
    device_add(3, "device3", 300);
    
    pr_info("RCU device module loaded\n");
    return 0;
}

static void __exit rcu_device_exit(void)
{
    struct device_node *node, *tmp;
    
    remove_proc_entry("rcu_devices", NULL);
    
    // 清理所有设备
    spin_lock(&list_lock);
    list_for_each_entry_safe(node, tmp, &device_list, list) {
        list_del_rcu(&node->list);
        call_rcu(&node->rcu, device_free_rcu);
    }
    spin_unlock(&list_lock);
    
    // 等待所有RCU回调完成
    rcu_barrier();
    
    pr_info("RCU device module unloaded\n");
}

module_init(rcu_device_init);
module_exit(rcu_device_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("RCU Device Management Example");

7. 性能对比测试

7.1 测试代码

复制代码
// 读写锁版本
static DEFINE_RWLOCK(rwlock);

static void rwlock_read_test(void)
{
    int i;
    ktime_t start, end;
    
    start = ktime_get();
    
    for (i = 0; i < 1000000; i++) {
        read_lock(&rwlock);
        // 读取数据
        read_unlock(&rwlock);
    }
    
    end = ktime_get();
    pr_info("RWLock read: %lld ns\n", ktime_to_ns(ktime_sub(end, start)));
}

// RCU版本
static void rcu_read_test(void)
{
    int i;
    ktime_t start, end;
    
    start = ktime_get();
    
    for (i = 0; i < 1000000; i++) {
        rcu_read_lock();
        // 读取数据
        rcu_read_unlock();
    }
    
    end = ktime_get();
    pr_info("RCU read: %lld ns\n", ktime_to_ns(ktime_sub(end, start)));
}

7.2 性能结果

复制代码
测试结果(100万次读操作):
- RWLock: 150ms
- RCU:    5ms

RCU读性能提升:30倍

8. 使用注意事项

8.1 适用场景

适合使用RCU

  • 读操作远多于写操作(10:1以上)
  • 数据结构较小,复制开销低
  • 读者不需要长时间持有数据
  • 可以接受写操作的延迟

不适合使用RCU

  • 读写操作频率相当
  • 数据结构很大,复制开销高
  • 需要立即释放旧数据
  • 读者需要长时间持有数据

8.2 常见错误

复制代码
// 错误1:读临界区内睡眠
rcu_read_lock();
ptr = rcu_dereference(global_ptr);
msleep(100);  // 错误!不能睡眠
rcu_read_unlock();

// 错误2:未使用rcu_dereference
rcu_read_lock();
ptr = global_ptr;  // 错误!缺少内存屏障
rcu_read_unlock();

// 错误3:未使用rcu_assign_pointer
global_ptr = new_ptr;  // 错误!缺少内存屏障

// 错误4:过早释放
rcu_assign_pointer(global_ptr, new_ptr);
kfree(old_ptr);  // 错误!应该等待宽限期

8.3 调试技巧

复制代码
# 启用RCU调试
echo 1 > /sys/module/rcupdate/parameters/rcu_cpu_stall_suppress

# 查看RCU统计
cat /proc/rcu/rcudata

# 查看RCU宽限期
cat /sys/kernel/debug/rcu/rcu_data

9. 总结

RCU机制要点:

  1. 核心思想:读者无锁,写者Copy-Update
  2. 关键API:rcu_read_lock/unlock, rcu_dereference, rcu_assign_pointer
  3. 适用场景:读多写少(10:1以上)
  4. 性能优势:读操作性能提升10-30倍
  5. 注意事项:读临界区不能睡眠,必须使用专用API

参考资料

  1. What is RCU?
  2. RCU API
  3. Linux内核设计与实现 - RCU章节
相关推荐
JAVA+C语言2 小时前
如何优化 Java 多主机通信的性能?
java·开发语言·php
亓才孓2 小时前
[数据库]应该注意的细节
数据库·sql
编程彩机3 小时前
互联网大厂Java面试:从分布式架构到大数据场景解析
java·大数据·微服务·spark·kafka·分布式事务·分布式架构
m0_561359673 小时前
掌握Python魔法方法(Magic Methods)
jvm·数据库·python
xxxmine3 小时前
redis学习
数据库·redis·学习
小酒窝.3 小时前
【多线程】多线程打印1~100
java·多线程
君爱学习3 小时前
基于SpringBoot的选课调查系统
java
qq_5470261793 小时前
Redis 常见问题
数据库·redis·mybatis
APIshop3 小时前
Java 实战:调用 item_search_tmall 按关键词搜索天猫商品
java·开发语言·数据库