前言
Linux下有3個特殊的程序,idle程序(PID = 0), init程序(PID = 1)和kthreadd(PID = 2):
a. PID=0 系統自動建立、運作在核心态;
b. PID=1 由0程序建立,完成系統的初始化. 是系統中所有其它使用者程序的祖先程序Linux中的所有程序都是有init程序建立并運作的。首先Linux核心啟動,然後在使用者空間中啟動init程序,再啟動其他系統程序。在系統啟動完成完成後,init将變為守護程序監視系統其他程序;
c. PID=2 它的任務就是管理和排程其他核心線程kernel_thread, 會循環執行一個kthreadd的函數,該函數的作用就是運作kthread_create_list全局連結清單中維護的kthread, 當我們調用kernel_thread建立的核心線程會被加入到此連結清單中,是以所有的核心線程都是直接或者間接的以kthreadd為父程序。
1. kthreadd線程
路徑:\linux-3.10.x\init\main.c start_kernel()-->rest_init();
static noinline void __init_refok rest_init(void)
{
int pid;
rcu_scheduler_starting();
/*
* We need to spawn init first so that it obtains pid 1, however
* the init task will end up wanting to create kthreads, which, if
* we schedule it before we create kthreadd, will OOPS.
*/
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
numa_default_policy();
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
rcu_read_lock();
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
rcu_read_unlock();
complete(&kthreadd_done); //喚醒完成量
/*
* The boot idle thread must execute schedule()
* at least once to get things moving:
*/
init_idle_bootup_task(current);
schedule_preempt_disabled();
/* Call into cpu_idle with preempt disabled */
cpu_startup_entry(CPUHP_ONLINE);
}
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); kthread線程建立,源碼如下:
int kthreadd(void *unused)
{
struct task_struct *tsk = current;
/* Setup a clean context for our children to inherit. */
//為我們的子程序設定一個幹淨的上下文
set_task_comm(tsk, "kthreadd");
ignore_signals(tsk);
set_cpus_allowed_ptr(tsk, cpu_all_mask); //運作kthread在任意cpu上運作
set_mems_allowed(node_states[N_MEMORY]);
current->flags |= PF_NOFREEZE;
for (;;) {
set_current_state(TASK_INTERRUPTIBLE); //設定目前程序為中斷模式,即可被cpu打斷
if (list_empty(&kthread_create_list)) //kthread_create_list 連結清單是否為空,為空就調用schedule()讓出cpu進入休眠
schedule();
__set_current_state(TASK_RUNNING); //到這裡表示此程序已從休眠狀态恢複,即kthread_create_list連結清單不為空,設定目前程序為運作态
spin_lock(&kthread_create_lock);
while (!list_empty(&kthread_create_list)) { //判斷目前連結清單是否為空,不為空就進行相應程序建立
struct kthread_create_info *create;
create = list_entry(kthread_create_list.next,
struct kthread_create_info, list);
list_del_init(&create->list);
spin_unlock(&kthread_create_lock);
create_kthread(create); //完成真正的程序建立
spin_lock(&kthread_create_lock);
}
spin_unlock(&kthread_create_lock);
}
return 0;
}
這裡要看下是在哪裡對kthread_create_list連結清單進行添加的, 在連結清單裡主要是線程執行函數、線程參數進行綁定:
struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
void *data, int node,
const char namefmt[],
...)
{
struct kthread_create_info create;
create.threadfn = threadfn; //線程執行函數
create.data = data; //線程執行函數參數
create.node = node;
init_completion(&create.done); //初始化線程完成量
spin_lock(&kthread_create_lock);
list_add_tail(&create.list, &kthread_create_list); //加入到kthread_create_list連結清單中
spin_unlock(&kthread_create_lock);
wake_up_process(kthreadd_task); //喚醒kthreadd_task核心任務線程
wait_for_completion(&create.done); //等待threadfn線程完成//至此線程建立完畢 , 會喚醒kthread_create_on_node()函數内的完成量wait_for_completion(&create.done);
if (!IS_ERR(create.result)) {
static const struct sched_param param = { .sched_priority = 0 };
va_list args;
va_start(args, namefmt);
vsnprintf(create.result->comm, sizeof(create.result->comm),
namefmt, args);
va_end(args);
/*
* root may have changed our (kthreadd's) priority or CPU mask.
* The kernel thread should not inherit these properties.
*/
sched_setscheduler_nocheck(create.result, SCHED_NORMAL, ¶m);
set_cpus_allowed_ptr(create.result, cpu_all_mask);
}
return create.result;
}
EXPORT_SYMBOL(kthread_create_on_node);
通過create_kthread(create),完成真正的程序建立:
static void create_kthread(struct kthread_create_info *create)
{
int pid;
#ifdef CONFIG_NUMA
current->pref_node_fork = create->node;
#endif
/* We want our own signal handler (we take no signals by default). */
pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
if (pid < 0) {
create->result = ERR_PTR(pid);
complete(&create->done);
}
}
調用kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD)完成create線程的建立:
static int kthread(void *_create)
{
/* Copy data: it's on kthread's stack */
struct kthread_create_info *create = _create;
int (*threadfn)(void *data) = create->threadfn; //新的程序建立完後執行的函數
void *data = create->data; //建立程序的參數
struct kthread self;
int ret;
self.flags = 0;
self.data = data;
init_completion(&self.exited);
init_completion(&self.parked);
current->vfork_done = &self.exited;
/* OK, tell user we're spawned, wait for stop or wakeup */
__set_current_state(TASK_UNINTERRUPTIBLE);
create->result = current; //current 表示目前新建立的 thread 的 task_struct 結構
complete(&create->done); //至此線程建立完畢 , 會喚醒kthread_create_on_node()函數内的等待完成量wait_for_completion(&create.done);
schedule();
ret = -EINTR;
if (!test_bit(KTHREAD_SHOULD_STOP, &self.flags)) {
__kthread_parkme(&self);
ret = threadfn(data); //執行新程序中的函數
}
/* we can't just return, we must preserve "self" on stack */
do_exit(ret);
}//至此線程建立完畢 , 會喚醒kthread_create_on_node()函數内的等待完成量wait_for_completion(&create.done);
schedule();
ret = -EINTR;
if (!test_bit(KTHREAD_SHOULD_STOP, &self.flags)) {
__kthread_parkme(&self);
ret = threadfn(data); //執行新程序中的函數
}
/* we can't just return, we must preserve "self" on stack */
do_exit(ret);
}
線程建立完畢:
建立新 thread 的程序恢複運作 kthread_create() 并且傳回新建立線程的任務描述符 新建立的線程由于執行了 schedule() 排程,此時并沒有執行.直到我們使用wake_up_process(p);喚醒新建立的線程線程被喚醒後, 會接着執行threadfn(data)
總結:
kthreadd程序由idle通過kernel_thread建立,并始終運作在核心空間, 負責所有核心線程的排程和管理,它的任務就是管理和排程其他核心線程kernel_thread, 會循環執行一個kthreadd的函數,該函數的作用就是運作kthread_create_list全局連結清單中維護的kthread, 當我們調用kernel_thread建立的核心線程會被加入到此連結清單中,是以所有的核心線程都是直接或者間接的以kthreadd為父程序我們在核心中通過kernel_create或者其他方式建立一個核心線程, 然後kthreadd核心線程被喚醒, 來執行核心線程建立的真正工作,新的線程将執行kthread函數, 完成建立工作,建立完畢後讓出CPU,是以新的核心線程不會立刻運作.需要手工 wake up, 被喚醒後将執行自己的真正工作函數。
任何一個核心線程入口都是 kthread()
通過 kthread_create() 建立的核心線程不會立刻運作.需要手工 wake up.
通過 kthread_create() 建立的核心線程有可能不會執行相應線程函數threadfn而直接退出