内核线程是直接由内核本身启动的进程。内核线程实际上是将内核函数委托给独立的进程,与系统中其他进程“并行”执行(实际上,也并行于内核自身的执行),内核线程经常被称为内核“守护进程”。它们主要用于执行下列任务:

l  周期性地将修改的内存页与页来源块设备同步。

l  如果内存页很少使用,则写入交换区。

l  管理延时动作

l  实现文件系统的事务日志。

 

内核线程主要有两种类型:

1. 线程启动后一直等待,直至内核请求线程执行某一特定操作。

2. 线程启动后按周期性间隔运行,检测特定资源的使用,在用量超出或低于预置的限制时采取行动。

 

内核线程由内核自身生成,其特点在于:

1. 它们在CPU的管态执行,而不是用户态。

2. 它们只可以访问虚拟地址空间的内核部分(高于TASK_SIZE的所有地址),但不能访问用户空间。

 

task_struct进程描述符中包含两个跟进程地址空间相关的字段mm, active_mm,对于普通用户进程来说,mm指向虚拟地址空间的用户空间部分,而对于内核线程,mm为NULL。

 

active_mm主要用于优化,由于内核线程不与任何特定的用户层进程相关,内核并不需要倒换虚拟地址空间的用户层部分,保留旧设置即可。由于内核线程之前可能是任何用户层进程在执行,故用户空间部分的内容本质上是随机的,内核线程决不能修改其内容,故将mm设置为NULL,同时如果切换出去的是用户进程,内核将原来进程的mm存放在新内核线程的active_mm中。假如内核线程之后运行的进程与之前是同一个,内核并不需要修改用户空间地址表,TLB中 信息仍然有效;只有在内核线程之后执行的进程与此前用户层进程不同时,才需要切换,并清除对应TLB数据。

 

内核线程可以通过两种方式实现:

1. 将一个函数传递给kernel_thread,该函数接下来负责帮助内核调用daemonize已转换为守护进程,具体包括下列操作:

l  该函数释放其父进程的所有资源,不然这些资源会一直锁定直到线程结束。

l  阻塞信号的接收。

l  将init用作守护进程的父进程。

2. 创建内核更常用的方法是辅助函数kthread_create,该函数创建一个新的内核线程。最初线程是停止的,需要使用wake_up_process启动它。或使用kthread_run,与kthread_create不同的是,其创建新线程后立即唤醒它。

 

通过ps fax命令查看系统中运行的内核线程信息。


###################################################################################################################


Linux内核线程之深入浅出



【摘要】本文首先介绍了进程和线程的区别,接着分析了内核线程、轻量级LWP线程以及常见的用户线程的特点,同时介绍了内核线程和进程的区别。分析了创建内核线程kernel_thread函数的实现过程,介绍了一个在驱动中使用内核线程的实例。最后针对内核线程创建销毁的特点,给出了通用的内核线程操作函数API,使用该API可在自己的驱动或内核代码中方便的使用内核线程。

 

【关键词】线程,进程,内核线程,用户线程,LWPkernel_threaddaemonizekill_proc allow_signalSIGKILL

 

 

1      线程和进程的差别

在现代操作系统中,进程支持多线程。进程是资源管理及分配的最小单元;而线程是程序执行的最小单元。一个进程的组成实体可以分为两大部分:线程集和资源集。进程中的线程是动态的对象,代表了进程指令的执行过程。资源,包括地址空间、打开的文件、用户信息等等,由进程内的线程共享。线程有自己的私有数据:程序计数器,栈空间以及寄存器。

 

现实中有很多需要并发处理的任务,如数据库的服务器端、网络服务器、大容量计算等。传统的UNIX进程是单线程的,单线程意味着程序必须是顺序执行,不能并发,即在一个时刻只能运行在一个处理器上,因此不能充分利用多处理器框架的计算机

 

如果采用多进程的方法,则有如下问题:

?      fork一个子进程的消耗是很大的,fork是一个昂贵的系统调用。

?      各个进程拥有自己独立的地址空间,进程间的协作需要复杂的IPC技术,如消息传递和共享内存等。

 

线程推广了进程的概念,使一个进程可以包含多个活动(或者说执行序列等等)。多线程的优点和缺点实际上是对立统一的。使用线程的优点在于:

?      改进程序的实时响应能力;

?      更有效的使用多处理器,真正的并行(parallelism)

?      改进程序结构,具备多个控制流;

?      通讯方便,由于共享进程的代码和全局数据;

