内核笔记:内核线程的创建

****  注:源码来自2.6.33内核 纯粹为个人笔记使用****
 一 创建内核线程的内核线程 kthreadd. 
         在内核初始化时,为了后期的内核线程的创建, 内核特地创建了一个新的特殊的内核线程来为后期的内核线程创建服务。
         init/main.c rest_init(){
            kthreadd_task  =  find_task_by_pid_ns ( pid , & init_pid_ns );
          ....
         } 
          这个内核线程将会执行kthreadd()函数, 而全局描述符kthreadd_task将用于记录这个内核线程的任务描述符.

    二  kthread_create_info  -- 创建内核线程信息描述符 
         顾名思义,这个描述符便是用于描述将要执行的内核进程的信息.
     struct kthread_create_info
      {
                int (*thread_fn)(void *data);
                void *data;
                struct task_struct *result;
                struct completion done;

                struct list_head list;
          }

 1)threadfn   创建的内核线程将要执行的函数
 2)data -- 内核线程执行thread_fn时将要执行的函数
 3)  thread -- 指向将要创建的内核线程的任务描述符
 4)  done -- 完成变量. 用于保证调用进程创建内核线程时的同步。
 5) list --  所有的kthread_create_info描述符都会连接到同一个list中
.                                          


  kernel/kthread.c  定义了一个通用的双向链表. 
  static LIST_HEAD(kthread_create_list); 所有的kthreat_create_info将会连接到这个list中。
 

三, 创建新内核线程的接口  --- struct task_struct *kthread_create(iint  (*threadnf)(void (*data)),
                                                      void *data,
                                                  const char namefmt[]
                                                  ....)
    
    这个接口主要做几件事:
      1)根据 新内核线程的需求来初始化一个kthread_create_info描述符
              struct kthread_create_info create; 
            create.threadfn = threadfn;
           create_data  = data;
           init_completion(&create.done);
     2)  将这个新的kthread_create_info描述符 链接到上面提到的kthread_create_list双向链表中
     3)  唤醒kthreadd_task指向的进程。 一开始就提到这个描述符指定kthreadd内核线程的。而该内核线程创建后便一直 处于睡眠状态。  Kthreadd内核线程一旦唤醒, 获得cpu的执行权后便会执行kthreadd()函数
    4) 调用进程等待完成变量create.done而进入睡眠状态。//这个睡眠状态知道后续调用的kthread()函数才得以解除。

 
214 int kthreadd(void *unused)
215 {
216         struct task_struct *tsk = current;
217 
218         /* Setup a clean context for our children to inherit. */
219         set_task_comm(tsk, "kthreadd");
220         ignore_signals(tsk);
221         set_cpus_allowed_ptr(tsk, cpu_all_mask);
222         set_mems_allowed(node_states[N_HIGH_MEMORY]);
223 
224         current->flags |= PF_NOFREEZE | PF_FREEZER_NOSIG;
225 
226         for (;;) {
227                 set_current_state(TASK_INTERRUPTIBLE);
228                 if (list_empty(&kthread_create_list))
229                         schedule();
230                 __set_current_state(TASK_RUNNING);
231 
232                 spin_lock(&kthread_create_lock);
233                  while (!list_empty(&kthread_create_list)) {
234                         struct kthread_create_info *create;
235 
236                         create = list_entry(kthread_create_list.next,
237                                             struct kthread_create_info, list);
238                         list_del_init(&create->list);
239                         spin_unlock(&kthread_create_lock);
240 
241                          create_kthread(create);
242 
243                         spin_lock(&kthread_create_lock);
244                 }
245                 spin_unlock(&kthread_create_lock);
246         }
247 
248         return 0;
249 }
250 

   kthreadd内核线程一开始先检测kthread_create_list有没有需要内核线程需要创建. 
   如果没有要创建的内核线程, 那么kthreadd内核线程就 直接进入睡眠状态。等待有下一个执行kthread_create()的进程把它唤醒。
   如果有要创建的内核线程, 这kthreadd遍历所有的kthread_create_list中的kthread_create_info信息, 逐一移除kthread_create_info描述符,并传递给create_kthread(state)函数作为参数。

 83 
 84 static void create_kthread(struct kthread_create_info *create)
 85 {
 86         int pid;
 87 
 88         /* We want our own signal handler (we take no signals by default). */
 89         pid =  kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
 90         if (pid < 0) {
 91                 create->result = ERR_PTR(pid);
 92                 complete(&create->done);
 93         }
 94 }
 95 
