RCU:Read-Copy-Update
已经有自旋锁,信号量,互斥锁等锁机制,为什么还需要RCU锁?
上述锁都使用了原子操作指令,即原子的访问内存,多CPU争用共享变量会让高速缓存一致性变得很糟,使得系统性能下降。
RCU实现目标
RCU实现关键原理
RCU记录了所有指向共享数据的指针的使用者,当要修改共享数据是,首先创建一个副本,在副本中修改,所有读者线程离开读者临街区后,指针指向修改后的副本,并且删除旧数据。
常见应用场景
使用RCU,链表可以有效的提高遍历读取数据的效率,读取链表成员使用rcu_read_lock函数,允许多线程同时读取该链表,并且允许一个线程同时修改链表。
如何保证链表访问的正确性?
读者在遍历链表时,一个线程删除了一个节点。删除线程会把这个节点从链表中移除,但是不会直接销毁它,RCU会等到所有线程读完链表数据后,才销毁这个节点。
RCU提供的接口
测试代码
#include
#include
#include
#include
#include
#include
#include
#include
struct foo {
int a;
struct rcu_head rcu;
};
static struct foo *g_ptr;
static int myrcu_reader_thread1(void *data) //读者线程1
{
struct foo *p1 = NULL;
while (1) {
if(kthread_should_stop())
break;
msleep(20);
rcu_read_lock();
mdelay(200);
p1 = rcu_dereference(g_ptr);
if (p1)
printk("%s: read a=%d\n", __func__, p1->a);
rcu_read_unlock();
}
return 0;
}
static int myrcu_reader_thread2(void *data) //读者线程2
{
struct foo *p2 = NULL;
while (1) {
if(kthread_should_stop())
break;
msleep(30);
rcu_read_lock();
mdelay(100);
p2 = rcu_dereference(g_ptr);
if (p2)
printk("%s: read a=%d\n", __func__, p2->a);
rcu_read_unlock();
}
return 0;
}
static void myrcu_del(struct rcu_head *rh)
{
struct foo *p = container_of(rh, struct foo, rcu);
printk("%s: a=%d\n", __func__, p->a);
kfree(p);
}
static int myrcu_writer_thread(void *p) //写者线程
{
struct foo *old;
struct foo *new_ptr;
int value = (unsigned long)p;
while (1) {
if(kthread_should_stop())
break;
msleep(250);
new_ptr = kmalloc(sizeof (struct foo), GFP_KERNEL);
old = g_ptr;
*new_ptr = *old;
new_ptr->a = value;
rcu_assign_pointer(g_ptr, new_ptr);
call_rcu(&old->rcu, myrcu_del);
printk("%s: write to new %d\n", __func__, value);
value++;
}
return 0;
}
static struct task_struct *reader_thread1;
static struct task_struct *reader_thread2;
static struct task_struct *writer_thread;
static int __init my_test_init(void)
{
int value = 5;
printk("figo: my module init\n");
g_ptr = kzalloc(sizeof (struct foo), GFP_KERNEL);
reader_thread1 = kthread_run(myrcu_reader_thread1, NULL, "rcu_reader1");
reader_thread2 = kthread_run(myrcu_reader_thread2, NULL, "rcu_reader2");
writer_thread = kthread_run(myrcu_writer_thread, (void *)(unsigned long)value, "rcu_writer");
return 0;
}
static void __exit my_test_exit(void)
{
printk("goodbye\n");
kthread_stop(reader_thread1);
kthread_stop(reader_thread2);
kthread_stop(writer_thread);
if (g_ptr)
kfree(g_ptr);
}
MODULE_LICENSE("GPL");
module_init(my_test_init);
module_exit(my_test_exit);
读临界区
写临界区