?      减少对系统资源的使用。对属于同一个进程的线程之间进行调度切换时不需要调用系统调用,因此将减少额外的消耗,往往一个进程可以启动上千个线程也没有什么问题。 

缺点在于:

由于各线程共享进程的地址空间,因此可能会导致竞争,因此对某一块有多个线程要访问的数据需要一些同步技术。

 

2      线程的分类

2.1     内核线程

Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求)。内核需要多个执行流并行,为了防止可能的阻塞,多线程化是必要的。内核线程就是内核的分身,一个分身可以处理一件特定事情。Linux内核使用内核线程来将内核分成几个功能模块,像kswapdkflushd等,这在处理异步事件如异步IO时特别有用。内核线程的使用是廉价的,唯一使用的资源就是内核栈和上下文切换时保存寄存器的空间。支持多线程的内核叫做多线程内核(Multi-Threads kernel )。内核线程的调度由内核负责,一个内核线程处于阻塞状态时不影响其他的内核线程,因为其是调度的基本单位。这与用户线程是不一样的。

 

2.2     轻量级进程

轻量级进程(LWP)是一种由内核支持的用户线程。它是基于内核线程的高级抽象,因此只有先支持内核线程,才能有LWP。每一个进程有一个或多个LWPs每个LWP由一个内核线程支持。这种模型实际上就是恐龙书上所提到的一对一线程模型。在这种实现的操作系统中,LWP就是用户线程。

 

由于每个LWP都与一个特定的内核线程关联,因此每个LWP都是一个独立的线程调度单元。即使有一个LWP在系统调用中阻塞,也不会影响整个进程的执行。

 

轻量级进程具有局限性。首先,大多数LWP的操作,如建立、析构以及同步,都需要进行系统调用。系统调用的代价相对较高:需要在user modekernel mode中切换。其次,每个LWP都需要有一个内核线程支持,因此LWP要消耗内核资源(内核线程的栈空间)。因此一个系统不能支持大量的LWP

注:

LWP的术语是借自于SVR4/MPSolaris 2.x。将之称为轻量级进程的原因可能是:在内核线程的支持下,LWP是独立的调度单元,就像普通的进程一样。所以LWP的最大特点还是每个LWP都有一个内核线程支持。

 

2.3     用户线程

LWP虽然本质上属于用户线程,LWP线程库是建立在内核之上的,LWP的许多操作都要进行系统调用,因此效率不高。而这里的用户线程指的是指不需要内核支持而完全建立在用户空间的线程库,用户线程的建立、同步、销毁及调度完全在用户空间完成,不需要内核的参与帮助。因此这种线程的操作是极其快速的且低消耗的。

 

上图是最初的一个多对一用户线程模型,从中可以看出,进程中包含线程,用户线程在用户空间中实现,内核并没有直接对用户线程进程调度,内核的调度对象和传统进程一样,还是进程本身,内核并不知道用户线程的存在。

 

由于Linux内核没有轻量级进程(线程)的概念,因此不能独立的对用户线程进行调度,而是由一个线程运行库来组织线程的调度,其主要工作在于在各个线程的栈之间调度。如果一个进程中的某一个线程调用了一个阻塞的系统调用,整个进程就会被调度程序切换为等待状态,其他线程得不到运行的机会。因此UNIX使用了异步I/O机制。这种机制主要的缺点在于在一个进程中的多个线程的调度中无法发挥多处理器的优势(如上述的阻塞情况)。

 

3      初识内核线程

内核线程(thread)或叫守护进程(daemon),在操作系统中占据相当大的比例,当Linux操作系统启动以后,尤其是X window也启动以后,你可以用”ps -ef”命令查看系统中的进程,这时会发现很多以”d”结尾的进程名,确切说名称显示里面加 "[]"的,这些进程就是内核线程。系统的启动是从硬件->内核->用户态进程的,pid的分配是一个往前的循环的过程,所以随系统启动的内核线程的pid往往很小。

 

?      PID TTY STAT TIME COMMAND

?      1 ? S 0:01 init [3]

?      3 ? SN 0:00 [ksoftirqd/0]

?      5 ? SN 0:00 [ksoftirqd/1]

?      6 ? S< 0:00 [events/0]

?      7 ? S< 0:00 [events/1]

?      8 ? S< 0:00 [khelper]

?      9 ? S< 0:00 [kblockd/0]

?      10 ? S< 0:00 [kblockd/1]

?      11 ? S 0:00 [khubd]

?      35 ? S 0:42 [pdflush]

?      36 ? S 0:02 [pdflush]

