在看linux驱动代码的时候,经常惠会碰到kthread_create这个函数,google一下,发现很多人在讲二者的区别,但是都在讲源码的区别而已,总结不够,感觉没有说出二者之间的本质区别,自己总结下。
    一. 源码分析(linux-2.6.39)
1.  kthread_create源码分析

  • #define kthread_create(threadfn, data, namefmt, arg...)\ kthread_create_on_node(threadfn, data, -1, namefmt, ##arg)
  • 149 struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
  • 150 void *data,
  • 151 int node,
  • 152 const char namefmt[],
  • 153 ...)
  • 154 {
  • 155 struct kthread_create_info create;
  • 156
  • 157 create.threadfn = threadfn;
  • 158 create.data = data;
  • 159 create.node = node;
  •     /*kthread_create采用了完成量机制,不明白的同学可以先去查阅下等待量机制的原理*/
  • 160 init_completion(&create.done);
  • 161
  • 162 spin_lock(&kthread_create_lock);
  •     /*注意这个全局链表kthread_create_list, 所用通过kthread_create创建的内核线程都会挂在这*/
  • 163 list_add_tail(&create.list, &kthread_create_list);
  • 164 spin_unlock(&kthread_create_lock);
  •     /*这是最重要的地方,从代码看是唤醒了kthreadd_task这个进程,如果对代码比较熟悉的话,就会想到这是内核中  的1号进程kthreadd*/
  • 166 wake_up_process(kthreadd_task);
  •    /*当前进程在完成量上睡眠等待*/
  • 167 wait_for_completion(&create.done);
  • 168
  • 169 if (!IS_ERR(create.result)) {
  • 170 static const struct sched_param param = { .sched_priority = 0 };
  • 171 va_list args;
  • 172
  • 173 va_start(args, namefmt);
  • 174 vsnprintf(create.result->comm, sizeof(create.result->comm),
  • 175 namefmt, args);
  • 176 va_end(args);
  • 177 /*
  • 178 * root may have changed our (kthreadd's) priority or CPU mask.
  • 179 * The kernel thread should not inherit these properties.
  • 180 */
  • 181 sched_setscheduler_nocheck(create.result, SCHED_NORMAL, &param);
  • 182 set_cpus_allowed_ptr(create.result, cpu_all_mask);
  • 183 }
  • 184 return create.result;
  • 185 }
  • 具体讲解参见注释,上面代码的执行路径跳转到kthreadd这个内核线程,下面分析下kthreadd的代码:

  • 249
  • 250 int kthreadd(void *unused)
  • 251 {
  • 252 struct task_struct *tsk = current;
  • 253
  • 254 /* Setup a clean context for our children to inherit. */
  •     /*上面这段注释尤为重要,个人认为这就是kthread_create和kernel_thread的本质区别,对current做了一些       /*处理:设置当前进程的进程名,清空当前进程的信号等,都是为了保证一个干净的上下文环境*/
  • 255 set_task_comm(tsk, "kthreadd");
  • 256 ignore_signals(tsk);
  • 257 set_cpus_allowed_ptr(tsk, cpu_all_mask);
  • 258 set_mems_allowed(node_states[N_HIGH_MEMORY]);
  • 259
  • 260 current->flags |= PF_NOFREEZE | PF_FREEZER_NOSIG;
  • 261
  • 262 for (;;) {
  • 263 set_current_state(TASK_INTERRUPTIBLE);
  • 264 if (list_empty(&kthread_create_list))
  • 265 schedule();  /*如果没有内核线程需要创建,即不是通过kthread_create入口进来的,当前进程就应该调度*/
  • 266 __set_current_state(TASK_RUNNING);
  • 267
  • 268 spin_lock(&kthread_create_lock);
  • 269 while (!list_empty(&kthread_create_list)) { /*遍历全局链表kthread_create_list,创建内核线程*/
  • 270 struct kthread_create_info *create;
  • 271
  • 272 create = list_entry(kthread_create_list.next,
  • 273 struct kthread_create_info, list);
  • 274 list_del_init(&create->list);
  • 275 spin_unlock(&kthread_create_lock);
  • 276
  • 277 create_kthread(create);/代码执行路径转入create_kthread/
  • 278
  • 279 spin_lock(&kthread_create_lock);
  • 代码执行路径转到create_kthread。

  • 112 static void create_kthread(struct kthread_create_info *create)
  • 113 {
  • 114 int pid;
  • 115
  • 116 #ifdef CONFIG_NUMA
  • 117 current->pref_node_fork = create->node;
  • 118 #endif
  • 119 /* We want our own signal handler (we take no signals by default). */
  • 120 pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
  • 121 if (pid < 0) {
  • 122 create->result = ERR_PTR(pid);
  • 123 complete(&create->done);
  • 124 }
  • 125 }
  • 126
  • 这里我们看到了kernel_thread,而且通过kthread_create入口进来的内核线程创建路径都具有统一的线程函数kthread。

  • 74 static int kthread(void *_create)
  •  75 {
  •  76 /* Copy data: it's on kthread's stack */
  •  77 struct kthread_create_info *create = _create;
  •  78 int (*threadfn)(void *data) = create->threadfn;
  •  79 void *data = create->data;
  •  80 struct kthread self;
  •  81 int ret;
  •  82
  •  83 self.should_stop = 0;
  •  84 self.data = data;
  •  85 init_completion(&self.exited);
  •  86 current->vfork_done = &self.exited;
  •  87
  •  88 /* OK, tell user we're spawned, wait for stop or wakeup */
  •     /*这里需要注意:创建的新的内核线程被置为TASK_UNINTERRUPTIBLE,需要显示的被唤醒才能运行*/
  •  89 __set_current_state(TASK_UNINTERRUPTIBLE);
  •  90 create->result = current;
  •  91 complete(&create->done); /*这里显示的唤醒在完成量上等待的进程,然后发生调度,唤醒的是调用kthread_create函数的进程*/
  •  92 schedule();
  •  93
  •  94 ret = -EINTR;
  •  95 if (!self.should_stop)
  •  96 ret = threadfn(data);
  •  97
  •  98 /* we can't just return, we must preserve "self" on stack */
  •  99 do_exit(ret);
  • 100 }
  • 到此为止,一共出现了这么几个进程(线程):(本文中进程和线程不做区分)
    (1)调用kthread_create的进程,假设为进程A
    (2)kthreadd进程
    (3)新创建的进程,假设为进程B
    整个执行流程为:
    A调用kthread_create, 然后在kthread_create中唤醒kthreadd进程,A进程自己在完成量上睡眠,等待kthreadd进程创建过程完成,当kthreadd最后创建完新进程B,即kthread函数中,再去唤醒睡眠的进程A,如果B进程想要运行,还要显示的对他唤醒。

    二 区别总结
    上面分析了kthread_create和kernel_thread的代码的不同部分,其中也提到了几点不同,现在总结一下:
    (1)最重要的不同:kthread_create创建的内核线程有干净的上那上下文环境,适合于驱动模块或用户空间的程序创建内核线程使用,不会把某些内核信息暴露给用户程序
    (2)二者创建的进程的父进程不同: kthread_create创建的进程的父进程被指定为kthreadd, 而kernel_thread创建的进程可以是init或其他内核线程。

    相关文章:

    • 2021-08-19
    • 2021-06-07
    • 2021-12-16
    • 2022-02-06
    • 2021-09-20
    • 2021-09-20
    • 2021-07-07
    • 2022-02-19
    猜你喜欢
    • 2022-12-23
    • 2022-12-23
    • 2021-12-25
    • 2022-12-23
    • 2022-12-23
    • 2021-04-05
    • 2021-09-21
    相关资源
    相似解决方案