rcu参考资料:
https://airekans.github.io/c/2016/05/10/dive-into-liburcu
https://lwn.net/Articles/262464/
https://cloud.tencent.com/developer/article/1684477
https://www.cnblogs.com/LoyenWang/p/12681494.html

userspace rcu:
https://github.com/urcu/userspace-rcu
内核rcu实现不太一样,但是原理类似

rcu的原理:

rcu会维护一个全局的数据结构rcu_gp

struct rcu_gp {unsigned long ctr;int32_t futex;
} __attribute__((aligned(CAA_CACHE_LINE_SIZE)));
extern struct rcu_gp rcu_gp;

他是通过ctr成员变量来实现一写多读操作

假设一个写线程,4个读线程
读线程ctr可能的值:
读线程1:rcu_register_thread,会获取ctr的值,假设此时为10
读线程2:rcu_register_thread,会获取ctr的值,假设此时为11(可能发生在synchronize_rcu之后,获取到了新的值)
读线程3:rcu_register_thread,会获取ctr的值,假设此时为10
读线程4:rcu_register_thread,会获取ctr的值,假设此时为10

rcu_quiescent_state:
1、如果将全局g_ctr的值等于读线程的ctr值,直接唤醒写线程(写线程等待的读线程数减一)
2、不相等的话直接把全局g_ctr的值赋值给读线程的ctr

/** This is a helper function for _rcu_quiescent_state().* The first cmm_smp_mb() ensures memory accesses in the prior read-side* critical sections are not reordered with store to* URCU_TLS(urcu_qsbr_reader).ctr, and ensures that mutexes held within an* offline section that would happen to end with this* urcu_qsbr_quiescent_state() call are not reordered with* store to URCU_TLS(urcu_qsbr_reader).ctr.*/
static inline void _urcu_qsbr_quiescent_state_update_and_wakeup(unsigned long gp_ctr)
{cmm_smp_mb();_CMM_STORE_SHARED(URCU_TLS(urcu_qsbr_reader).ctr, gp_ctr);cmm_smp_mb();   /* write URCU_TLS(urcu_qsbr_reader).ctr before read futex */urcu_qsbr_wake_up_gp();cmm_smp_mb();
}/** Inform RCU of a quiescent state.** This function is less than 10 lines long.  The intent is that this* function meets the 10-line criterion for LGPL, allowing this function* to be invoked directly from non-LGPL code.** We skip the memory barriers and gp store if our local ctr already* matches the global urcu_qsbr_gp.ctr value: this is OK because a prior* _rcu_quiescent_state() or _rcu_thread_online() already updated it* within our thread, so we have no quiescent state to report.*/
static inline void _urcu_qsbr_quiescent_state(void)
{unsigned long gp_ctr;urcu_assert_debug(URCU_TLS(urcu_qsbr_reader).registered);if ((gp_ctr = CMM_LOAD_SHARED(urcu_qsbr_gp.ctr)) == URCU_TLS(urcu_qsbr_reader).ctr)return;_urcu_qsbr_quiescent_state_update_and_wakeup(gp_ctr);
}

写线程1:synchronize_rcu会将ctr的值加1,ctr的值为11,并且会等待所有读线程的值都为11(即所有读线程都执行完一次rcu_quiescent_state)才会执行下一个步骤

所以可以理解为临界区间在rcu_register_thread开始,到rcu_quiescent_state结束,写线程可以继续执行

