linux內核中兩大重要的線程,migration_thread負責cpu的負載均衡(將進程從本地隊列移動到目標cpu的隊列),kthreadd負責爲kthread_create_list鏈表中的成員創建內核線程。
內核版本2.6.24中的引導部分,start_kernel()->rest_init():
點擊(此處)摺疊或打開
static void noinline __init_refok rest_init(void)
__releases(kernel_lock)
{
int pid;
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
numa_default_policy();
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
kthreadd_task = find_task_by_pid(pid);
……
}
以上依次創建了kernel_init線程和kthreadd線程,rest_init()是在禁用搶佔(preempt_disable)的情況下運行,因此保證了kernel_init()運行時kthreadd_task 已經指向ktheadd線程。
kernel_init()調用do_pre_smp_initcalls()->migration_init();創建了負責將進程在cpu間移動(cpu負載均衡)的內核線程migration_thread(每個cpu一個),創建線程是通過將包含待運行函數及參數的kthread_create_info結構掛入kthread_create_list鏈表,然後喚醒kthreadd_task(即ktheadd線程),而ktheadd線程負責爲鏈表上的每個結構創建相應的線程。
點擊(此處)摺疊或打開
void __init migration_init(void)
{
void *cpu = (void *)(long)smp_processor_id();
int err;
/* Start one for the boot CPU: */
err = migration_call(&migration_notifier, CPU_UP_PREPARE, cpu);
BUG_ON(err == NOTIFY_BAD);
migration_call(&migration_notifier, CPU_ONLINE, cpu);
register_cpu_notifier(&migration_notifier);
}
首先直接調用migration_call兩次創建了引導cpu的migration_thread線程並喚醒,然後調用register_cpu_notifier()將migration_notifier掛入cpu_chain鏈表,在之後kernel_init()->smp_init()中將依次對其餘未上線的cpu調用cpu_up()->_cpu_up(),該函數分別以參數CPU_UP_PREPARE和CPU_ONLINE調用兩次__raw_notifier_call_chain(),實則是運行cpu_chain鏈表上的函數,也包括了migration_call(),因此其餘cpu的migration_thread也得以創建,最終是每個cpu上都有一個migration_thread線程。
點擊(此處)摺疊或打開
static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen)
{
int ret, nr_calls = 0;
void *hcpu = (void *)(long)cpu;
unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0;
if (cpu_online(cpu) || !cpu_present(cpu))
return -EINVAL;
raw_notifier_call_chain(&cpu_chain, CPU_LOCK_ACQUIRE, hcpu);
ret = __raw_notifier_call_chain(&cpu_chain, CPU_UP_PREPARE | mod, hcpu,
-1, &nr_calls);
……
/* Now call notifier in preparation. */
raw_notifier_call_chain(&cpu_chain, CPU_ONLINE | mod, hcpu);
……
}
/*
1、以CPU_UP_PREPARE爲參數調用時,創建migration_thread線程,並綁定到cpu,設置調用策略爲實時進程SCHED_FIFO,
優先級99,cpu運行隊列的migration_thread指向該內核線程,此時線程是不可中斷睡眠狀態。
2、以CPU_ONLINE爲參數調用時,喚醒cpu_rq(cpu)->migration_thread指向的migration_thread線程。
*/
點擊(此處)摺疊或打開
static int __cpuinit
migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
{
struct task_struct *p;
int cpu = (long)hcpu;
unsigned long flags;
struct rq *rq;
switch (action) {
case CPU_LOCK_ACQUIRE:
mutex_lock(&sched_hotcpu_mutex);
break;
case CPU_UP_PREPARE:
case CPU_UP_PREPARE_FROZEN:
p = kthread_create(migration_thread, hcpu, "migration/%d", cpu);
if (IS_ERR(p))
return NOTIFY_BAD;
kthread_bind(p, cpu);
/* Must be high prio: stop_machine expects to yield to it. */
rq = task_rq_lock(p, &flags);
__setscheduler(rq, p, SCHED_FIFO, MAX_RT_PRIO-1);
task_rq_unlock(rq, &flags);
cpu_rq(cpu)->migration_thread = p;
break;
case CPU_ONLINE:
case CPU_ONLINE_FROZEN:
/* Strictly unnecessary, as first user will wake it. */
wake_up_process(cpu_rq(cpu)->migration_thread);
break;
……
}
/*創建線程是通過將包含線程運行函數和參數的kthread_create_info結構掛入kthread_create_list鏈表,
並喚醒內核線程kthreadd_task對鏈表上的各個線程創建需求進行處理來實現的,當創建成功後返回進程指針,
這是通過wait_for_completion(&create.done)進行同步的。此內核線程創建函數不負責將線程綁定到cpu,綁定工作
須由調用函數負責。
*/
點擊(此處)摺疊或打開
struct task_struct *kthread_create(int (*threadfn)(void *data),
void *data,
const char namefmt[],
...)
{
struct kthread_create_info create;
create.threadfn = threadfn;
create.data = data;
init_completion(&create.started);
init_completion(&create.done);
spin_lock(&kthread_create_lock);
list_add_tail(&create.list, &kthread_create_list);
wake_up_process(kthreadd_task);
spin_unlock(&kthread_create_lock);
wait_for_completion(&create.done);
……
return create.result;
}