RCU(Read-Copy
Update),顾名思义就是读-拷贝修改,它是基于其原理命名的。对于被RCU保护的共享数据结构,读者不需要获得任何锁就可以访问它,但写者在访问它
时首先拷贝一个副本,然后对副本进行修改,最后使用一个回调(callback)机制在适当的时机把指向原来数据的指针重新指向新的被修改的数据。这个时
机就是所有引用该数据的CPU都退出对共享数据的操作。
在定义需要使用rcu的资源时,定义中可以加上__rcu修饰,如:
-
static struct our_data __rcu *pmy_data = &my_data;
好处:1、告诉读者这个变量是由rcu所保护的。
2、linux kernel有一个静态的检查工具,他会去检查用rcu申明的指针,是不是用rcu_read_lock()保护的。
读者
使用rcu_read_lock()来进入读侧
使用rcu_dereference()来获取指针对应的资源
使用rcu_read_unlock()来退出读侧
可以这样用比如
-
struct our_data *data;
-
rcu_read_lock();
-
data = rcu_dereference(pmy_data);//pmy_data是我们需要读取的指针,返回到data中获取
-
printk("read count1 %d count2 %d .\n", data->count1, data->count2);
-
rcu_read_unlock();
写者
如果有多个写端,需要写端自己做同步
使用rcu_assign_pointer()来更新指针
使用call_rcu()/synchronize_rcu()来释放旧指针对应的资源
使用call_rcu()和synchronize_rcu()的区别
call_rcu()用来将释放的工作交给后台,可以快速返回,不会导致睡眠
synchronisze_rcu()会等待所有的读者退出再返回,会导致睡眠
-
data = kmalloc(sizeof(*data), GFP_KERNEL);//先分配一块内存空间
-
if (!data)
-
return;
-
/*memcpy
函数原型:extern void *memmove(void *dest, const void *src, unsigned int count)
参数说明:dest为目的字符串,src为源字符串,count为要拷贝的字节数。*/
-
memcpy(data, pmy_data, sizeof(struct our_data));//然后把需要改写的内容复制到data中
-
data->count1 ++;
-
data->count2 += 10;
-
-
rcu_assign_pointer(pmy_data, data);//改写内容后通过rcu_assign_pointer将指针更新
-
-
if (tmp != &my_data) {
-
synchronize_rcu();//会等待所有的读者退出再返回,会导致睡眠
-
kfree(tmp);
-
}
然后是call_rcu,原型如下:
-
extern void call_rcu(struct rcu_head *head,
-
void (*func)(struct rcu_head *head));
call_rcu可以这样使用:
-
struct our_data {
-
int count1;
-
int count2;
-
struct rcu_head rhead;//先设定一个rcu_hread
-
};
-
-
static void rcu_free(struct rcu_head *head)
-
{
-
struct our_data *data;
-
-
data = container_of(head, struct our_data, rhead);//从结构体中的指针,获取结构体的指针
-
kfree(data);//用来释放data
-
}
-
-
...
-
call_rcu(&tmp->rhead, rcu_free);//传入rcu_hread指针,定义函数名字
-
...
第一次写,失败了。又写了一遍
-
#include
-
#include <linux/module.h>
-
#include <linux/delay.h>
-
#include <linux/err.h>
-
#include <linux/slab.h>
-
#include <linux/rcupdate.h>
-
-
MODULE_LICENSE("GPL");
-
MODULE_AUTHOR("chen");
-
MODULE_DESCRIPTION("exam for opensource.");
-
-
#define MAX_KTHREADS 10
-
-
struct our_data {
-
int count1;
-
int count2;
-
struct rcu_head rhead;
-
};
-
-
static unsigned long reader_bitmap;
-
static struct task_struct *kthreads[MAX_KTHREADS];
-
static struct our_data my_data;
-
static struct our_data __rcu *pmy_data = &my_data;
-
-
static void show_my_data(void)
-
{
-
printk("my_data.count1 is %d, count2 is %d.\n", my_data.count1, my_data.count2);
-
printk("pmy_data.count1 is %d, count2 is %d.\n", pmy_data->count1, pmy_data->count2);
-
-
}
-
-
static void set_reader_number(int reader)
-
{
-
WARN_ON((MAX_KTHREADS - reader) != 1);
-
reader_bitmap = 0;
-
while(reader)
-
{
-
reader_bitmap |= 1 << --reader;
-
}
-
}
-
-
static void reader_do(void)
-
{
-
struct our_data *data;
-
rcu_read_lock();
-
data = rcu_dereference(pmy_data);
-
printk("read count1 %d count2 %d\n", data->count1, data->count2);
-
rcu_read_unlock();
-
}
-
-
static void rcu_free(struct rcu_head *head)
-
{
-
struct our_data *data;
-
-
data = container_of(head, struct our_data, rhead);
-
kfree(data);
-
}
-
-
static void writer_do(void)
-
{
-
struct our_data *data, *tmp;
-
tmp = pmy_data;
-
-
data = kmalloc(sizeof(*data), GFP_KERNEL);
-
if (!data)
-
return;
-
-
memcpy(data, pmy_data, sizeof(struct our_data));
-
data->count1 ++;
-
data->count2 += 10;
-
-
rcu_assign_pointer(pmy_data, data);
-
-
if (tmp != &my_data)
-
call_rcu(&tmp->rhead, rcu_free);
-
}
-
-
static void kthread_cleanup(void)
-
{
-
int i = 0;
-
for(i = 0; i < MAX_KTHREADS; i++)
-
if (kthreads[i])
-
kthread_stop(kthreads[i]);
-
}
-
-
static int kthread_do(void *data)
-
{
-
long i = (long)data;
-
int reader = (reader_bitmap & (1 << i));
-
printk("run...%ld %s ...\n", i, reader ? "reader" : "writer");
-
while(!kthread_should_stop()) {
-
if (reader_bitmap & (1 << i))
-
reader_do();
-
else
-
writer_do();
-
-
msleep(10);
-
}
-
-
return 0;
-
}
-
-
static int create_kthread(void)
-
{
-
int i = 0, ret = 0;
-
for(i = 0; i < MAX_KTHREADS; i++) {
-
struct task_struct *tsk;
-
tsk = kthread_run(kthread_do, (void *)(long)i, "kthread-%d", i);
-
if (IS_ERR(tsk)) {
-
printk("create kthread err.\n");
-
ret = PTR_ERR(tsk);
-
goto err;
-
}
-
kthreads[i] = tsk;
-
}
-
return ret;
-
-
err:
-
kthread_cleanup();
-
return ret;
-
}
-
-
static int __init main_init(void)
-
{
-
printk("call %s.\n", __FUNCTION__);
-
set_reader_number(9);
-
-
if (create_kthread())
-
goto err;
-
return 0;
-
-
err:
-
kthread_cleanup();
-
return -1;
-
}
-
-
static void __exit main_exit(void)
-
{
-
kthread_cleanup();
-
show_my_data();
-
printk("call %s .\n", __FUNCTION__);
-
}
-
-
module_init(main_init);
-
module_exit(main_exit);
为什么pmy_data->count1和pmy_data->count2能输出,而用my_data.count1和my_data.count2就显示是0?
因为指针变了。。。
http://www.ibm.com/developerworks/cn/linux/l-rcu/
阅读(1851) | 评论(0) | 转发(0) |