マルチCPU
Rev.1を表示中。最新版はこちら。
ちゃんと理解しきれておらず、推測を交えての記載です。マルチCPUはメモリを共有するCPU毎をバーチャルなコンピュータとし、ただしCPU単位で管理するrunキュー等は、CPU IDをインデックスとしてCPU毎に設定されます。forkで新しくタスクを作成し、それを条件に応じた該当CPUのrunキューに登録する事で実装されているようです。
カーネル起動時、CPU毎にCPU IDのidleタスクが起動され、このタスクによりかかるrunキューのタスクを起床させるようです。CPU IDはprocess->cpuに設定されます。
カレントプロセスのCPUIDを/proc/cpu-idで表示するサンプルです。私の環境は2CPUです。catコマンドでのforkによるプロセスが、CPU0/1下て起動されるのが確認できます。
[root@localhost lkm]# cat proc_cpuid.c #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/sched.h> static int cpuid_proc_show(struct seq_file *m, void *v) { seq_printf(m, "%s cpuid:%d \n", rt_task(current)? "rt": "fair", get_cpu()); return 0; } static int cpuid_proc_open(struct inode *inode, struct file *file) { return single_open(file, cpuid_proc_show, NULL); } static const struct file_operations cpuid_proc_fops = { .open = cpuid_proc_open, .read = seq_read, }; static int __init cpuid_init(void) { proc_create("cpu-id", 0, NULL, &cpuid_proc_fops); return 0; } static void __exit cpuid_exit( void ) { remove_proc_entry("cpu-id", NULL); } module_init(cpuid_init); module_exit(cpuid_exit);検証
[root@localhost lkm]# insmod proc_cpuid.ko [root@localhost lkm]# cat /proc/cpu-id fair cpuid:0 [root@localhost lkm]# cat /proc/cpu-id fair cpuid:1 [root@localhost lkm]# cat /proc/cpu-id fair cpuid:0 [root@localhost lkm]# cat /proc/cpu-id fair cpuid:0 [root@localhost lkm]# cat /proc/cpu-id fair cpuid:0 [root@localhost lkm]# cat /proc/cpu-id fair cpuid:1 [root@localhost lkm]# cat /proc/cpu-id fair cpuid:0 [root@localhost lkm]# cat /proc/cpu-id fair cpuid:1 [root@localhost lkm]# cat /proc/cpu-id fair cpuid:1forkからp = copy_processで子プロセスを作成し、wake_up_new_task()で子プロセスを起床させます。set_task_cpu()でselect_task_rq()で条件に応じたCPU IDを取得し、子プロセスp->cpuに設定します。そして、__task_rq_lock()で子プロセスp->cpuのCPUのrunキューを取得し、activate_task()でそのrunキューにプロセスをリストします。
runキューにプロセスを登録する時、プロセスのはenqueue_taskコールバックはp->sched_class下のコールバック関数がコールされ、リアルタイムタスク/フェアープロセスによる実装が行われます。
void wake_up_new_task(struct task_struct *p) { unsigned long flags; struct rq *rq; raw_spin_lock_irqsave(&p->pi_lock, flags); #ifdef CONFIG_SMP set_task_cpu(p, select_task_rq(p, SD_BALANCE_FORK, 0)); #endif rq = __task_rq_lock(p); activate_task(rq, p, 0); p->on_rq = 1; trace_sched_wakeup_new(p, true); check_preempt_curr(rq, p, WF_FORK); #ifdef CONFIG_SMP if (p->sched_class->task_woken) p->sched_class->task_woken(rq, p); #endif task_rq_unlock(rq, p, &flags); } void activate_task(struct rq *rq, struct task_struct *p, int flags) { if (task_contributes_to_load(p)) rq->nr_uninterruptible--; enqueue_task(rq, p, flags); } static void enqueue_task(struct rq *rq, struct task_struct *p, int flags) { update_rq_clock(rq); sched_info_queued(p); p->sched_class->enqueue_task(rq, p, flags); }