linux kthread

来源:互联网 发布:真正美腿的数据化标准 编辑:程序博客网 时间:2024/05/16 06:38

前言


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_NUMAcurrent->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);}

线程创建完毕:
创建新 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而直接退出




原创粉丝点击