?      38 ? S< 0:00 [aio/0]

?      39 ? S< 0:00 [aio/1]

?      37 ? S 0:19 [kswapd0]

?      112 ? S 0:00 [kseriod]

?      177 ? S 0:00 [scsi_eh_0]

?      178 ? S 0:00 [ahc_dv_0]

?      188 ? S 0:00 [scsi_eh_1]

?      189 ? S 0:00 [ahc_dv_1]

?      196 ? S 2:31 [kjournald]

?      1277 ? S 0:00 [kjournald]

?      1745 ? Ss 0:02 syslogd -m 0

?      1749 ? Ss 0:00 klogd -x

?      1958 ? Ss 0:13 /usr/sbin/sshd

?      2060 ? Ss 0:00 crond

?      2135 tty2 Ss+ 0:00 /sbin/mingetty tty2

?      2136 tty3 Ss+ 0:00 /sbin/mingetty tty3

?      2137 tty4 Ss+ 0:00 /sbin/mingetty tty4

?      2138 tty5 Ss+ 0:00 /sbin/mingetty tty5

?      2139 tty6 Ss+ 0:00 /sbin/mingetty tty6

?      23564 ? S 0:00 bash

?      25605 ? Ss 0:00 sshd: peter [priv]

?      25607 ? S 0:00 sshd: peter@pts/2

 

3.1     内核线程

?      events 处理内核事件 很多软硬件事件(比如断电,文件变更)被转换为events,并分发给对相应事件感兴趣的线程进行响应

?      ksoftirqd 处理软中断 硬件中断处理往往需要关中断,而这个时间不能太长,否则会丢失新的中断。所以中断处理的很大一部分工作移出,转给任劳任怨的ksoftirqd在中断之外进行处理。比如一个网络包,从网卡里面取出这个过程可能需要关中断,但是TCP/IP协议处理就不必关中断了

?      kblockd 管理磁盘块读写

?      kjournald Ext3文件系统的日志管理 通常每个 _mount_  Ext3分区会有一个 kjournald看管,各分区的日志是独立

?      pdflush dirty内存页面的回写 太多dirty的页面意味着风险,比如故障时候的内容丢失,以及对突发的大量物理内存请求的响应(大量回写会导致糟糕的响应时间)

?      kswapd 内存回收 确保系统空闲物理内存的数量在一个合适的范围

?      aio 代替用户进程管理io 用以支持用户态的AIO

3.2     用户进程

?      crond 执行定时任务

?      init 为内核创建的第一个线程。引导用户空间服务,管理孤儿线程,以及运行级别的转换

?      mingetty 等待用户从tty登录

?      bash shell进程,一个命令行形式的系统接口;接受用户的命令,并进行解释、执

?      sshd ssh登录、文件传输、命令执行 等操作的服务进程

?      klogd 从内核信息缓冲区获取打印信息。内核在发现异常的时候,往往会输出一些消息给用户,这个对于故障处理很有用

?      syslogd 系统日志进程

?      udevd 支持用户态设备操作 (userspace device)

4      内核线程与用户进程的关系

内核线程也可以叫内核任务,例如,磁盘高速缓存的刷新,网络连接的维护,页面的换入换出等等。在Linux中,内核线程与普通进程有一些本质的区别,从以下几个方面可以看出二者之间的差异:

?      内核线程能够访问内核中数据,调用内核函数,而普通进程只有通过系统调用才能执行内核中的函数;

?      内核线程只运行在内核态,而普通进程既可以运行在用户态,也可以运行在内核态;

?      因为内核线程指只运行在内核态,因此,它只能使用大于PAGE_OFFSET3G的地址空间。另一方面,不管在用户态还是内核态,普通进程可以使用4GB的地址空间。

 

5      内核线程的创建

内核线程是由kernel_thread(  )函数在内核态下创建的:

linux+v2.6.19/arch/arm/kernel/process.c

 460 pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)

 461{

 462        struct pt_regs regs;

 463

 464        memset(&regs, 0, sizeof(regs));

 465

 466        regs.ARM_r1 = (unsigned long)arg;

 467        regs.ARM_r2 = (unsigned long)fn;

 468        regs.ARM_r3 = (unsigned long)do_exit;

 469        regs.ARM_pc = (unsigned long)kernel_thread_helper;

 470        regs.ARM_cpsr = SVC_MODE;

 471

 472        return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL,NULL);

 473}

 

Regs:内核栈中的一个地址,通过这个地址,可以找到内核栈CPU寄存器的初始化值。

