Linux内核线程(kthread)创建过程
我们在内核中创建并运行内核线程,直接调用kthread_run宏就可以实现。其原型为:/*** kthread_run - create and wake a thread.* @threadfn: the function to run until signal_pending(current).* @data: data ptr for @threadfn.* @namefm...
我们在内核中创建并运行内核线程,直接调用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线程中将执行我们需要的线程的函数。
更多推荐
所有评论(0)