Linux核心執行緒(kthread)建立過程
阿新 • • 發佈:2019-01-24
我們在核心中建立並執行核心執行緒,直接呼叫kthread_run巨集就可以實現。其原型為:
/** * kthread_run - create and wake a thread. * @threadfn: the function to run until signal_pending(current). * @data: data ptr for @threadfn. * @namefmt: printf-style name for the thread. * * Description: Convenient wrapper for kthread_create() followed by * wake_up_process(). Returns the kthread or ERR_PTR(-ENOMEM). */ #define kthread_run(threadfn, data, namefmt, ...) \ ({ \ struct task_struct *__k \ = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \ if (!IS_ERR(__k)) \ wake_up_process(__k); \ __k; \ })
可以看到,kthread_run首先使用kthread_create建立核心執行緒,然後呼叫wake_up_process喚醒建立的執行緒。
kthread_run是一個巨集定義,先是建立一個task_struct執行緒結構,然後呼叫wake_up喚醒。
#define kthread_create(threadfn, data, namefmt, arg...) \
kthread_create_on_node(threadfn, data, -1, namefmt, ##arg)
核心建立執行緒,實際呼叫到了kthread_create_on_node函式。
struct task_struct *kthread_create_on_node(int (*threadfn)(void *data), void *data, int node, const char namefmt[], ...) { DECLARE_COMPLETION_ONSTACK(done); struct task_struct *task; struct kthread_create_info *create = kmalloc(sizeof(*create), GFP_KERNEL); if (!create) return ERR_PTR(-ENOMEM); create->threadfn = threadfn; create->data = data; create->node = node; create->done = &done; spin_lock(&kthread_create_lock); list_add_tail(&create->list, &kthread_create_list); spin_unlock(&kthread_create_lock); wake_up_process(kthreadd_task); /* * Wait for completion in killable state, for I might be chosen by * the OOM killer while kthreadd is trying to allocate memory for * new kernel thread. */ if (unlikely(wait_for_completion_killable(&done))) { /* * If I was SIGKILLed before kthreadd (or new kernel thread) * calls complete(), leave the cleanup of this structure to * that thread. */ if (xchg(&create->done, NULL)) return ERR_PTR(-EINTR); /* * kthreadd (or new kernel thread) will call complete() * shortly. 等待執行緒建立結束 */ wait_for_completion(&done); } task = create->result; if (!IS_ERR(task)) { static const struct sched_param param = { .sched_priority = 0 }; va_list args; va_start(args, namefmt); vsnprintf(task->comm, sizeof(task->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(task, SCHED_NORMAL, ¶m); set_cpus_allowed_ptr(task, cpu_all_mask); } kfree(create); return task; } EXPORT_SYMBOL(kthread_create_on_node);
從上面程式碼可以看到,實際的執行緒建立並不是由kthread_create_on_node完成,而是通過一個kthreadd_task執行緒完成。通過填充kthread_create_info結構,並加入到kthread_create_list,然後kthreadd_task從kthread_create_list取出需要建立的執行緒資訊,來建立核心執行緒。
kthreadd_task執行緒,在核心啟動的早期便建立。
@/kernel/init/main.c static __initdata DECLARE_COMPLETION(kthreadd_done); static noinline void __init_refok rest_init(void) { int pid; rcu_scheduler_starting(); smpboot_thread_init(); /* * 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); 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); }
上面kernel_thread()函式呼叫do_fork建立核心執行緒。
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
return do_fork(flags|CLONE_VM|CLONE_UNTRACED, (unsigned long)fn,
(unsigned long)arg, NULL, NULL);
}
這裡建立了兩個執行緒,一個kernel_init,一個是kthreadadd。
此處我們先看kthreadadd執行緒,對應的執行緒函式為kthreadd。
@/kernel/kernel/kthread.c
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);
set_mems_allowed(node_states[N_MEMORY]);
current->flags |= PF_NOFREEZE;
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (list_empty(&kthread_create_list))
schedule();
__set_current_state(TASK_RUNNING);
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為空,則手動進行排程,放棄CPU時間。
- 如果kthread_create_list不為空,則從該連結串列取出每個kthread_create_info結構。
- 使用取出的個kthread_create_info結構,呼叫create_kthread()函式建立執行緒。
@/kernel/kernel/kthread.c
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) {
/* If user was SIGKILLed, I release the structure. */
struct completion *done = xchg(&create->done, NULL);
if (!done) {
kfree(create);
return;
}
// 將task_struct返回
create->result = ERR_PTR(pid);
// 喚醒建立執行緒的執行緒
complete(done);
}
}
這裡用呼叫kernel_thread建立一個執行緒函式為kthread的執行緒,在該執行緒中執行我們的執行緒函式。因此對於所以執行緒的執行,都是從kthread開始的。
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;
__set_current_state(TASK_UNINTERRUPTIBLE);
create->result = current;
complete(done);
// 建立執行緒後,排程出去等待喚醒
schedule();
// 不是stop狀態,呼叫我們的執行緒函式
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_info結構,賦值執行緒的操作函式,資料等;
- 將執行緒的kthread_create_info結構新增到kthread_create_list連結串列,並喚醒kthreadd執行緒。
- kthreadd執行緒執行緒將從kthread_create_list連結串列取出每一個kthread_create_info結構,並呼叫create_kthread()函式建立一個函式為kthread的執行緒,在kthread執行緒中將執行我們需要的執行緒的函式。