ふと気になったのでめも。
実際にidleスレッドを作るのはinit_idle()だけどそこに至までの流れは結構深い。
start_kernel()
--> sched_init()
--> init_idle()
--> rest_init()
--> kernel_init()
--> kernel_init_freeable()
--> smp_init()
--> idle_threads_init()
--> idle_init()
--> fork_idle()
--> init_idle()
init_idle()が呼ばれるのは見た限りでは2パターンがある模様。
- start_kernel() -> sched_init() -> init_idle()
- start_kernel() -> rest_init() -> 色々 -> init_idle()
ただし、sched_init()の場合は現在のcpu用につくるだけっぽい。
6539 /* 6540 * Make us the idle thread. Technically, schedule() should not be 6541 * called from this thread, however somewhere below it might be, 6542 * but because we are the idle thread, we just pick up running again 6543 * when this runqueue becomes "idle". 6544 */ 6545 init_idle(current, smp_processor_id());
いずれにせよcpuコア分のidleスレッドは作る必要が有るのでidle_threads_init()でカーネルが認識している数分のループでidleスレッド作成。idle_threads_init()はsmp_init()から呼ばれる。
64 void __init idle_threads_init(void) 65 { 66 unsigned int cpu, boot_cpu; 67 68 boot_cpu = smp_processor_id(); 69 70 for_each_possible_cpu(cpu) { 71 if (cpu != boot_cpu) 72 idle_init(cpu); 73 } 74 }
ここの71行目のif文でboot_cpuでない場合ってチェックがあるのはsched_init()ですでにidleスレッドを作っているからかな。。
start_kernel()からsched_init()を呼んだ時点ではsmp_init()も呼ばれていないのでcpuは1個しか使われていないはず。
idle_initではcpu毎のtask構造体にデータを設定する(fork_idle()で)。
48 static inline void idle_init(unsigned int cpu) 49 { 50 struct task_struct *tsk = per_cpu(idle_threads, cpu); 51 52 if (!tsk) { 53 tsk = fork_idle(cpu); 54 if (IS_ERR(tsk)) 55 pr_err("SMP: fork_idle() failed for CPU %u\n", cpu); 56 else 57 per_cpu(idle_threads, cpu) = tsk; 58 } 59 } 60
idle_threadsはkernel/smpboot.c にて定義。
20 #ifdef CONFIG_GENERIC_SMP_IDLE_THREAD 21 /* 22 * For the hotplug case we keep the task structs around and reuse 23 * them. 24 */ 25 static DEFINE_PER_CPU(struct task_struct *, idle_threads);
fork_idle()は名前から予想が付く通りでプロセスをforkする。
1550 struct task_struct *fork_idle(int cpu) 1551 { 1552 struct task_struct *task; 1553 task = copy_process(CLONE_VM, 0, 0, NULL, &init_struct_pid, 0); 1554 if (!IS_ERR(task)) { 1555 init_idle_pids(task->pids); 1556 init_idle(task, cpu); 1557 } 1558 1559 return task; 1560 }
copy_process()のプロトタイプは以下の通り。
1131 static struct task_struct *copy_process(unsigned long clone_flags, 1132 unsigned long stack_start, 1133 unsigned long stack_size, 1134 int __user *child_tidptr, 1135 struct pid *pid, 1136 int trace) 1137 {
fork_idle()ではcopy_process()のpid引数にinit_struct_pid構造体へのポインタを渡していて、この構造体はkernel/pid.cで初期化されている。
46 struct pid init_struct_pid = INIT_STRUCT_PID;
INIT_STRUCT_PIDマクロはと言うと、linux/init_task.hにて定義されている構造体を初期化するためのマクロ。
71 #define INIT_STRUCT_PID { \ 72 .count = ATOMIC_INIT(1), \ 73 .tasks = { \ 74 { .first = NULL }, \ 75 { .first = NULL }, \ 76 { .first = NULL }, \ 77 }, \ 78 .level = 0, \ 79 .numbers = { { \ 80 .nr = 0, \ 81 .ns = &init_pid_ns, \ 82 .pid_chain = { .next = NULL, .pprev = NULL }, \ 83 }, } \ 84 }
copy_process()に戻って、このpid構造体が使われる場所は2ヶ所。
1ヶ所はこのチェック部分で、pidがinit_struct_pid出ない場合にpid構造体のメモリを確保する時。idleスレッド作成時はこのif文内には入りませんが。
1353 if (pid != &init_struct_pid) { 1354 retval = -ENOMEM; 1355 pid = alloc_pid(p->nsproxy->pid_ns_for_children); 1356 if (!pid) 1357 goto bad_fork_cleanup_io; 1358 }
2ヶ所目はここでidleスレッドのpidを決定するのと、このスレッドのスレッドグループIDを決定する部分。
1394 /* ok, now we should be set up.. */ 1395 p->pid = pid_nr(pid); 1396 if (clone_flags & CLONE_THREAD) { 1397 p->exit_signal = -1; 1398 p->group_leader = current->group_leader; 1399 p->tgid = current->tgid; 1400 } else { 1401 if (clone_flags & CLONE_PARENT) 1402 p->exit_signal = current->group_leader->exit_signal; 1403 else 1404 p->exit_signal = (clone_flags & CSIGNAL); 1405 p->group_leader = p; 1406 p->tgid = p->pid; 1407 }
pid_nr()はinclude/linux/pid.hで定義されていて
164 static inline pid_t pid_nr(struct pid *pid) 165 { 166 pid_t nr = 0; 167 if (pid) 168 nr = pid->numbers[0].nr; 169 return nr; 170 }
pid構造体のnumbers配列の0番目の要素のnrの値を返しています。今使っているpid構造体はINIT_STRUCT_PIDで初期化されているのでこれを見ると、
78 .level = 0, \ 79 .numbers = { { \ 80 .nr = 0, \ 81 .ns = &init_pid_ns, \ 82 .pid_chain = { .next = NULL, .pprev = NULL }, \ 83 }, }
nrは0となりますね。ということでpidは0。pidが決まったら次は以下の分岐ですが、fork_idle()ではclone_flagsにCLONE_VMしか設定していないのでelse文が実行されます。
1396 if (clone_flags & CLONE_THREAD) { 1397 p->exit_signal = -1; 1398 p->group_leader = current->group_leader; 1399 p->tgid = current->tgid; 1400 } else { 1401 if (clone_flags & CLONE_PARENT) 1402 p->exit_signal = current->group_leader->exit_signal; 1403 else 1404 p->exit_signal = (clone_flags & CSIGNAL); 1405 p->group_leader = p; 1406 p->tgid = p->pid; 1407 }
よってtgidも0ですね。
copy_processが成功すればfork_idle()はinit_idle_pids()とinit_idle()を実行する。
1554 if (!IS_ERR(task)) { 1555 init_idle_pids(task->pids); 1556 init_idle(task, cpu); 1557 }
init_idle_pidsはこのような関数。
1540 static inline void init_idle_pids(struct pid_link *links) 1541 { 1542 enum pid_type type; 1543 1544 for (type = PIDTYPE_PID; type < PIDTYPE_MAX; ++type) { 1545 INIT_HLIST_NODE(&links[type].node); /* not really needed */ 1546 links[type].pid = &init_struct_pid; 1547 } 1548 }
PIDTYPE_PIDやPIDTYPE_MAXはinclude/linux/pid.hで定義されているenumの定数
6 enum pid_type 7 { 8 PIDTYPE_PID, 9 PIDTYPE_PGID, 10 PIDTYPE_SID, 11 PIDTYPE_MAX 12 };
init_idle()に。
この関数でタスクの状態などが設定されたり、
4189 __sched_fork(idle); 4190 idle->state = TASK_RUNNING; 4191 idle->se.exec_start = sched_clock();
ランキューの設定が有ったり、
4208 rq->curr = rq->idle = idle;
スケジューラーの設定があったり、
4217 /* 4218 * The idle tasks have their own, simple scheduling class: 4219 */ 4220 idle->sched_class = &idle_sched_class;
プロセス名の設定をするなどが行われる。
4223 #if defined(CONFIG_SMP) 4224 sprintf(idle->comm, "%s/%d", INIT_TASK_COMM, cpu); 4225 #endif
idle->commで設定された名前は例えばkmeleakで見た場合のcommで確認できますね。
unreferenced object 0xffff8808529e4960 (size 32): 2 comm "swapper/0", pid 1, jiffies 4294877548 (age 8061.956s) 3 hex dump (first 32 bytes): 4 00 01 10 00 00 00 ad de 00 02 20 00 00 00 ad de .......... ..... 5 00 14 8a 4e 08 88 ff ff 01 00 00 00 00 00 00 00 ...N............ 6 backtrace: 7 [<ffffffff814db54e>] kmemleak_alloc+0x4e/0xb0 8
φ(・・*)ゞ ウーン init_idle()の中でスケジューラ関連の部分はよく分からないのでまた調べよう