Linux_Kernel 源码注解 Sched_3

schedule -> __schedule

static void __sched notrace __schedule(bool preempt)
{
	struct task_struct *prev, *next;
	unsigned long *switch_count;
	struct rq_flags rf;
	struct rq *rq;
	int cpu;
cpu = smp_processor_id();

smp_processor_id
在ARM32架构中展开为current_thread_info()->cpu,在ARM64中有专门的per-cpu变量cpu_number,
为什么ARM32中不使用per-cpu?毕竟per-cpu属于高速缓存,应该挺快的,不过current_thread_info是寄存器取值再位移,也非常快

rq = cpu_rq(cpu);

找当前cpu的rq,这个也是per-cpu

prev = rq->curr;

当前cpu执行的进程

schedule_debug(prev);

schedule时机的debug,包括栈指针,抢占检测,rcu检查

if (sched_feat(HRTICK))
		hrtick_clear(rq);

如果使用了Hrtick,就先清除hrtick,hrtick应该是高精度定时器的进程tick,
注意到这个hrtick是rq上的,主要在进程使用完理想时间片后触发的,用来被动启动schedule切换进程

local_irq_disable();

失能irq,再往下就是临界代码区了,必须完整运行,

rcu_note_context_switch(preempt);

rcu机制,暂时不太清楚具体的功能,以后再说

rq_lock(rq, &rf);

自旋锁定rq,rf应该是死锁检测功能的一部分,以后再看

smp_mb__after_spinlock();

内存屏障,有意思的是只有在ARM64中才有实现,ARM32位的没找到

rq->clock_update_flags <<= 1;

clock_update_flags 左移一位,clock_update_flags包含三个值:
RQCF_REQ_SKIP 0x01 下一次schedule的不更新rq_clock
RQCF_ACT_SKIP 0x02 这一次schedule的不更新rq_clock
RQCF_UPDATED 0x04 指示上一次rq_lock以来更新rq_clock没有
这个flag有啥用,为啥这样搞还不清楚

update_rq_clock(rq);

更新rq的时间戳,貌似也更新了rq的计算量

switch_count = &prev->nivcsw;

不知道是啥含义的切换次数,
应该是非自愿切换次数,也就是被抢占的次数

if (!preempt && prev->state) {

当不是内核抢占且当前进程不可运行时执行之后代码,
也就是这是内核抢占省下来的一段代码,

if (signal_pending_state(prev->state, prev)) {
		prev->state = TASK_RUNNING;

当进程有待处理的信号,并且进程可以被信号唤醒或者存在KILL信号,
就是强行唤醒进程,记得前面是因为prev->state不等于0才进入这里,这里又设为0了
不过该进程还没有离队,所以不用实际唤醒,只有设置state就行,之后也就不会离队了

} else {
			deactivate_task(rq, prev, DEQUEUE_SLEEP | DEQUEUE_NOCLOCK);

这个就是进程prev的真正离队 DEQUEUE_SLEEP 表示这是进程sleep的离队
DEQUEUE_NOCLOCK表示不需要更新rq_clock

if (prev->in_iowait) {
				atomic_inc(&rq->nr_iowait);
				delayacct_blkio_start();
			}

当前进程是否在io_wait?如果是,那么就增加rq中的计数,
并且开始记录当前进程的io延迟即current->delays->blkio_start = ktime_get_ns();

}
	switch_count = &prev->nvcsw;

转化成自愿切换次数,也就是自己时间片用尽或者阻塞后的切换次数

}
next = pick_next_task(rq, prev, &rf);

依靠sched_class的pick函数,选出下一个该执行的进程

clear_tsk_need_resched(prev);

清除进程的need_resched的flag,因为接下来就会切换了,就不需要need_resched了

clear_preempt_need_resched();

这个preempt.need_resched不清楚什么意思

if (likely(prev != next)) {

选出了的next进程不是prev,选出来的进程还是prev这个可能性有点不大,不清楚啥情况下会发生

rq->nr_switches++;
rq->curr = next;
++*switch_count;
trace_sched_switch(preempt, prev, next);

ftrace系统,用于故障调试或性能分析,不详谈

   rq = context_switch(rq, prev, next, &rf);

实际的切换过程,主要包括mm与寄存器的切换

} else {
		rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);

如果next还是prev,那么清除RQCF_ACT_SKIP|RQCF_REQ_SKIP,不清楚啥意思

	rq_unlock_irq(rq, &rf);
}
balance_callback(rq);

SMP系统特有的,回调函数,主要包含dl_class与rt_class的pull与push函数
在需要的地方注册进回调链表,在这里执行
注册pull的时机为: rq从有rt进程转变为没有rt进程且有其他进程时,注册pull rt 为什么不直接执行pull?不太清楚
rt进程的优先级增大时,注册pull 为啥不直接pull?
rt_class的pick函数中会注册pusk
rq从没有rt进程转变为有rt进程时,并且当前rq超载overload时,注册push

那么什么时刻主动pull和push呢?rt_class的pick时会判断是否需要主动pull
rootdomain中的rto_push_work备注为rto_push_irq_work_func,会判断push
task_woken_rt中也会判断是否push,这里条件比较复杂

}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
rcu_sched_clock_irq 是 Linux 内核中用于实现抢占式调度的一个重要函数。下面是该函数的源码: ```c /* * RCU scheduler clock interrupt. * * This function is called from the timer interrupt. It increments * the jiffies variable and then does a bunch of work. * * The math in this function is designed to be done with interrupts disabled * to prevent races between this and the timer interrupt. We use the * low-level clock interrupt to do this. */ static void rcu_sched_clock_irq(struct clock_event_device *dev) { ktime_t now; /* * Update jiffies before doing anything else. */ now = ktime_get(); if (time_after(now, dev->next_event)) rcu_tick_update(jiffies - dev->next_jiffies); dev->next_event = now + dev->delta; dev->next_jiffies = jiffies + 1; /* * Preempt idle tasks if needed. */ if (need_resched()) preempt_schedule_irq(); /* * Check for RCU grace period completion. */ if (rcu_scheduler_active && !rcu_is_watching()) rcu_check_callbacks(1); /* * Update jiffies after doing everything else. */ rcu_tick_update(jiffies - dev->next_jiffies); dev->next_jiffies = jiffies + 1; } ``` 这个函数会在定时器中断中被调用。它会先更新 jiffies 变量,然后执行一些工作。在这个函数中,使用低级别的时钟中断来禁止中断,以防止与定时器中断之间的竞争。函数的具体逻辑如下: 1. 更新 jiffies 变量,确保 jiffies 的值正确。 2. 如果当前需要进行抢占调度,则调用 preempt_schedule_irq() 函数进行抢占。 3. 检查 RCU grace period 是否已完成,如果是,则调用 rcu_check_callbacks() 函数。 4. 最后再次更新 jiffies 变量。 这段代码的作用是实现抢占式调度,并在特定条件下执行 RCU grace period 检查。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值