在read, copy, update之后,就是要回收旧的数据了,
call_cpu(rcu_head, call_back_func)
跟踪call_back_func的注册,call_back_func被赋值进入rcu_head,rcu_head被加入rcu_data的nxttail链表
rcu_data是per-cpu variable, rcu_state.rda[]保存rcu_data指针
__call_cpu函数结构:
前半部分,等宽限期(grace period)结束,并开启一个gp;
后面是强制静止时间的操作;
rcu要考虑到irq/nmi等(from lwn)
跟着call_back func走,所有的操作都在rcutree.c中定义,可以索引到大部分的rcu初始化,更新,invoke机制,这是旧数据结构回收的主要任务。
kernel/rcutree.c: rcu_init()初始化rcu机制,在系统启动时的start_kernel中被调用
这么长时间都像无头苍蝇一样乱撞,自己认真想一下,找到主线也就好了
Document/RCU中有一些描述,其中whatIsRCU.txt中给出了几个lwn中的链接,是RCU的实现者写的,可以一看;
这位兄弟写得相当详细,佩服~[http://blogold.chinaunix.net/u1/51562/showart_1341707.html]
rcu_process_callbacks负责调用rcu_data中注册的回调函数;
而rcu_process_callbacks本身在rcu_init中被注册为softirq:
open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
softirq静态分配,由do_softirq()函数负责调用各个softirq的action,
而do_softirq由在内核启动时启动的ksoftirqd内核线程来执行(详见ULK3)。
program = algorithm + data structure
后两者是可以转换的
algorithm: 初始化,各cpu context switch时处理,最后一个CPU的处理
ds: rcu_state, rcu_data, rcu_node-level, rcu_head
concepts related: grace preiod, quiescent state
DEFINE_PER_CPU(struct rcu_data, rcu_sched_data);
DEFINE_PER_CPU(struct rcu_data, rcu_bh_data);
这里并不是直接分配一个NR_CPUS大小的数组,这是我的误解,其背后机制有待研究
使用DEFINE_PER_CPU声明的变量在链接时,会被放在.init.percpu section,
在系统启动时,调用setup_per_cpu_areas()重新为每个CPU分配一个.init.percpu section以供使用,内存分配的方式是为访问高度优化的(使用到了GDT等)
先看rcutiny吧,这个里面想必只保留了rcu的核心机制
在schedule函数中,调用了rcu_note_context_switch()在进程切换时,通知RCU
RCU必然有一种机制,来标示RCU的全局状态,这个全局标志,每个cpu进程切换时亦即在rcu_note_context_switch()中,修改全局标志,但最后的标志即所有的CPU都已经进行过进程上下文切换该如何确定呢?NR_CPUS?
看来研究rcutiny的想法有点问题,rcutiny跟rcutree中的实现差别有点小大
/*
* Note a quiescent state. Because we do not need to know
* how many quiescent states passed, just if there was at least
* one since the start of the grace period, this just sets a flag.
*/
void rcu_sched_qs(int cpu){}
do not need to know or do not know or cannot know, IMO, it can be get in other means[implements], but it's has more overhead.
我很高兴通过这个开始考虑进程切换的场景了