rcu锁example:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <urcu-qsbr.h>struct foo
{int a;int b;int c;
};static struct foo *g_foo;void g_foo_init()
{g_foo = (struct foo *)malloc(sizeof(struct foo));g_foo->a = 1;g_foo->b = 1;g_foo->c = 1;
}void g_foo_release()
{free(g_foo);g_foo = NULL;
}void do_something(struct foo *l_foo)
{  int i = 0;int j = 0;long sum = 0;for (i = 0; i < 10000; i++) {for (j = 0; j < 100000; j++) {// 加锁或者不加锁都ok,rcu_read_lock内部实现为NULL// rcu_read_lock();if (l_foo) {sum = sum + l_foo->a + l_foo->b + l_foo->c;}// rcu_read_unlock();}}printf("sum:%ld\n", sum);
}void * reader_thread()
{rcu_register_thread();struct foo *l_foo = rcu_dereference(g_foo);if (l_foo) {do_something(l_foo);}rcu_quiescent_state();rcu_unregister_thread();
}void * writer_thread()
{struct foo *old_foo = g_foo;struct foo *new_foo = (struct foo *)malloc(sizeof(struct foo));new_foo->a = 10;new_foo->b = 11;new_foo->c = 12;rcu_xchg_pointer(&g_foo, new_foo);synchronize_rcu();if (old_foo) {free(old_foo);}
}/*** rcu,一写多读需要遵循的规则* * 读线程:* 1、rcu_register_thread    将改线程加入rcu链表,synchronize_rcu判断的时候会用到* 2、rcu_unregister_thread  将改线程从rcu链表中移除* 3、rcu_quiescent_state    读线程主动通知写线程已经结束一批临界区* 4、rcu_dereference        读线程获取被保护的共享指针需要用该API* 5、rcu_read_lock/rcu_read_unlock 可选API,实际什么都没做* * 写线程:* 1、rcu_xchg_pointer       写线程更新指针需要用到该API* 2、synchronize_rcu        等待Grace Period结束(所有读线程都已经调用过rcu_quiescent_state)*/
int main()
{pthread_t reader1, reader2, reader3, reader4, writer;g_foo_init();pthread_create(&writer, NULL, writer_thread, NULL);pthread_create(&reader1, NULL, reader_thread, NULL);pthread_create(&reader2, NULL, reader_thread, NULL);pthread_create(&reader3, NULL, reader_thread, NULL);pthread_create(&reader4, NULL, reader_thread, NULL);pthread_join(writer, NULL);pthread_join(reader1, NULL);pthread_join(reader2, NULL);pthread_join(reader3, NULL);pthread_join(reader4, NULL);g_foo_release();return 0;
}

运行结果:

从上图也可以看出来,rcu锁并不保证每次读到的数据到底是旧数据还是新数据,但是会保证旧数据和新数据都能正常访问到

rcu锁原理以及rcu example学习相关推荐

  1. linux内核rcu锁实例,Linux Rcu到底有没有锁?

    3.14内核代码 rcu机制不是宣称无锁吗? 但是rcu_lock_acquire那句是个锁吗?怎么解释呢?求解惑 806 static inline void rcu_read_lock(void) ...

  2. openVswitch(OVS)源代码之linux RCU锁机制分析

    前言 本来想继续顺着数据包的处理流程分析upcall调用的,但是发现在分析upcall调用时必须先了解linux中内核和用户空间通信接口Netlink机制,所以就一直耽搁了对upcall的分析.如果对 ...

  3. 带你一文解析RCU锁机制原理

    原理 Read Copy Update 读(Read):读者不需要获得任何锁就可访问RCU保护的临界区: 拷贝(Copy):写者在访问临界区时,写者"自己"将先拷贝一个临界区副本, ...

  4. 深入理解RCU|核心原理

    hi,大家好,今天给大家分享并行程序设计中最重要的锁-RCU锁,RCU锁本质是用空间换时间,是对读写锁的一种优化加强,但不仅仅是这样简单,RCU体现出来的垃圾回收思想,也是值得我们学习和借鉴,各个语言 ...

  5. java锁原理_Java锁原理学习

    Java锁原理学习 为了学习Java锁的原理,参照ReentrantLock实现了自己的可重入锁,代码如下: 先上AQS的相关方法: // AQS = AbstractQueuedSynchroniz ...

  6. 互斥锁 、 自旋锁、读写锁和RCU锁

    互斥锁 mutex: 在访问共享资源之前对进行加锁操作,在访问完成之后进行解锁操作. 加锁后,任何其他试图再次加锁的线程会被阻塞,直到当前进程解锁. 如果解锁时有一个以上的线程阻塞,那么所有该锁上的线 ...

  7. 浅谈Linux内核RCU机制原理

    RCU(Read-Copy Update)是数据同步的一种方式,在当前的Linux内核中发挥着重要的作用.RCU主要针对的数据对象是链表,目的是提高遍历读取数据的效率,为了达到目的使用RCU机制读取数 ...

  8. 关于分布式锁原理的一些学习与思考:redis分布式锁,zookeeper分布式锁

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:牛人 20000 字的 Spring Cloud 总结,太硬核了~ 作者:队长给我球. 出处:https://w ...

  9. zookeeper 分布式锁_关于redis分布式锁,zookeeper分布式锁原理的一些学习与思考

    编辑:业余草来源:https://www.xttblog.com/?p=4946 首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法 ...

最新文章

  1. pandas删除dataframe列名称中包含特定字符串的数据列(dropping columns contains specifiec substring in dataframe)
  2. hdu3665 水最短路
  3. PMCAFF|来来来!我们一起重新设计微信公众号(图多杀猫 慎重浏览)
  4. java中string类的常用方法举例说明
  5. 在MingW下编译llvm/clang
  6. 拓端tecdat|R语言多臂试验 - 我们应该考虑多重性吗?
  7. 对极几何基本概念与极线约束
  8. 【mysql】文本字符串类型
  9. Linux XAMP is currently only availably as 32 bit application.
  10. python爬歌词生成词云图_Python爬虫摇滚网易云音乐歌词生成词云图
  11. 电脑能正常上网百度,但是网络显示无Internet
  12. TinyPng:在线PNG图片压缩工具
  13. 2019小程序赚钱全攻略:零基础搭建、引爆、变现你的小程序
  14. 【蓝桥杯技巧篇】处理日期相关问题详解
  15. 流浪地球动态桌面壁纸
  16. IOS性能调优系列:使用Zombies动态分析内存中的僵尸对象
  17. Cisco Nexus vPC 之 Sticky Master
  18. PCL学习笔记(二):PCL官方教程学习
  19. 高考失利出国留学计算机专业,高考失利如何出国留学
  20. jquery插件备忘录

热门文章

  1. Python多线程顺序运行
  2. 解读指标 Mali-400MP4 GPU 指标
  3. Reloaded modules:在Spyder运行时错误
  4. [原]极域电子教室V6 2007通信协议解析(一)
  5. java实现TCP通信服务器群发给用户
  6. hashcat解密握手包
  7. This function does not fully set the dimensions of output port 2 .
  8. 操作无法完成。打印后台程序服务没有运行
  9. 织梦DEDECMS系统安装环境配置教程
  10. 百度Apollo6.0中Overlap.proto每个字段的含义