#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
#include <linux/delay.h>
#include <linux/kthread.h>
struct foo {
int a;
struct rcu_head rcu;
};
static struct foo *g_ptr;
static int myrcu_reader_thread(void *data)//读者线程
{
struct foo *p = NULL;
while(1) {
//msleep(200);
mdelay(200); //不可抢占RCU中读者获取锁不允许睡眠
rcu_read_lock();
p = rcu_dereference(g_ptr);
if (p)
printk("%s: read a=%d\n", __func__, p->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: del 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) {
msleep(400);
new_ptr = kmalloc(sizeof(struct foo), GFP_KERNEL);
old = g_ptr;
printk("%s: write to new %d\n", __func__, value);
*new_ptr = *old;
new_ptr->a = value;
rcu_assign_pointer(g_ptr, new_ptr);
call_rcu(&old->rcu, myrcu_del);
value++;
}
return 0;
}
static int __init my_test_init(void)
{
struct task_struct *reader_thread;
struct task_struct *writer_thread;
int value = 5;
printk("figo: my module init");
g_ptr = kzalloc(sizeof(struct foo), GFP_KERNEL);
reader_thread = kthread_run(myrcu_reader_thread,NULL,"rcu_reader");
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");
if (g_ptr)
kfree(g_ptr);
}
MODULE_LICENSE("GPL");
module_init(my_test_init);
module_exit(my_test_exit);
对于读者线程myrcu_reader_thread:
1) 通过rcu_read_lock()和rcu_read_unlock()来构建一个读者的临界区。
2) 通过调用rcu_dereference()获取被保护数据g_ptr指针的一个副本,即指针p,这时p和g_ptr都指向旧的被保护数据。
3) 读者线程每隔200毫秒读取一次被保护数据。
对于写者线程myrcu_writer_thread:
1) 分配一个新的保护数据new_ptr,并修改相应的数据。
2) rcu_assign_pointer()让g_ptr指向新数据。
3) call_rcu()注册一个回调函数,确保所有对旧数据的引用都执行完成之后,才调用回调函数来删除旧数据old_data.
4) 写者线程每隔400毫秒修改被保护数据。
输出:
[162809.226469] figo: my module init
[162809.441385] myrcu_reader_thread: read a=0
[162809.644025] myrcu_writer_thread: write to new 5
[162809.648888] myrcu_reader_thread: read a=5
[162809.660006] myrcu_del: del a=0
[162809.846702] myrcu_reader_thread: read a=5
[162810.049708] myrcu_writer_thread: write to new 6
[162810.053969] myrcu_reader_thread: read a=6
[162810.080608] myrcu_del: del a=5
[162810.270821] myrcu_reader_thread: read a=6
[162810.454659] myrcu_writer_thread: write to new 7
[162810.486279] myrcu_del: del a=6
[162810.488527] myrcu_reader_thread: read a=7
[162810.692156] myrcu_reader_thread: read a=7
[162810.865196] myrcu_writer_thread: write to new 8
[162810.881099] myrcu_del: del a=7