创建内核线程常见用于两种用途:
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()真正结束后, 才会唤醒这个进程。