hi, 上次分析了RCU核心思想:[深入理解RCU|核心原理] ,后面说会分享一篇RCU的源码剖析,其实我这边已经总结得差不多:
但自己思考了一下,发现大部分都是代码分析,这样很多人其实并不喜欢看源代码分析(代码有点多),所以可能其他方式更好,比如图解,我发现已经有人搞了这个,而且质量也挺高的,打算分享给大家。
Read the fucking source code!
--By 鲁迅A picture is worth a thousand words.
--By 高尔基说明:
我会假设你已经看过了``
[深入理解RCU|核心原理]
本文将进一步去探索下RCU
背后的机制。
Grace Period
Grace Period
,中文叫做宽限期,从Removal
到Reclamation
,中间就隔了一个宽限期;Quiescent Status
;Quiescent Status
),并且判断宽限期是否结束。来一张图:
Quiescent Status
Quiescent Status
,用于描述处理器的执行状态。当某个CPU正在访问RCU保护的临界区时,认为是活动的状态,而当它离开了临界区后,则认为它是静止的状态。当所有的CPU都至少经历过一次QS后,宽限期将结束并触发回收工作。
用户模式
或者idle模式
,则表明CPU离开了临界区;cpumask
去记录CPU经历静止状态,在经典RCU(Classic RCU
)实现中,由于使用了全局的cpumask
位图,当CPU数量很大时锁争用会带来很大开销(GP开始时设置对应位,GP结束时清除对应位),因此也促成了Tree RCU
的诞生;Tree RCU
以树形分层来组织CPU,将CPU分组,本小组的CPU争用同一个锁,当本小组的某个CPU经历了一个静止状态QS后,将其对应的位从位图清除,如果该小组最后一个CPU经历完静止状态QS后,表明该小组全部经历了CPU的QS状态,那么将上一层对应该组的位从位图清除;struct rcu_state
,struct rcu_node
,struct rcu_data
;图来了:
struct rcu_state
:用于描述RCU的全局状态,它负责组织树状层级结构,系统中支持不同类型的RCU状态:rcu_sched_state
, rcu_bh_state
,rcu_preempt_state
;struct rcu_node
:Tree RCU
中的组织节点;struct rcu_data
:用于描述处理器的RCU状态,每个CPU都维护一个数据,它归属于某一个struct rcu_node
,struct rcu_data
检测静止状态并进行处理,对应的CPU进行RCU回调,__percpu
的定义也减少了同步的开销;看到这种描述,如果还是在懵逼的状态,那么再来一张拓扑图,让真相更白一点:
struct rcu_node
来组成,这些节点在struct rcu_state
结构中是放置在数组中的,由于struct rcu_node
结构有父节点指针,因此可以构造树形;CPU0/CPU1
就不需要和CPU6/CPU7
去争用锁了,逐级以淘汰赛的形式向上;
关键点来了:Tree RCU使用rcu_node节点来构造层级结构,进而管理静止状态Quiescent State和宽限期Grace Period,静止状态信息QS是从每个CPU的rcu_data往上传递到根节点的,而宽限期GP信息是通过根节点从上往下传递的,当每个CPU经历过一次QS状态后,宽限期结束
关键字段还是有必要介绍一下的,否则岂不是耍流氓?
struct rcu_state {
struct rcu_node node[NUM_RCU_NODES]; // rcu_node节点数组,组织成层级树状
struct rcu_node *level[RCU_NUM_LVLS + 1]; //指向每层的首个rcu_node节点,数组加1是为了消除编译告警
struct rcu_data __percpu *rda; //指向每个CPU的rcu_data实例
call_rcu_func_t call; //指向特定RCU类型的call_rcu函数:call_rcu_sched, call_rcu_bh等
int ncpus; // 处理器数量
unsigned long gpnum; //当前宽限期编号,gpnum > completed,表明正处在宽限期内
unsigned long completed; //上一个结束的宽限期编号,如果与gpnum相等,表明RCU空闲
...
unsigned long gp_max; //最长的宽限期时间,jiffies
...
}
/*
* Definition for node within the RCU grace-period-detection hierarchy.
*/
struct rcu_node {
raw_spinlock_t __private lock; //保护本节点的自旋锁
unsigned long gpnum; //本节点宽限期编号,等于或小于根节点的gpnum
unsigned long completed; //本节点上一个结束的宽限期编号,等于或小于根节点的completed
unsigned long qsmask; //QS状态位图,某位为1,代表对应的成员没有经历QS状态
unsigned long qsmaskinit; //正常宽限期开始时,QS状态的初始值
...
int grplo; //该分组的CPU最小编号
int grphi; //该分组的CPU最大编号
u8 grpnum; //该分组在上一层分组里的编号
u8 level; //在树中的层级,Root为0
...
struct rcu_node *parent; //指向父节点
}
/* Per-CPU data for read-copy update. */
struct rcu_data {
unsigned long completed; //本CPU看到的已结束的宽限期编号
unsigned long gpnum; //本CPU看到的最高宽限期编号
union rcu_noqs cpu_no_qs; //记录本CPU是否经历QS状态
bool core_need_qs; //RCU需要本CPU上报QS状态
unsigned long grpmask; //本CPU在分组的位图中的掩码
struct rcu_segcblist; //回调函数链表,用于存放call_rcu注册的延后执行的回调函数
...
}
我们看到了RCU的写端调用了synchronize_rcu/call_rcu
两种类型的接口,事实上Linux内核提供了三种不同类型的RCU,因此也对应了相应形式的接口。
来张图:
RCU
写者,可以通过两种方式来等待宽限期的结束,一种是调用同步接口等待宽限期结束,一种是异步接口等待宽限期结束后再进行回调处理,分别如上图的左右两侧所示;wait_for_completion
睡眠等待操作,并且会将wakeme_after_rcu
回调函数传递给异步接口,当宽限期结束后,在异步接口中回调了wakeme_after_rcu
进行唤醒处理;可抢占RCU:rcu_read_lock/rcu_read_unlock
来界定区域,在读端临界区可以被其他进程抢占;
不可抢占RCU(RCU-sched)
:rcu_read_lock_sched/rcu_read_unlock_sched
来界定区域,在读端临界区不允许其他进程抢占;
关下半部RCU(RCU-bh)
:rcu_read_lock_bh/rcu_read_unlock_bh
来界定区域,在读端临界区禁止软中断;
从图中可以看出来,不管是同步还是异步接口,最终都是调到__call_rcu
接口,它是接口实现的关键,所以接下来分析下这个函数了;
__call_rcu
函数的调用流程如下:
__call_rcu
函数,第一个功能是注册回调函数,而回调的函数的维护是在rcu_data
结构中的struct rcu_segcblist cblist
字段中;rcu_accelerate_cbs/rcu_advance_cbs
,实现中都是通过操作struct rcu_segcblist
结构,来完成回调函数的移动处理等;__call_rcu
函数第二个功能是判断是否需要开启新的宽限期GP;链表的维护关系如下图所示:
那么通过__call_rcu
注册的这些回调函数在哪里调用呢?答案是在RCU_SOFTIRQ
软中断中:
invoke_rcu_core
时,在该函数中调用raise_softirq
接口,从而触发软中断回调函数rcu_process_callbacks
的执行;rcu_process_callbacks
中会调用rcu_gp_kthread_wake
唤醒内核线程,最终会在rcu_gp_kthread
线程中执行;rcu_do_batch
函数中执行,其中有两种执行方式:1)如果不支持优先级继承的话,直接调用即可;2)支持优先级继承,在把回调的工作放置在rcu_cpu_kthread
内核线程中,其中内核为每个CPU都创建了一个rcu_cpu_kthread
内核线程;既然涉及到宽限期GP的操作,都放到了rcu_gp_kthread
内核线程中了,那么来看看这个内核线程的逻辑操作吧:
rcu_preempt_state, rcu_bh_state, rcu_sched_state
创建了内核线程rcu_gp_kthread
;rcu_gp_kthread
内核线程主要完成三个工作:1)创建新的宽限期GP;2)等待强制静止状态,设置超时,提前唤醒说明所有处理器经过了静止状态;3)宽限期结束处理。其中,前边两个操作都是通过睡眠等待在某个条件上。很显然,对这种状态的检测通常都是周期性的进行,放置在时钟中断处理中就是情理之中了:
rcu_sched/rcu_bh
类型的RCU中,当检测CPU处于用户模式或处于idle
线程中,说明当前CPU已经离开了临界区,经历了一个QS静止状态,对于rcu_bh
的RCU,如果没有出去softirq
上下文中,也表明CPU经历了QS静止状态;rcu_pending
满足条件的情况下,触发软中断的执行,rcu_process_callbacks
将会被调用;rcu_process_callbacks
回调函数中,对宽限期进行判断,并对静止状态逐级上报,如果整个树状结构都经历了静止状态,那就表明了宽限期的结束,从而唤醒内核线程去处理;rcu_pending
函数中,rcu_pending->__rcu_pending->check_cpu_stall->print_cpu_stall
的流程中,会去判断是否有CPU stall的问题,这个在内核中有文档专门来描述,不再分析了;如果要观察整个状态机的变化,跟踪一下trace_rcu_grace_period
接口的记录就能发现:
/*
* Tracepoint for grace-period events. Takes a string identifying the
* RCU flavor, the grace-period number, and a string identifying the
* grace-period-related event as follows:
*
* "AccReadyCB": CPU acclerates new callbacks to RCU_NEXT_READY_TAIL.
* "AccWaitCB": CPU accelerates new callbacks to RCU_WAIT_TAIL.
* "newreq": Request a new grace period.
* "start": Start a grace period.
* "cpustart": CPU first notices a grace-period start.
* "cpuqs": CPU passes through a quiescent state.
* "cpuonl": CPU comes online.
* "cpuofl": CPU goes offline.
* "reqwait": GP kthread sleeps waiting for grace-period request.
* "reqwaitsig": GP kthread awakened by signal from reqwait state.
* "fqswait": GP kthread waiting until time to force quiescent states.
* "fqsstart": GP kthread starts forcing quiescent states.
* "fqsend": GP kthread done forcing quiescent states.
* "fqswaitsig": GP kthread awakened by signal from fqswait state.
* "end": End a grace period.
* "cpuend": CPU first notices a grace-period end.
*/
大体流程如下:
RCU_SOFTIRQ
和内核线程rcu_gp_kthread
的动态运行及交互等;rcu_state, rcu_node, rcu_data
组织成树状结构来维护,此外回调函数是通过rcu_data
中的分段链表来批处理,至于这些结构中相关字段的处理(比如gpnum, completed
字段的设置来判断宽限期阶段等),以及链表的节点移动等,都没有进一步去分析跟进了;Verification of the Tree-Based Hierarchical Read-Copy Update in the Linux Kernel
Documentation/RCU
What is RCU, Fundamentally?
What is RCU? Part 2: Usage
RCU part 3: the RCU API
Introduction to RCU
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/0ksx2yk-wShJ7cHlPQjTSQ
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。