Linux 2.6 のランキューはCPUコアごとにある

Linux Kernel 2.6 のランキューはCPUコアごとにあると思ったが、調べてみた。

[24時間365日] サーバ/インフラを支える技術 ?スケーラビリティ、ハイパフォーマンス、省力運用 (WEB+DB PRESS plusシリーズ)

[24時間365日] サーバ/インフラを支える技術 ?スケーラビリティ、ハイパフォーマンス、省力運用 (WEB+DB PRESS plusシリーズ)

P.161

cpu_rq()はCPUに紐づいているランキューを取得するマクロです。

ということなので、CPUコアごとにあるようだ。*1


ついでに SytemTap でちょっと遊びながカーネルのソースを覗いてみた。

適当に requeue_task.stp という SystemTapスクリプトを用意して、

probe kernel.statement("requeue_task@kernel/sched.c"){
        print_backtrace()
        exit()  
}

実行してみると、

[root@localhost ~]# stap requeue_task.stp 
 0xc041eea6 : requeue_task+0x0/0x27 [kernel]
 0xc04205b8 : scheduler_tick+0x307/0x33e [kernel]
 0xc042dadf : update_process_times+0x57/0x62 [kernel]
 0xc041884a : smp_apic_timer_interrupt+0x5d/0x6f [kernel]
 0xc04059d7 : apic_timer_interrupt+0x1f/0x24 [kernel]

こんな感じでバックトレースがとれた。

/*
 * Local APIC timer interrupt. This is the most natural way for doing
 * local interrupts, but local timer interrupts can be emulated by
 * broadcast interrupts too. [in case the hw doesn't support APIC timers]
 *
 * [ if a single-CPU system runs an SMP kernel then we call the local
 *   interrupt as well. Thus we cannot inline the local irq ... ]
 */

fastcall void smp_apic_timer_interrupt(struct pt_regs *regs)
{
	int cpu = smp_processor_id();

	/*
	 * the NMI deadlock-detector uses this.
	 */
	per_cpu(irq_stat, cpu).apic_timer_irqs++;

	/*
	 * NOTE! We'd better ACK the irq immediately,
	 * because timer handling can be slow.
	 */
	ack_APIC_irq();
	/*
	 * update_process_times() expects us to have done irq_enter().
	 * Besides, if we don't timer interrupts ignore the global
	 * interrupt lock, which is the WrongThing (tm) to do.
	 */
	irq_enter();
	smp_local_timer_interrupt(regs);
	irq_exit();
}
  • kernel/timer.c
/*
 * Called from the timer interrupt handler to charge one tick to the current 
 * process.  user_tick is 1 if the tick is user time, 0 for system.
 */
void update_process_times(int user_tick)
{
	struct task_struct *p = current;
	int cpu = smp_processor_id();

	/* Note: this timer irq context must be accounted for as well. */
	if (user_tick)
		account_user_time(p, jiffies_to_cputime(1));
	else
		account_system_time(p, HARDIRQ_OFFSET, jiffies_to_cputime(1));
	run_local_timers();
	if (rcu_pending(cpu))
		rcu_check_callbacks(cpu, user_tick);
	scheduler_tick();
 	run_posix_cpu_timers(p);
}
  • kernel/sched.c
/*
 * This function gets called by the timer code, with HZ frequency.
 * We call it with interrupts disabled.
 *
 * It also gets called by the fork code, when changing the parent's
 * timeslices.
 */
void scheduler_tick(void)
{
	unsigned long long now = sched_clock();
	struct task_struct *p = current;
	int cpu = smp_processor_id();
	struct rq *rq = cpu_rq(cpu);

	update_cpu_clock(p, rq, now);

	rq->timestamp_last_tick = now;

	if (p == rq->idle) {
		if (wake_priority_sleeper(rq))
			goto out;
		rebalance_tick(cpu, rq, SCHED_IDLE);
		return;
	}

*1:厳密にはハイパースレッディングを使っている場合はスレッドごとだと思う