线程的创建本质上就是建立了一个task_struct进程结构,这样内核调度时就会根据此结构的Regs信息运行。

 

建立新的内核线程时默认采用了如下标志:

CLONE_VM:置起此标志在进程间共享地址空间。因此内核线程和调用的进程(current)具备相同的进程空间,因为调用者运行在进程的内核态,所以进程在内核态时共享内核空间。

CLONE_UNTRACED:保证即使父进程正在被调试,内核线程也不会被调试。

其他调用时可用的标志如下: CLONE_FS置起此标志在进程间共享文件系统信息CLONE_FILES置起此标志在进程间共享打开的文件 CLONE_SIGHAND置起此标志在进程间共享信号处理程序

 

建立线程完毕后将其状态设置为TASK_RUNNING,然后放入运行队列,等待调度。kernel_thread的返回值同sys_fork,正数为新建线程的pid,否则为错误代码。kernel_thread.未返回时,新建线程已经退出,则返回值为0

 

新线程中执行fn函数,arg为传递给新建线程fn的参数,应确保fn未退出时arg可用。

 

如果父进程未得到子进程退出信息时已经退出,则内核线程将变为孤儿进程。新建内核线程的父进程是模块的加载者,或者确切的说是调用kernel_thread的进程。

 

kernel_thread_helper是一个汇编函数,代码如下:

 441/*

 442 * Shuffle the argument into the correct register before calling the

 443 * thread function.  r1 is the thread argument, r2 is the pointer to

 444 * the thread function, and r3 points to the exit function.

 445 */

 446extern void kernel_thread_helper(void);

 447asm(    ".section .text/n"

 448"       .align/n"

 449"       .type   kernel_thread_helper, #function/n"

 450"kernel_thread_helper:/n"

 451"       mov     r0, r1/n"

 452"       mov     lr, r3/n"

 453"       mov     pc, r2/n"

 454"       .size   kernel_thread_helper, . - kernel_thread_helper/n"

 455"       .previous");

Pc执行r2即待创建的线程执行体,arg参数r1赋值给r0,传递给r2的执行线程,lrARM的链接寄存器,当从r2返回时将赋给pc,即执行r3指向的do_exit,进行线程的清除工作。

所以内核线程是通过fn函数开始的。当结束时,执行系统调用do_exit。本质上相当于运行exit(fn(arg))

 

6      如何在驱动中使用内核线程

/* * file mythread.c */

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/init.h>

#include <linux/param.h>

#include <linux/jiffies.h>

#include <asm/system.h>

#include <asm/processor.h>

#include <asm/signal.h>

static pid_t thread_id;

static DECLARE_COMPLETION(exit_completion);

static atomic_t time_to_quit = ATOMIC_INIT(0);

 

int my_fuction(void *arg) {     

int ret;

daemonize("demo-thread");        

allow_signal(SIGKILL);

complete (&exit_completion);

      while(!signal_pending(current))        

     {                

                printk("jiffies is %lu/n", jiffies);

                set_current_stat(TASK_INTERRUPTIBLE);                                   

               schedule_timeout(10 * HZ);

if (atomic_read(&kthread->terminate))

{ /* we received a request to terminate ourself */ break; }

     }

/*

for(;;)

{

Ret = wait_event_interruptible(wq, condition); //可采用任何同步措施

}*/

  complete_and_exit(&exit_completion, 1); 

  return 0;

}

static int __init init(void)

{        

thread_id = kernel_thread(my_fuction, NULL, CLONE_FS | CLONE_FILES);

wait_for_completion(&exit_completion);

return 0;

}

static void __exit finish(void)

{     

atomic_inc(&time_to_quit); 

        kill_proc(thread_id, SIGKILL, 1); 

        wait_for_completion(&exit_completion);

         printk("Goodbye/n");

}

module_init(init);

module_exit(finish);

MODULE_LICENSE("GPL");

 

 

7      通用的内核线程模块

7.1     内核线程相关API

本驱动实例展示如何创建及停止一个内核线程。以模块形式加载,相关接口函数export到内核中,供其他模块使用。

 

内核版本 2.6.19,包含kthread.hkthread.c文件。

?      start_kthread

建立一个内核线程。可在任何进程上下文中执行,down()阻塞在同步信号量semaphore上直至新建立线程启动后执行init_kthread,其调用up()通知start_kthread新线程建立成功并运行。

 

?      stop_kthread

