Linux RCU队列(3)树形RCU队列实现代码分析

本文深入解析Linux RCU(Read-Copy Update)队列的实现,重点关注树形RCU队列的代码细节。通过`wait_for_completion`、`wakeme_after_rcu`等函数,分析RCU如何处理回调函数队列、等待操作和grace period。文章详细阐述了RCU的数据结构,包括rcu_state、rcu_data和rcu_node,并讨论了grace period的启动、结束条件以及强制性QS处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

经过之前的分析基本清楚了整个RCU的结构体,下面就通过分代码来解读RCU的实现。

void synchronize_rcu(void)
{
	if (!rcu_scheduler_active)
		return;
	wait_rcu_gp(call_rcu);
}

当rcu_scheduler_active变量将会在第一个任务创建之前由0转换为1.当这个变量等于0的时候,RCU可以假设当前系统中仅仅存在一个任务。这样进行RCU等待就没有必要,所以通过这个变量对初始化时RCU队列进行优化,当这个变量转换为1时,表明系统已经准备好探测真正的grace period。顺便提一下,这里之所以从synchronize_rcu函数开始,是因为这个函数最后会调用call_rcu,而这个函数的指针则是作为参数传递到wait_rcu_gp中。

void wait_rcu_gp(call_rcu_func_t crf)
{
	struct rcu_synchronize rcu;
	init_rcu_head_on_stack(&rcu.head);
	init_completion(&rcu.completion);
	crf(&rcu.head, wakeme_after_rcu);
	wait_for_completion(&rcu.completion);
	destroy_rcu_head_on_stack(&rcu.head);
}

Wait_rcu_gp函数的实现如上图所示,首先从栈中分配一个结构体,这个阶梯就是中包含rcu的实现——一个队列指针,一个函数指针,已经一个同步等待的结构体(如果是异步rcu则没有同步等待结构体)。在初始化之后就转入到调用异步的call_rcu函数中,然后进行等待操作以及对堆栈上的RCU对象进行清理。先看一下wait_for_completion。

void wait_for_completion(struct completion *x)
{
	wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);
}

从wait_for_completion函数的实现可以看出,不能在x上进行唤醒操作。则当前函数将会无限等待下去(MAX_SCHEDULE_TIMEOUT)而不会被信号打断(TASK_UNINTERRUPTIBLE)。

static void wakeme_after_rcu(struct rcu_head  *head)
{
	struct rcu_synchronize *rcu;
	rcu = container_of(head, struct rcu_synchronize, head);
	complete(&rcu->completion);
}

再看看wakeme_after_rcu函数的实现,这个函数的实现仅仅唤醒当前正在等待的任务而不会释放内存,因为内存在堆栈中。

void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
{
	__call_rcu(head, func, &rcu_preempt_state);
}
static void
__call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
	   struct rcu_state *rsp)
{
	unsigned long flags;
	struct rcu_data *rdp;
	head->func = func;
	head->next = NULL;
	smp_mb(); 
	local_irq_save(flags);
	rdp = this_cpu_ptr(rsp->rda);
	*rdp->nxttail[RCU_NEXT_TAIL] = head;
	rdp->nxttail[RCU_NEXT_TAIL] = &head->next;
	rdp->qlen++;
	if (irqs_disabled_flags(flags)) {
		local_irq_restore(flags);
		return;
	}
	if ((rdp->qlen > rdp->qlen_last_fqs_check + qhimark)) {
		rcu_process_gp_end(rsp, rdp);
		check_for_new_grace_period(rsp, rdp);
		if (!rcu_gp_in_progress(rsp)) {
			unsigned long nestflag;
			struct rcu_node *rnp_root = rcu_get_root(rsp);
			raw_spin_lock_irqsave(&rnp_root->lock, nestflag);
			rcu_start_gp(rsp, nestflag);  /* rlses rnp_root->lock */
		} else {
			rdp->blimit = LONG_MAX;
			if (rsp->n_force_qs == rdp->n_force_qs_snap &&
			    *rdp->nxttail[RCU_DONE_TAIL] != head)
				force_quiescent_state(rsp, 0);
			rdp->n_force_qs_snap = rsp->n_force_qs;
			rdp->qlen_last_fqs_check = rdp->qlen;
		}
	} 
	else if (ULONG_CMP_LT(rsp->jiffies_force_qs, jiffies))
		force_quiescent_state(rsp, 1);
	local_irq_restore(flags);
}

上面是call_rcu的具体实现,不过这个实现只包括一部分。因

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值