265 int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
266 {
267         struct pt_regs regs;
268 
269         memset(&regs, 0, sizeof(regs));
270 
271          regs.si = (unsigned long) fn;
272          regs.di = (unsigned long) arg;
273 
274 #ifdef CONFIG_X86_32
275         regs.ds = __USER_DS;
276         regs.es = __USER_DS;
277         regs.fs = __KERNEL_PERCPU;
278         regs.gs = __KERNEL_STACK_CANARY;
279 #else
280         regs.ss = __KERNEL_DS;
281 #endif
282 
283         regs.orig_ax = -1;
284          regs.ip = (unsigned long) kernel_thread_helper;
285         regs.cs = __KERNEL_CS | get_kernel_rpl();
286         regs.flags = X86_EFLAGS_IF | 0x2;
287 
288         /* Ok, create the new process.. */
289         return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
290 }
 
  x86中, kernel_thread的主要作用就是创建内核线程的进程描述符和执行环境(execute eviroment, execute context.执行上下文.)简单点来说就是设置cpu的寄存器信息。 

 特别关键的一点是 regs.si = (unsigned long) fn;  //注意在不同的内核版本中,这两个参数可能存放于不同的寄存器
                              regs.di = (unsigned long) arg; //但是ip一般都是指向
                              regs.ip  = (unsigned long) kernel_thread_helper;
  在x86架构中, cpu将会读取并执行ip(instruction pointer)指向的指令. 因此当这个内核线程开始执行时,它实际执行的函数是kernel_thread_helper(). kernel_thread_helper()很重要的一点就是它会调用传递的fn函数,而arg作为fn函数的参数。通过create_kthread()可知, 传递的fn参数政治kthread,而arg参数就是kthread_create_info描述符。  
 因此新的内核内核线程在开始执行时它会调用kthread()函数.  
 
   57 static int kthread(void *_create)
 58 {
 59         /* Copy data: it's on kthread's stack */
 60         struct kthread_create_info *create = _create;
 61         int (*threadfn)(void *data) = create->threadfn;
 62         void *data = create->data;
 63         struct kthread self;
 64         int ret;
 65 
 66         self.should_stop = 0;
 67         init_completion(&self.exited);
 68         current->vfork_done = &self.exited; //这里也是一个完成变量 在内核线程退出时会用到.
 69 
 70         /* OK, tell user we're spawned, wait for stop or wakeup */
 71         __set_current_state(TASK_UNINTERRUPTIBLE);
 72         create->result = current;
 73          complete(&create->done);              //唤醒原先一直睡眠的调用进程
 74          schedule();                                     //这里说明了 新建的内核线程唤醒了调用进程便进入了睡眠状态。
 75                                                                //因此调用进程还要手动唤醒内核线程.
 76         ret = -EINTR;
 77         if (!self.should_stop)
 78                  ret = threadfn(data);              //内核线程开始真正执行预定义的函数.
 79 
 80         /* we can't just return, we must preserve "self" on stack */
 81          do_exit(ret);                                  //内核线程完成使命.一定要返回到这里退出。
 82 }

 这里的重点是1) 新建的内核线程创建后必将进入不可中断的睡眠。因而创建者需要自己唤醒内核线程.
                      2)前面在kthread_create()提到过, 调用进程将会执行wait_for_completion(create->done).如果熟悉complete原理的话,你就知道调用进程执行这个语句后陷入了无尽的睡眠。 唯一可以唤醒这个进程的方法,就是完成相关的kthread_create_info相关的完成变量->done.也就是执行complete(create->done).  因此执行完wait_for_completion(), 原先一直睡眠的调用进程得以恢复执行。对于该调用进程而言,看起来就好像还是执行的操作是无缝的,一旦执行kthread_create()就可以获得所需要的内核线程。
                      3)当内核线程退出时,必须回到kthread()执行退出清理工作。 
 

调用链: kthread_create()--->kthreadd()--->create_kthread()---->kernel_thread_helper()---->kthread()--->预定义的函数.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值