清除一个内核线程。可在除待清楚进程之外的任何进程上下文中执行。设置退出标志供所建线程检测并发送SIGKILL信号给该线程,阻塞,直至其调用exit_kthread通知killer其已经安全退出。

 

?      init_kthread

初始化新建立的线程环境,在新建线程循环外部执行。其将调用daemonize更新父进程为init进程便于回收资源,设置待捕捉的信号以便killer可以清除该线程。同时设置新线程的名称,清除退出标志,up()通知建立者新线程成功建立。

 

?      exit_kthread

收到中止信号后调用,退出。up()通知stop_kthread()其已经退出。

 

7.2     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>

/* 新建线程的所以相关信息 */

typedef struct kthread_struct {

/* private data */

/* New Linux task structure of thread ,新建线程的进程数据结构,由current获得*/

struct task_struct *thread;

/* 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.killer设置的退出标识 */

atomic_t  terminate;

/* additional data to pass to kernel thread 。传递给新建线程的附加信息,可选*/

void *arg;

} kthread_t;

/* 原型声明*/

/* 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

 

 

 

7.3     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"

/* 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 thedown 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;

kernel_thread((int (*)(void *))kthread->function, (void *)kthread, 0);

/* 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; }

 /* 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 */

atomic_inc(&kthread->terminate);

/* 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);

} /*

initialize new created thread. Called by the new thread. */

void init_kthread(kthread_t *kthread, char *name)

{

/* fill in thread structure */

kthread->thread = current; 

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)

/* reparent it to init and give name of “Kthread_example” to the new thread */

daemonize("Kthread_example");

#else

        daemonize();

        strcpy(current->comm, " Kthread_example");

#endif

/* set signal mask to what we want to respond */

allow_signal (SIGKILL| SIGINT| SIGTERM);

/* initialise wait queue */

init_waitqueue_head(&kthread->queue);

/* initialise termination flag */

atomic_set( & kthread->terminate, 0 );

/* 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 */

kthread->thread = NULL;

/* notify the stop_kthread() routine that we are terminating. */

up(&kthread->startstop_sem);



static int __init init(void)

{    

printk("Kernel thread Lib start!/n");

return 0;

}

static void __exit finish(void)

{             

printk("Kernel thread Lib Goodbye!/n");

}

 

module_init(init); module_exit(finish);

EXPORT_SYMBOL(start_kthread);

EXPORT_SYMBOL(stop_kthread);

EXPORT_SYMBOL(init_kthread);

EXPORT_SYMBOL(exit_kthread);

MODULE_LICENSE("GPL");

 

 

 

7.4     thread_drv.c

新建线程函数体为 example_thread()

执行loop (for(;;)),在内部等待监测事件或者信号,收到事件后执行正常任务,收到信号时检测退出标志。

 

#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); return(0); } /* remove the module */ void cleanup_module(void) { int i; /* terminate the kernel threads */ for (i=0; i<NTHREADS; i++) stop_kthread(&example); 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 (atomic_read(&kthread->terminate)) { /* we received a request to terminate ourself */ break; } /* Insert your own code here */ 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 */ }

 

 

 

7.5     Makefile

# set to your kernel tree KERNEL = XXXXX # get the Linux architecture. Needed to find proper include file for CFLAGS ARCH= XXXXX

# set cross compile tools

CROSS_COMPILE := XXXXX

 

#

# Include the make variables (CC, etc...)

#

LD           = $(CROSS_COMPILE)ld

CC           = $(CROSS_COMPILE)gcc

# set default flags to compile module CFLAGS = -D__KERNEL__ -DMODULE -I$(KERNEL)/include CFLAGS+= -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing # 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 

OBJS =XXXX.ko

 

all : $(OBJS)

$(OBJS) : thread_drv.o XXXX.o YYYY.ko

$(LD) -r $^ -o $@ clean: rm -f *.o

 

8      参数文献

内核线程的使用

tarius.wu,内核线程、轻量级进程、用户线程

唐宇,关于内核线程(kernel_thread)

Linux内核线程的解释,http://blog.sina.com.cn/s/blog_4980e953010003bb.html

Linux Kernel Threads in Device Drivers―― http://www.scs.ch/~frey/linux/kernelthreads.html

Sreekrishnan Venkateswaran Kernel Threadshttp://www.linux-mag.com/id/2195

How to kill a kernel thread?http://kerneltrap.org/node/6207

Can I start kernel threads from a kernel module?http://www.kasperd.net/%7Ekasperd/comp.os.linux.development.faq

Dinesh AhujaLinux Kernel Series: Linux Kernel Threads
Logo

更多推荐