内核线程的实例与摧毁

  创建内核线程常见用于两种用途:

      1)内核线程创建后便陷入睡眠, 直到有任务要执行。内核线程才会被唤醒, 执行完任务被再次睡眠.(比如说内核线程kevent)

      2)内核线程创建后, 进入周期式唤醒/睡眠。

  

 通常来说, 内核线程执行的是无尽循环(死循环)的工作。下面举例用于处理workqueue的内核线程Kevent。 

914 static int create_workqueue_thread(struct cpu_workqueue_struct *cwq, int cpu)
915 {
916         struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
917         struct workqueue_struct *wq = cwq->wq;
918         const char *fmt = is_wq_single_threaded(wq) ? "%s" : "%s/%d";
919         struct task_struct *p;
920 
921         p = kthread_create(worker_thread, cwq, fmt, wq->name, cpu);
922         /*
923          * Nobody can add the work_struct to this cwq,
924          *      if (caller is __create_workqueue)
925          *              nobody should see this wq
926          *      else // caller is CPU_UP_PREPARE
927          *              cpu is not on cpu_online_map
928          * so we can abort safely.
929          */
930         if (IS_ERR(p))
931                 return PTR_ERR(p);
932         if (cwq->wq->rt)
933                 sched_setscheduler_nocheck(p, SCHED_FIFO, ¶m);
934         cwq->thread = p;
935 
936         trace_workqueue_creation(cwq->thread, cpu);
937 
938         return 0;
939 }
  这个函数将会创建一个内核线程, 而该内核线程的任务就是执行worker_thread函数。

  

423 
424 static int worker_thread(void *__cwq)
425 {
426         struct cpu_workqueue_struct *cwq = __cwq;
427         DEFINE_WAIT(wait);
428 
429         if (cwq->wq->freezeable)
430                 set_freezable();
431 
432         for (;;) {
433                 prepare_to_wait(&cwq->more_work, &wait, TASK_INTERRUPTIBLE);
434                 if (!freezing(current) &&
435                     !kthread_should_stop() &&
436                     list_empty(&cwq->worklist))
437                         schedule();
438                 finish_wait(&cwq->more_work, &wait);
439 
440                 try_to_freeze();
441 
442                 if (kthread_should_stop())
443                         break;
444 
445                 run_workqueue(cwq);
446         }
447 
448         return 0;
449 }
450 
  只关注于这个函数的结构, 可以看到实际这个函数是会不停地睡眠,而且是死循环结构.  唯一退出这个死循环的条件是kthread_should_stop()

 51 int kthread_should_stop(void)
 52 {
 53         return to_kthread(current)->should_stop;
 54 }

 36 struct kthread {
 37         int should_stop;
 38         struct completion exited;
 39 };
 40 
 41 #define to_kthread(tsk) \
 42         container_of((tsk)->vfork_done, struct kthread, exited)
 43 
 44 /**

 从这两段语句可以看出来, 实际上是检测当前进程vfork_done域指向的完成变量所在的容器kthread的should_stop标志。

                                                


     之前创建内核线程的时候提到过的kthread()函数, 绑定了task_struct和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         self.should_stop = 0;
 66        
 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 }

   所以说只要我们设置了kthread->should_stop为1, 那么就可以中断内核线程程序的死循环, 使其返回到kthread()函数的执行流.

   而在内核中执行这个设置的函数便是

190 int kthread_stop(struct task_struct *k)
191 {
192         struct kthread *kthread;
193         int ret;
194 
195         trace_sched_kthread_stop(k);
196         get_task_struct(k);
197 
198         kthread = to_kthread(k);
199         barrier(); /* it might have exited */
200         if (k->vfork_done != NULL) {
201                 kthread->should_stop = 1;
202                 wake_up_process(k);
203                 wait_for_completion(&kthread->exited);
204         }
205         ret = k->exit_code;
206 
207         put_task_struct(k);
208         trace_sched_kthread_stop_ret(ret);
209 
210         return ret;
211 }
只要我们将内核线程的task_struct描述传递给kthread_stop(), 便会设置内核线程相关的kthread->should_stop为1.那么剩下的便是等待内核线程被唤醒,检测kernel_should_stop()后便会退出,返回到kthread()h函数的执行流中,执行最后的清理函数do_exit();

但是这里值得注意的是 调用kthread_stop()的进程会因为等待完成变量kthread->exited进入睡眠, 直到内核线程执行了do_exit()真正结束后, 才会唤醒这个进程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值