内核线程和普通的进程间的区别在于内核线程没有独立的地址空间,它只在 内核空间运行,从来不切换到用户空间去;并且和普通进程一样,可以被调度,也可以被抢占。
一 线程的创建
[plain] view plain copy print ?
- struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, const char namefmt[], ...);
- 线程创建后,不会马上运行,而是需要将kthread_create() 返回的task_struct指针传给wake_up_process()才能驱动线程。
-
- 也可以使用kthread_run创建线程并启动线程
- struct task_struct *kthread_run(int (*threadfn)(void *data),void *data,const char *namefmt, ...);
-
- #define kthread_run(threadfn, data, namefmt, ...) \
- ({ \
- struct task_struct *__k \
- = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
- if (!IS_ERR(__k)) \
- wake_up_process(__k); \
- __k; \
- })
可见kthread_run是在调用了kthread_create后执行了wake_up_process.
在非内核线程中调用kernel_thread,必须在调用daemonize(...)来释放资源,成为真正的内核线程,kthread_create实际调用kernel_thread但是内部已经做了处理,不需要自己调用daemonize。
二 线程的退出
kthread_stop:设置线程的退出标记(线程函数内应用int kthread_should_stop(void)函数,当返回真时应退出函数),kthread_stop会一直等待至线程结束,线程结束前会发送完成结束给kthread_stop,如果直接使用do_exit直接退出线程那么kthread_stop不会收到完成信号将一直等待下去。如果线程已经退出那么kthread_stop会先设置退出标记再唤醒一下thread,唤醒线程后会判断退出标记因此设定的处理函数不会被调用。如果线程已经被唤醒并已经退出那么kthread_stop会一直等待。
[cpp] view plain copy print ?
- int kthread_stop(struct task_struct *thread);
- 如果处理函数没用kthread_should_stop判断退出,那么 kthread_stop会一直等待处理函数主动退出。
三 源码分析 这里使用的内核版本是2.6.21.5
3.1 管理调度其它的内核线程kthread
使用ps命令可以查看有个名叫kthread的进程,它在内核初始化的时候被创建。
[html] view plain copy print ?
- static __init int helper_init(void)
- {
- //创建一个单线程的共享列队
- helper_wq = create_singlethread_workqueue("kthread");
- BUG_ON(!helper_wq);
- return 0;
- }
- core_initcall(helper_init);
就是这个共享列队kthread_create会定义一个工作,在工作内创建创建具体的线程。
3.2 kthread_create创建线程
再看kthread_create前先看下kthread_create_info结构,每个线程创建时使用。
[cpp] view plain copy print ?
- struct kthread_create_info
- {
-
- int (*threadfn)(void *data);
- void *data;
- struct completion started;
-
-
- struct task_struct *result;
- struct completion done;
- struct work_struct work;
- };
[cpp] view plain copy print ?
-
-
-
-
-
-
-
-
-
-
- struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, const char namefmt[], ...)
- {
- struct kthread_create_info create;
-
-
- create.threadfn = threadfn;
- create.data = data;
- init_completion(&create.started);
- init_completion(&create.done);
- INIT_WORK(&create.work, keventd_create_kthread);
-
-
- if (!helper_wq)
- create.work.func(&create.work);
- else {
- queue_work(helper_wq, &create.work);
- wait_for_completion(&create.done);
- }
- if (!IS_ERR(create.result)) {
- va_list args;
- va_start(args, namefmt);
- vsnprintf(create.result->comm, sizeof(create.result->comm),
- namefmt, args);
- va_end(args);
- }
-
- return create.result;
- }
上面看到创建工作是在keventd_create_kthread函数里,那么看下keventd_create_kthread函数
[cpp] view plain copy print ?
-
- static void keventd_create_kthread(struct work_struct *work)
- {
- struct kthread_create_info *create =container_of(work, struct kthread_create_info, work);
- int pid;
-
-
-
- pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
-
- if (pid < 0) {
- create->result = ERR_PTR(pid);
- } else {
- wait_for_completion(&create->started);
- read_lock(&tasklist_lock);
- create->result = find_task_by_pid(pid);
- read_unlock(&tasklist_lock);
- }
- complete(&create->done);
- }
这时kthread_create在等待create->done信号,内核线程keventd在等待线程创建完create->started。上面创建了线程,处理函数为kthread
[cpp] view plain copy print ?
- static int kthread(void *_create)
- {
- struct kthread_create_info *create = _create;
- int (*threadfn)(void *data);
- void *data;
- sigset_t blocked;
- int ret = -EINTR;
-
- kthread_exit_files();
-
-
- threadfn = create->threadfn;
- data = create->data;
-
-
- sigfillset(&blocked);
- sigprocmask(SIG_BLOCK, &blocked, NULL);
- flush_signals(current);
-
-
- set_cpus_allowed(current, CPU_MASK_ALL);
-
-
- __set_current_state(TASK_INTERRUPTIBLE);
- complete(&create->started);
- schedule();
-
- if (!kthread_should_stop())
- ret = threadfn(data);
-
-
- if (kthread_should_stop()) {
- kthread_stop_info.err = ret;
- complete(&kthread_stop_info.done);
- }
- return 0;
- }
至此我们看到kthread_create是如何创建线程,和线程是如何工作的了
3.3 kthread_stop线程的停止
先看下停止相关的结构
[cpp] view plain copy print ?
- struct kthread_stop_info
- {
- struct task_struct *k;
- int err;
- struct completion done;
- };
-
-
- static DEFINE_MUTEX(kthread_stop_lock);
- static struct kthread_stop_info kthread_stop_info;
[cpp] view plain copy print ?
-
-
-
-
-
-
- int kthread_should_stop(void)
- {
- return (kthread_stop_info.k == current);
- }
这个函数在kthread_stop()被调用后返回真,当返回为真时你的处理函数要返回,返回值会通过kthread_stop()返回。所以你的处理函数应该有判断kthread_should_stop然后退出的代码。
[cpp] view plain copy print ?
-
-
-
-
-
-
-
-
-
-
-
-
-
- int kthread_stop(struct task_struct *k)
- {
- int ret;
- mutex_lock(&kthread_stop_lock);
-
- get_task_struct(k);
-
- init_completion(&kthread_stop_info.done);
- smp_wmb();
-
- kthread_stop_info.k = k;
- wake_up_process(k);
- put_task_struct(k);
-
- wait_for_completion(&kthread_stop_info.done);
- kthread_stop_info.k = NULL;
- ret = kthread_stop_info.err;
- mutex_unlock(&kthread_stop_lock);
- return ret;
- }
注意如果调用了kthread_stop你的处理函数不能调用do_exit(),函数返回你处理函数的返回值,如果创建的线程还没调用过wake_up_process()那么会返回-EINTR .
四 测试代码
[cpp] view plain copy print ?
- struct task_struct *mytask;
-
- int func(void* data)
- {
- while(1 )
- {
- if( kthread_should_stop()) return -1;
- printk(KERN_ALERT "func running\n");
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(1*HZ);
- }
- return 0;
- }
-
- 线程创建和驱动
- mytask=kthread_create(func,0,"mykthread");
- wake_up_process(mytask);
-
- 在需要结束的地方调用
- kthread_stop(mytask);
通过几个函数可以很容易的创建内核线程,但线程创建出来之后我们更关注的是有多线程带来的并发和竞争问题。并发的管理是操作系统编程的核心问题之一,引起的错误是一些最易出现又最难发现的问题.