Purpose
This examples shows how to create and stop a kernel thread.
The driver is implemented as a loadable module. In the init_module() routine five kernel threads are created. This kernel threads sleep one second, wake up, print a message and fall asleep again. On unload of the module (cleanup_module), the kernel threads are killed.
The example has been tested with Linux kernel 2.4.2 on Intel (uni processor only) and Alpha platform (COMPAQ Personal Workstation 500au (uni processor), DS20 and ES40 (SMP).
A version for the 2.2 kernel can be found here. Note: depending on the context of the creator of the threads the new threads may inherit properties from the parent you do not want to have. The new version avoids this by having keventd
create the threads. The 2.2. kernel do not have a keventd
, so this approach is not implementable there.
Functions in example
start_kthread
: creates a new kernel thread. Can be called from any process context but not from interrupt. The functions blocks until the thread started.
stop_kthread
: stop the thread. Can be called from any process context but the thread to be terminated. Cannot be called from interrupt context. The function blocks until the thread terminated.
init_kthread
: sets the environment of the new threads. Is to be called out of the created thread.
exit_kthread
: needs to be called by the thread to be terminated on exit
Creation of new Thread
A new thread is created with
kernel_thread()
. The thread inherits properties from its parents. To make sure that we do not get any weired properties, we let
keventd
create the new thread.
The new thread is created with
start_kthread()
. It uses a semaphore to block until the new thread is running. A
down()
blocks the
start_kthread()
routine until the corresponding
up()
call in
init_kthread()
is executed.
The new thread must call
init_kthread()
in order to let the creator continue.
Stop of new Thread
stop_kthread()
sets a flag that the thread uses to determine whether do die or not and sends a SIGKILL to the thread. This signal causes the thread to be woken up. On wakeup it will check for the flag and then terminate itself by calling
exit_kthread
and returning from the thread function. With a semaphore the
stop_kthread()
function blocks until the thread terminated.
Initialization of new Thread
Within the new created thread,
init_kthread()
needs to be called. This function sets a signal mask, initialises a wait queue, the termination flag and sets a new name for the thread. With a
up()
call it notifies the creator that the setup is done.
Exit of new Thread
When the thread receives the notification to terminate itself, is calls the
exit_kthread()
function. It notifies the
stop_kthread()
function that it terminated with an
up()
call.
The new Thread itself
The new thread is implemented in the
example_thread()
function. It runs an endless loop (
for(;;)
). In the loop it falls asleep with the
interruptible_sleep_on_timeout()
function. It comes out of this function either when the timeout expires or when a signal got caught.
The "work" in the thread is to print out a message with printk.
Kernel Versions
The example has been tested on 2.4.2.
Example Device Driver Code
The example consists of four files: kthread.h, kthread.c, thread_drv.c and a Makefile
kthread.h
#ifndef _KTHREAD_H
#define _KTHREAD_H
#include <linux/config.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/tqueue.h>
#include <linux/wait.h>
#include <asm/unistd.h>
#include <asm/semaphore.h>
/* a structure to store all information we need
for our thread */
typedef struct kthread_struct
{
/* private data */
/* Linux task structure of thread */
struct task_struct *thread;
/* Task queue need to launch thread */
struct tq_struct tq;
/* function to be started as thread */
void (*function) (struct kthread_struct *kthread);
/* semaphore needed on start and creation of thread. */
struct semaphore startstop_sem;
/* public data */
/* queue thread is waiting on. Gets initialized by
init_kthread, can be used by thread itself.
*/
wait_queue_head_t queue;
/* flag to tell thread whether to die or not.
When the thread receives a signal, it must check
the value of terminate and call exit_kthread and terminate
if set.
*/
int terminate;
/* additional data to pass to kernel thread */
void *arg;
} kthread_t;
/* prototypes */
/* start new kthread (called by creator) */
void start_kthread(void (*func)(kthread_t *), kthread_t *kthread);
/* stop a running thread (called by "killer") */
void stop_kthread(kthread_t *kthread);
/* setup thread environment (called by new thread) */
void init_kthread(kthread_t *kthread, char *name);
/* cleanup thread environment (called by thread upon receiving termination signal) */
void exit_kthread(kthread_t *kthread);
#endif
kthread.c
#include <linux/config.h>
#include <linux/version.h>
#if defined(MODVERSIONS)
#include <linux/modversions.h>
#endif
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/tqueue.h>
#include <linux/wait.h>
#include <linux/signal.h>
#include <asm/semaphore.h>
#include <asm/smplock.h>
#include "kthread.h"
/* private functions */
static void kthread_launcher(void *data)
{
kthread_t *kthread = data;
kernel_thread((int (*)(void *))kthread->function, (void *)kthread, 0);
}
/* public functions */
/* create a new kernel thread. Called by the creator. */
void start_kthread(void (*func)(kthread_t *), kthread_t *kthread)
{
/* initialize the semaphore:
we start with the semaphore locked. The new kernel
thread will setup its stuff and unlock it. This
control flow (the one that creates the thread) blocks
in the down operation below until the thread has reached
the up() operation.
*/
init_MUTEX_LOCKED(&kthread->startstop_sem);
/* store the function to be executed in the data passed to
the launcher */
kthread->function=func;
/* create the new thread my running a task through keventd */
/* initialize the task queue structure */
kthread->tq.sync = 0;
INIT_LIST_HEAD(&kthread->tq.list);
kthread->tq.routine = kthread_launcher;
kthread->tq.data = kthread;
/* and schedule it for execution */
schedule_task(&kthread->tq);
/* wait till it has reached the setup_thread routine */
down(&kthread->startstop_sem);
}
/* stop a kernel thread. Called by the removing instance */
void stop_kthread(kthread_t *kthread)
{
if (kthread->thread == NULL)
{
printk("stop_kthread: killing non existing thread!\n");
return;
}
/* this function needs to be protected with the big
kernel lock (lock_kernel()). The lock must be
grabbed before changing the terminate
flag and released after the down() call. */
lock_kernel();
/* initialize the semaphore. We lock it here, the
leave_thread call of the thread to be terminated
will unlock it. As soon as we see the semaphore
unlocked, we know that the thread has exited.
*/
init_MUTEX_LOCKED(&kthread->startstop_sem);
/* We need to do a memory barrier here to be sure that
the flags are visible on all CPUs.
*/
mb();
/* set flag to request thread termination */
kthread->terminate = 1;
/* We need to do a memory barrier here to be sure that
the flags are visible on all CPUs.
*/
mb();
kill_proc(kthread->thread->pid, SIGKILL, 1);
/* block till thread terminated */
down(&kthread->startstop_sem);
/* release the big kernel lock */
unlock_kernel();
/* now we are sure the thread is in zombie state. We
notify keventd to clean the process up.
*/
kill_proc(2, SIGCHLD, 1);
}
/* initialize new created thread. Called by the new thread. */
void init_kthread(kthread_t *kthread, char *name)
{
/* lock the kernel. A new kernel thread starts without
the big kernel lock, regardless of the lock state
of the creator (the lock level is *not* inheritated)
*/
lock_kernel();
/* fill in thread structure */
kthread->thread = current;
/* set signal mask to what we want to respond */
siginitsetinv(¤t->blocked, sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM));
/* initialise wait queue */
init_waitqueue_head(&kthread->queue);
/* initialise termination flag */
kthread->terminate = 0;
/* set name of this process (max 15 chars + 0 !) */
sprintf(current->comm, name);
/* let others run */
unlock_kernel();
/* tell the creator that we are ready and let him continue */
up(&kthread->startstop_sem);
}
/* cleanup of thread. Called by the exiting thread. */
void exit_kthread(kthread_t *kthread)
{
/* we are terminating */
/* lock the kernel, the exit will unlock it */
lock_kernel();
kthread->thread = NULL;
mb();
/* notify the stop_kthread() routine that we are terminating. */
up(&kthread->startstop_sem);
/* the kernel_thread that called clone() does a do_exit here. */
/* there is no race here between execution of the "killer" and real termination
of the thread (race window between up and do_exit), since both the
thread and the "killer" function are running with the kernel lock held.
The kernel lock will be freed after the thread exited, so the code
is really not executed anymore as soon as the unload functions gets
the kernel lock back.
The init process may not have made the cleanup of the process here,
but the cleanup can be done safely with the module unloaded.
*/
}
thread_drv.c
#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#if defined(MODVERSIONS)
#include <linux/modversions.h>
#endif
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include "kthread.h"
#define NTHREADS 5
/* the variable that contains the thread data */
kthread_t example[NTHREADS];
/* prototype for the example thread */
static void example_thread(kthread_t *kthread);
/* load the module */
int init_module(void)
{
int i;
/* create new kernel threads */
for (i=0; i <NTHREADS; i++)
start_kthread(example_thread, &example[i]);
return(0);
}
/* remove the module */
void cleanup_module(void)
{
int i;
/* terminate the kernel threads */
for (i=0; i<NTHREADS; i++)
stop_kthread(&example[i]);
return;
}
/* this is the thread function that we are executing */
static void example_thread(kthread_t *kthread)
{
/* setup the thread environment */
init_kthread(kthread, "example thread");
printk("hi, here is the kernel thread\n");
/* an endless loop in which we are doing our work */
for(;;)
{
/* fall asleep for one second */
interruptible_sleep_on_timeout(&kthread->queue, HZ);
/* We need to do a memory barrier here to be sure that
the flags are visible on all CPUs.
*/
mb();
/* here we are back from sleep, either due to the timeout
(one second), or because we caught a signal.
*/
if (kthread->terminate)
{
/* we received a request to terminate ourself */
break;
}
/* this is normal work to do */
printk("example thread: thread woke up\n");
}
/* here we go only in case of termination of the thread */
/* cleanup the thread, leave */
exit_kthread(kthread);
/* returning from the thread here calls the exit functions */
}
Makefile
# set to your kernel tree
KERNEL = /usr/src/linux
# get the Linux architecture. Needed to find proper include file for CFLAGS
ARCH=$(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
# set default flags to compile module
CFLAGS = -D__KERNEL__ -DMODULE -I$(KERNEL)/include
CFLAGS+= -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing
all: thread_mod.o
# get configuration of kernel
include $(KERNEL)/.config
# modify CFLAGS with architecture specific flags
include $(KERNEL)/arch/${ARCH}/Makefile
# enable the module versions, if configured in kernel source tree
ifdef CONFIG_MODVERSIONS
CFLAGS+= -DMODVERSIONS -include $(KERNEL)/include/linux/modversions.h
endif
# enable SMP, if configured in kernel source tree
ifdef CONFIG_SMP
CFLAGS+= -D__SMP__
endif
# note: we are compiling the driver object file and then linking
# we link it into the module. With just one object file as in
# this example this is not needed. We can just load the object
# file produced by gcc
# link the thread driver module
thread_mod.o: thread_drv.o kthread.o
ld -r -o thread_mod.o thread_drv.o kthread.o
# compile the kthread object file
kthread.o: kthread.c kthread.h
gcc $(CFLAGS) -c kthread.c
# compile the thread driver
thread_drv.o: thread_drv.c kthread.h
gcc $(CFLAGS) -c thread_drv.c
clean:
rm -f *.o
Bugs
The code assumes that
keventd
is running with PID 2.
Comments, Corrections
Please send comments, corrections etc. to the address below.
创建内核线程
1. 头文件
#include <linux/sched.h> //wake_up_process()
#include <linux/kthread.h> //kthread_create()、kthread_run()
#include <err.h> //IS_ERR()、PTR_ERR()
2. 实现
2.1创建线程
在模块初始化时,可以进行线程的创建。使用下面的函数和宏定义:
struct task_struct *kthread_create(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; \
})
例如:
static struct task_struct *test_task;
static int test_init_module(void)
{
int err;
test_task = kthread_create(test_thread, NULL, "test_task");
if(IS_ERR(test_task)){
printk("Unable to start kernel thread.\n");
err = PTR_ERR(test_task);
test_task = NULL;
return err;
}
wake_up_process(test_task);
return 0;
}
module_init(test_init_module);
2.2线程函数
在线程函数里,完成所需的业务逻辑工作。主要框架如下所示:
int threadfunc(void *data){
…
while(1){
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop()) break;
if(){//条件为真
//进行业务处理
}
else{//条件为假
//让出CPU运行其他线程,并在指定的时间内重新被调度
schedule_timeout(HZ);
}
}
…
return 0;
}
2.3结束线程
在模块卸载时,可以结束线程的运行。使用下面的函数:
int kthread_stop(struct task_struct *k);
例如:
static void test_cleanup_module(void)
{
if(test_task){
kthread_stop(test_task);
test_task = NULL;
}
}
module_exit(test_cleanup_module);
3. 注意事项
(1) 在调用kthread_stop函数时,线程函数不能已经运行结束。否则,kthread_stop函数会一直进行等待。
(2) 线程函数必须能让出CPU,以便能运行其他线程。同时线程函数也必须能重新被调度运行。在例子程序中,这是通过schedule_timeout()函数完成的。
4.性能测试
可以使用top命令来查看线程(包括内核线程)的CPU利用率。命令如下:
top –p 线程号
可以使用下面命令来查找线程号:
ps aux|grep 线程名
可以用下面的命令显示所有内核线程:
ps afx
注:线程名由kthread_create函数的第三个参数指定
在分析usb_hub_init()的代码的时候,忽略掉了一部份.
代码片段如下所示:
int usb_hub_init(void)
{
……
khubd_task = kthread_run(hub_thread, NULL, "khubd");
……
}
Kthread_run()是kernel中用来启动一个新kernel线程的接口,它所要执行的函数就是后面跟的第一个参数.在这里,也就是hub_thread().另外,顺带提一句,要终止kthread_run()创建的线程,可以调用kthread_stop().
说说内核线程
话说曾经我们在看serio.c的时候,遇到了这么一个模糊的面孔(说模糊是因为我们没对她过多深入):
serio_task = kthread_run(serio_thread, NULL, "kseriod");
虽然犹抱琵琶半遮面,但是从侧面仍可以看出,这"应该"搞了个类似"thread"的东东
我们走进点:
#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; \
})
哦,没什么大不了的嘛,就创建了一个内核线程并唤醒,恩,说对了!
其实说线程有点不妥,这个和我们平时所说的并和进程所区别的线程不一样,这里的线程本质上还是进程,不过linus说她是线程,你敢说啥?
我们具体来看看咋回事:
kthread.c
/**
* kthread_create - create a kthread.
* @threadfn: the function to run until signal_pending(current).
* @data: data ptr for @threadfn.
* @namefmt: printf-style name for the thread.
*
* Description: This helper function creates and names a kernel
* thread. The thread will be stopped: use wake_up_process() to start
* it. See also kthread_run(), kthread_create_on_cpu().
*
* When woken, the thread will run @threadfn() with @data as its
* argument. @threadfn() can either call do_exit() directly if it is a
* standalone thread for which noone will call kthread_stop(), or
* return when 'kthread_should_stop()' is true (which means
* kthread_stop() has been called). The return value should be zero
* or a negative error number; it will be passed to kthread_stop().
*
* Returns a task_struct or ERR_PTR(-ENOMEM).
*/
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);
spin_lock(&kthread_create_lock);
list_add_tail(&create.list, &kthread_create_list);
wake_up_process(kthreadd_task);
spin_unlock(&kthread_create_lock);
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;
}
首先,声明了一个struct kthread_create_info结构,这个结构包含了针对内核线程操作的所要用到的字段:
struct kthread_create_info
{
/* Information passed to kthread() from kthreadd. */
int (*threadfn)(void *data);
void *data;
struct completion started;
/* Result passed back to kthread_create() from kthreadd. */
struct task_struct *result;
struct completion done;
struct list_head list;
};
其中包含了线程要干的活,同步用到的结构,以及线程的描述符,比如说函数最后返回的create.result,就是创建的内核"线程"的进程描述符.
然后初始化threadfn以及他的函数参数,将这个kthread_create_info结构挂入全局的内核线程队列,并唤醒进程kthreadd_task,开始等待,等待......
kthreadd_task是个什么玩意?这是在本文件头部声明的一个全局进程描述符,
在main.c中的rest_init里初始化:
static void noinline __init_refok rest_init(void)
__releases(kernel_lock)
{
int pid;
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
numa_default_policy();
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
kthreadd_task = find_task_by_pid(pid);
unlock_kernel();
/*
* The boot idle thread must execute schedule()
* at least one to get things moving:
*/
preempt_enable_no_resched();
schedule();
preempt_disable();
/* Call into cpu_idle with preempt disabled */
cpu_idle();
}
我们看到里面kernel_thread函数返回一个pid,然后我们通过这个pid得到了这个kthreadd_task的描述符.
其实我们的kthread_create这个函数并没有直接创建什么内核线程,这里的kernel_thread这里才是真正创建内核线程的所在:
/*
* Create a kernel thread
*/
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
struct pt_regs regs;
memset(®s, 0, sizeof(regs));
regs.ebx = (unsigned long) fn;
regs.edx = (unsigned long) arg;
regs.xds = __USER_DS;
regs.xes = __USER_DS;
regs.xfs = __KERNEL_PERCPU;
regs.orig_eax = -1;
regs.eip = (unsigned long) kernel_thread_helper;
regs.xcs = __KERNEL_CS | get_kernel_rpl();
regs.eflags = X86_EFLAGS_IF | X86_EFLAGS_SF | X86_EFLAGS_PF | 0x2;
/* Ok, create the new process.. */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
}
do_fork(),一个伟大的函数,经过他的流程,一个新的"内核线程"诞生了...
我们来看看kthreadd_task到底都干了些什么:
int kthreadd(void *unused)
{
/* Setup a clean context for our children to inherit. */
kthreadd_setup();
current->flags |= PF_NOFREEZE;
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (list_empty(&kthread_create_list))
schedule();
__set_current_state(TASK_RUNNING);
spin_lock(&kthread_create_lock);
while (!list_empty(&kthread_create_list)) {
struct kthread_create_info *create;
create = list_entry(kthread_create_list.next,
struct kthread_create_info, list);
list_del_init(&create->list);
spin_unlock(&kthread_create_lock);
create_kthread(create);
spin_lock(&kthread_create_lock);
}
spin_unlock(&kthread_create_lock);
}
return 0;
}
写过驱动的哥们都知道,这个套路不陌生.整个一大循环,如果全局的kthread_create_list为空,我们这个内核线程就会一直沉睡,如果醒来发现还是空的怎么办?废话,当然是继续睡了!
当有一天这个线程醒了发现有事可做了,那么他就会从这个队列中取下一个他渴望的猎物,然后,然后,然后你说呢?
static void create_kthread(struct kthread_create_info *create)
{
int pid;
/* We want our own signal handler (we take no signals by default). */
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);
}
我们的内核线程是在这里创建的,要做什么呢?答案在传进来的struct kthread_create_info *create里.
然后我们对返回的pid检查,如果正常,那么我们就等待内核线程的对create->started的同步,来告诉父"线程",我们已经OK了.同样我们通过另一个同步信号create->done来告诉调用kthread_create的进程,你想要的东西已经准备好了!
static int kthread(void *_create)
{
struct kthread_create_info *create = _create;
int (*threadfn)(void *data);
void *data;
int ret = -EINTR;
/* Copy data: it's on kthread's stack */
threadfn = create->threadfn;
data = create->data;
/* OK, tell user we're spawned, wait for stop or wakeup */
__set_current_state(TASK_UNINTERRUPTIBLE);
complete(&create->started);
schedule();
if (!kthread_should_stop())
ret = threadfn(data);
/* It might have exited on its own, w/o kthread_stop. Check. */
if (kthread_should_stop()) {
kthread_stop_info.err = ret;
complete(&kthread_stop_info.done);
}
return 0;
}
我们在同步create->started后就让出了CPU,并等待唤醒.在我们的kthread_run里,我们不需要等待很长的时间,因为很快,我们就有
if (!IS_ERR(__k))
wake_up_process(__k);
当然,我们写驱动的时候可以根据自己的情况,比如创建一个内核线程以后我们要做一些别的操作比如增加计数什么的,我们就不用这个宏,然后自己来唤醒内核线程.