linux内核可以看做是一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求)。内核需要多个执行流并行,为了防止可能的阻塞,多线程化是必要的。内核线程就是内核的分身,一个分身可以处理一件特定的事情。Linux内核使用内核线程来将内核分成几个功能模块,像kswapd、kflushd等等,这在处理异步事件如异步IO时特别有用,像我们在内核中可以通过创建一个线程来查询按键状态。内核线程的使用是廉价的,唯一使用的资源就是内核栈和上下文切换时保存寄存器的空间。支持多线程的内核叫多线程内核(Multi-Threads kernel)。内核线程的调度由内核负责,一个内核线程处于阻塞状态时不影响其他的内核线程,因为其实调度的基本单位。
查看linux内核线程文件kthread.h可以发现内核线程有以下几个函数
1、创建内核线程
struct task_struct *kthread_create(int (*threadfn)(void *data),
void *data,
const char namefmt[], ...);
注意:通过kthread_create创建的内核线程不会立即运行,还需要调用wake_up_process唤醒线程,即是kthread_run函数
2、创建内核线程并运行
/**
* kthread_run - create and wake a thread.
* @threadfn: the function to run until signal_pending(current).
* @data: data ptr for @threadfn.
* @namefmt: printf-style name for the thread.
*
* Description: Convenient wrapper for kthread_create() followed by
* wake_up_process(). Returns the kthread or ERR_PTR(-ENOMEM).
*/
#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实现的
3、将内核线程绑定到某个CPU
/**
* kthread_bind - bind a just-created kthread to a cpu.
* @k: thread created by kthread_create().
* @cpu: cpu (might not be online, must be possible) for @k to run on.
*
* Description: This function is equivalent to set_cpus_allowed(),
* except that @cpu doesn't need to be online, and the thread must be
* stopped (i.e., just returned from kthread_create()).
*/
void kthread_bind(struct task_struct *k, unsigned int cpu)
{
BUG_ON(k->state != TASK_INTERRUPTIBLE);
/* Must have done schedule() in kthread() before we set_task_cpu */
wait_task_inactive(k);
set_task_cpu(k, cpu);
k->cpus_allowed = cpumask_of_cpu(cpu);
}
4、停止内核线程
/**
* kthread_stop - stop a thread created by kthread_create().
* @k: thread created by kthread_create().
*
* Sets kthread_should_stop() for @k to return true, wakes it, and
* waits for it to exit. Your threadfn() must not call do_exit()
* itself if you use this function! This can also be called after
* kthread_create() instead of calling wake_up_process(): the thread
* will exit without calling threadfn().
*
* Returns the result of threadfn(), or %-EINTR if wake_up_process()
* was never called.
*/
int kthread_stop(struct task_struct *k)
{
int ret;
mutex_lock(&kthread_stop_lock);
/* It could exit after stop_info.k set, but before wake_up_process. */
get_task_struct(k);
/* Must init completion *before* thread sees kthread_stop_info.k */
init_completion(&kthread_stop_info.done);
smp_wmb();
/* Now set kthread_should_stop() to true, and wake it up. */
kthread_stop_info.k = k;
wake_up_process(k);
put_task_struct(k);
/* Once it dies, reset stop ptr, gather result and we're done. */
wait_for_completion(&kthread_stop_info.done);
kthread_stop_info.k = NULL;
ret = kthread_stop_info.err;
mutex_unlock(&kthread_stop_lock);
return ret;
}
5、内核线程是否停止
/**
* kthread_should_stop - should this kthread return now?
*
* When someone calls kthread_stop() on your kthread, it will be woken
* and this will return true. You should then return, and your return
* value will be passed through to kthread_stop().
*/
int kthread_should_stop(void)
{
return (kthread_stop_info.k == current);
}
6、内核线程测试例子
#include <linux/kthread.h>
#include <linux/module.h>
static struct task_struct *MyThread = NULL;
static int MyPrintk(void *data)
{
char *mydata = kmalloc(strlen(data)+1, GFP_KERNEL);
memset(mydata, '\0', strlen(data)+1);
strncpy(mydata, data, strlen(data));
while (!kthread_should_stop()) {
printk("%s\n", mydata);
msleep(1000);
}
kfree(mydata);
return 0;
}
static int __init init_thread(void)
{
MyThread = kthread_run(MyPrintk, "hello world", "mythread");
return 0;
}
static void __exit exit_thread1(void)
{
if (MyThread) {
printk("stop MyThread\n");
kthread_stop(MyThread);
}
}
module_init(init_thread);
module_exit(exit_thread1);