GVKun编程网logo

Linux内核模块编程(linux内核模块编程 与应用层交互数据)

25

如果您想了解Linux内核模块编程的相关知识,那么本文是一篇不可错过的文章,我们将对linux内核模块编程与应用层交互数据进行全面详尽的解释,并且为您提供关于Linux内核模块介绍1.1.1Linux

如果您想了解Linux内核模块编程的相关知识,那么本文是一篇不可错过的文章,我们将对linux内核模块编程 与应用层交互数据进行全面详尽的解释,并且为您提供关于 Linux内核模块介绍1.1.1 Linux内核模块概述、c – 制作简单的linux内核模块、Linux 内核模块编程(三)--Kenel Model 测试、linux-kernel – 交叉编译Linux内核模块的有价值的信息。

本文目录一览:

Linux内核模块编程(linux内核模块编程 与应用层交互数据)

Linux内核模块编程(linux内核模块编程 与应用层交互数据)

Linux内核模块编程

一、Linux内核模块简介

  Linux内核整体结构非常庞大,其包含的组件也非常多。我们怎么把需要的部分都包含在内核中呢?
  一种办法是把所有的需要的功能都编译到内核中。这会导致两个问题,一是生成的内核会很大,二是如果我们要在现有的内核中新增或删除功能,不得不重新编译内核,工作效率会非常的低,同时如果编译的模块不是很完善,很有可能会造成内核崩溃。
  Linux提供了另一种机制来解决这个问题,这种集中被称为模块,可以实现编译出的内核本身并不含有所有功能,而在这些功能需要被使用的时候,其对应的代码可以被动态的加载到内核中。

二、模块特点:

  1)模块本身并不被编译入内核,从而控制了内核的大小。
  2)模块一旦被加载,他就和内核中的其他部分完全一样。
  注意:模块并不是驱动的必要形式:即:驱动不一定必须是模块,有些驱动是直接编译进内核的;同时模块也不全是驱动,例如我们写的一些很小的算法可以作为模块编译进内核,但它并不是驱动。就像烧饼不一定是圆的,圆的也不都是烧饼一样。

三、最简单的模块分析

1)以下是一个最简单的模块例子
#include <linuxinit.h>         /* printk() */  
#include <linuxmodule.h>       /* __init __exit */  

static int __init hello_init(void) { /*模块加载函数,通过insmod命令加载模块时,被自动执行*/
   printk(KERN_INFO " Hello World enter\n");
   return 0;
}
static void __exit hello_exit(void) { /*模块卸载函数,当通过rmmod命令卸载时,会被自动执行*/
   printk(KERN_INFO " Hello World exit\n ");
}

module_init (hello_init);
module_exit (hello_exit);

MODULE_AUTHOR("dengwei"); /*模块作者,可选*/
MODULE_LICENSE("Dual BSD/GPL"); /*模块许可证明,描述内核模块的许可权限,必须*/

MODULE_DESCRIPTION("A simple Hello World Module"); /*模块说明,可选*/
MODULE_ALIAS("a simplest module"); /*模块说明,可选*/

2) 以下是编译上述模块所需的编写的makefile
obj-m :=hello.o                     //目标文件  
#module-objs := file1.o file.o      //当模块有多个文件组成时,添加本句  
KDIR :=usrsrclinux               //内核路径,根据实际情况换成自己的内核路径,嵌入式的换成嵌入式,PC机的指定PC机路径  
PWD := $(shell pwd)                 //模块源文件路径  
all:      
    $(MAKE)  -C  $(KDIR)  SUBDIRS=$(PWD)  modules  
    @rm -rf *.mod.*  
    @rm -rf .*.cmd  
    @rm -rf *.o  
    @rm -rf Module.*  
clean:  
    rm -rf *.ko
最终会编译得到:hello.ko文件
使用insmodhello.ko将模块插入内核,然后使用dmesg即可看到输出提示信息。

常用的几种模块操作命令:
insmod XXX.ko    加载指定模块
lsmod                      列举当前系统中的所有模块
rmmod  XXX         卸载指定模块(注意没有.ko后缀)
dmesg                    当打印等级低于默认输出等级时,采用此命令查看系统日志

3)linux内核模块的程序结构
  1. 模块加载函数:
Linux内核模块一般以__init标示声明,典型的模块加载函数的形式如下:
static int __init myModule_init(void) {
   /* Module init code */
   PRINTK("myModule_init\n");
   return 0;
}
module_init (myModule_init);
模块加载函数的名字可以随便取,但必须以“module_init(函数名)”的形式被指定;
执行insmod命令时被执行,用于初始化模块所必需资源,比如内存空间、硬件设备等;
它返回整形值,若初始化成功,应返回0,初始化失败返回负数。
  1. 模块卸载函数
典型的模块卸载函数形式如下:
static void __exit myModule_exit(void) {
   /* Module exit code */
   PRINTK("myModule_exit\n");
   return;
}
module_exit (myModule_exit);
模块卸载函数在模块卸载的时候执行,不返回任何值,需用”module_exit(函数名)”的形式被指定。
卸载模块完成与加载函数相反的功能:
  若加载函数注册了XXX,则卸载函数应当注销XXX
  若加载函数申请了内存空间,则卸载函数应当释放相应的内存空间
  若加载函数申请了某些硬件资源(中断、DMA、I/0端口、I/O内存等),则卸载函数应当释放相应的硬件资源
  若加载函数开启了硬件,则卸载函数应当关闭硬件。
其中__init 、__exit 为系统提供的两种宏,表示其所修饰的函数在调用完成后会自动回收内存,即内核认为这种函数只会被执行1次,然后他所占用的资源就会被释放。
  1. 模块声明与描述
  在linux内核模块中,我们可以用MODULE_AUTHOR、MODULE_DESCRIPTION、MODULE_VERSION、MODULE_TABLE、MODULE_ALIA,分别描述模块的作者、描述、版本、设备表号、别名等。
MODULE_AUTHOR("dengwei");  
MODULE_LICENSE("Dual BSD/GPL");  
MODULE_DESCRIPTION("A simple Hello World Module");  
MODULE_ALIAS("a simplest module");
四、有关模块的其它特性
1)模块参数:
我们可以利用module_param(参数名、参数类型、参数读写属性) 为模块定义一个参数,例如:
static char *string_test = “this is a test”;  
static num_test = 1000;    
module_param (num_test,int,S_IRUGO);  
module_param (steing_test,charp,S_ITUGO);
  在装载模块时,用户可以给模块传递参数,形式为:”insmod 模块名 参数名=参数值”,如果不传递,则参数使用默认的参数值
  参数的类型可以是:byte,short,ushort,int,uint,long,ulong,charp,bool;
  权限:定义在linux/stat.h中,控制存取权限,S_IRUGO表示所有用户只读;
  模块被加载后,在sys/module/下会出现以此模块命名的目录,当读写权限为零时:表示此参数不存在sysfs文件系统下的文件节点,当读写权限不为零时:此模块的目录下会存在parameters目录,包含一系列以参数名命名的文件节点,这些文件节点的权限值就是传入module_param()的“参数读/写权限“,而该文件的内容为参数的值。
  除此之外,模块也可以拥有参数数组,形式为:”module_param_array(数组名、数组类型、数组长、参数读写权限等)”,当不需要保存实际的输入的数组元素的个数时,可以设置“数组长“为0。
  运行insmod时,使用逗号分隔输入的数组元素。
下面是一个实际的例子,来说明模块传参的过程。
#include <linuxmodule.h>    /*module_init()*/
#include <linuxkernel.h> /* printk() */
#include <linuxinit.h>       /* __init __exit */

#define DEBUG   //open debug message

#ifdef DEBUG
#define PRINTK(fmt, arg...)     printk(KERN_WARNING fmt, ##arg)
#else
#define PRINTK(fmt, arg...)     printk(KERN_DEBUG fmt, ##arg)
#endif

static char *string_test = "default paramater";
static int num_test = 1000;

static int __init hello_init(void) {
   PRINTK("\nthe  string_test is : %s\n", string_test);
   PRINTK("the  num_test is : %d\n", num_test);
   return 0;
}

static void __exit hello_exit(void) {
   PRINTK(" input paramater module exit\n ");
}

module_init(hello_init);
module_exit(hello_exit);

module_param(num_test, int, S_IRUGO);
module_param(string_test, charp, S_IRUGO);

MODULE_AUTHOR("dengwei");
MODULE_LICENSE("GPL");



当执行 insmod hello_param.ko时,执行dmesg 查看内核输出信息:
Hello World enter  
the test string is: this is a test  
the test num is :1000
当执行insmod  hello_param.ko num_test=2000 string_test=“edit by dengwei”,执行dmesg查看内核输出信息:
Hello World enter  
the test string is: edit by dengwei  
the test num is :2000
2)导出模块及符号的相互引用
Linux2.6内核的“/proc/kallsyms“文件对应内核符号表,它记录了符号以及符号所在的内存地址,模块可以使用下列宏导到内核符号表中。
EXPORT_SYMBOL(符号名);       任意模块均可
EXPORT_SYMBOL_GPL(符号名);   只使用于包含GPL许可权的模块
导出的符号可以被其它模块使用,使用前声明一下即可。
下面给出一个简单的例子:将add sub符号导出到内核符号表中,这样其它的模块就可以利用其中的函数
#include <linuxmodule.h>    /*module_init()*/
#include <linuxkernel.h> /* printk() */
#include <linuxinit.h>       /* __init __exit */

int add_test(int a, int b) {
   return a + b;
}

int sub_test(int a, int b) {
   return a - b;
}

EXPORT_SYMBOL (add_test);
EXPORT_SYMBOL (sub_test);

MODULE_AUTHOR("dengwei");
MODULE_LICENSE("GPL");

执行 cat/proc/kallsyms | grep test 即可找到以下信息,表示模块确实被加载到内核表中。

f88c9008 r __ksymtab_sub_integar        [export_symb]  
f88c9020 r __kstrtab_sub_integar         [export_symb]  
f88c9018 r __kcrctab_sub_integar         [export_symb]  
f88c9010 r __ksymtab_add_integar        [export_symb]  
f88c902c r __kstrtab_add_integar          [export_symb]  
f88c901c r __kcrctab_add_integar         [export_symb]  
f88c9000 T add_tes                [export_symb]  
f88c9004 T sub_tes                [export_symb]  
13db98c9 a __crc_sub_integar           [export_symb]  
e1626dee a __crc_add_integar           [export_symb]  

在其它模块中可以引用此符号
#include <linuxmodule.h>    /*module_init()*/
#include <linuxkernel.h> /* printk() */
#include <linuxinit.h>       /* __init __exit */

#define DEBUG   //open debug message

#ifdef DEBUG
#define PRINTK(fmt, arg...)     printk(KERN_WARNING fmt, ##arg)
#else
#define PRINTK(fmt, arg...)     printk(KERN_DEBUG fmt, ##arg)
#endif

extern int add_test(int a, int b);
extern int sub_test(int a, int b);

static int __init hello_init(void) {
   int a, b;

   a = add_test(10, 20);
   b = sub_test(30, 20);
   PRINTK("the add test result is %d", a);PRINTK("the sub test result is %d\n", b);
   return 0;
}

static void __exit hello_exit(void) {
   PRINTK(" Hello World exit\n ");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_AUTHOR("dengwei");
MODULE_LICENSE("GPL");

 Linux内核模块介绍1.1.1 Linux内核模块概述

Linux内核模块介绍1.1.1 Linux内核模块概述

嵌入式设备驱动开发中将驱动程序以模块的形式发布,更是极大地提高了设备使用的灵活性——用户只需要拿到相关驱动模块,再插入到用户的内核中,即可灵活地使用你的设备。

1.1.2 使用Linux模块的优点

1.        用户可以随时扩展Linux系统的功能。

2.        当要修改当前Linux系统的驱动时,只需要卸载旧模块,编译目标驱动模块,重新安装插入即可。

3.        系统中如果需要使用新模块,不必重新编译内核,只要插入相应的模块即可。

4.        减小Linux内核的体积,节省flash。

1.2 Linux模块入门1.2.1 模块相关命令1.2.1.1 Linux模块命令详细介绍

1.      模块安装命令:insmod

insmod xxxx.ko

2.      查看当前已经安装模块:lsmod

lsmod 不需要参数

3.      模块卸载命令:rmmod

rmmod xxxxx.ko

4.      查看模块信息:modinfo

 X86上操作:

[root@zhifachen linux-3.5]# modinfo/root/work/rootfs/home/mod/tiny4412_hello_module.ko

filename:      /root/work/rootfs/home/mod/tiny4412_hello_module.ko

license:        GPL

depends:        

intree:         Y

vermagic:      3.5.0-FriendlyARM SMP preempt mod_unload ARMv7 p2v8

[root@zhifachen linux-3.5]#

1.2.1.2 Linux模块命令测试示例

注意:模块的编译是依赖具体一份源码,并且这份被编译过,并且没有使用mrproper,distclean清除的源码工程。  

1.2.2 使用makemodules编译内核源码树中的模块

1.      添加内核菜单

2.      make menuconfig 配置为 M

3.      在顶层目录终端输入makemodules

直接make 也可生成模块文件(.ko),也会生成zImage,当你只想编译模块而不想编译zImage时候使用 make modules 命令会更加快速。

1.2.3 把内核源码树中模块源文件编译到内核映像中

如果不需要配置菜单只想编译代码到内核zImage,Makefile可以这样写obj-y += xxxx.o

如果不需要配置菜单只想编译代码为外部模块,Makefile可以这样写 obj-m+= xxxx.o

1.2.4 在内核源码中添加自定义的模块并且编译1.2.5 外部独立Makefile编译模块

在驱动开发阶段,接触到新驱动机率非常高,如果都要去修改内核源码Makefile, 太过于繁杂,也不利于移植,实际开发中会使用另外一种方法来编译模块文件。

1.2.5.1 Linux内核模块代码文件模板

可以直接复制内核源码中自带的示例模板来测试:drivers/char/tiny4412_hello_module.c

1.2.5.2 Linux内核模块编译Makefile模板

编译模块Makefile文件:

 

obj-m +=  xyd_hello_module.o

 

all:

 

      @make -C /root/work/linux-3.5/  M=$(PWD)    modules

 

      @rm -rf *.o *.mod.c modules.order Module.symvers  *~   *.bak

 

clean:

 

      @rm -rf *.o *.mod.c modules.order  Module.symvers *.ko  *~  *.bak

 

make -C $(KDIR)M=$(PWD) modules

M=选项让该Makefile在构造modules目标之前返回到模块源代码目录,然后modules目标指向obj-m变量中设定的模块;在上面的例子中,我们将该变量设置成了module.o。

上面的清除规则是自己写的,也可利用内核Makefile清除规则来清编译生成目标文件,改进后代码:

 

obj-m +=  xyd_hello_module.o

 

# x86 编译时候使用:  uname -r 得到当前内核的版本号  

 

#KDIR :=  /lib/modules/`uname -r`/build

 

#arm 编译时候使用

 

KDIR  :=/root/work/linux-3.5

 
 

all:

 

      @make -C  $(KDIR)   M=$(PWD)  modules

 
 

clean:

 

      @make -C  $(KDIR)   M=$(PWD)  modules  clean

 

      @rm -f *.ko.unsigned *~

 


1.1.1 Linux内核printk 函数

内核中不能使用printf函数输出信息,要使用printk函数,这个函数有输出等级控制的。

内核通过 printk() 输出的信息具有日志级别,日志级别是通过在 printk() 输出的字符串前加一个带尖括号的整数来控制的,如 printk("<6>Hello, world!/n");。内核中共提供了八种不同的日志级别,在 linux/kernel.h 中有相应的宏对应。

#defineKERN_EMERG    "<0>"    /* system is unusable  系统不可用*/

#defineKERN_ALERT     "<1>"    /* action must be takenimmediately警报,必须立即采取行动*/

#define KERN_CRIT        "<2>"    /*critical conditions临界状态   */

#defineKERN_ERR        "<3>"    /* error conditions  错误状态*/

#defineKERN_WARNING  "<4>"    /* warningconditions警告状态*/

#defineKERN_NOTICE    "<5>"    /* normal but significant正常的,但引人注目*/

#define KERN_INFO        "<6>"    /*informational  信息*/

#defineKERN_DEBUG     "<7>"    /* debug-level messages调试等级信息 */

可以通过 查看/proc/sys/kernel/printk 文件内容知道设置信息。

 

[root@ChenZhiFa/]# cat /proc/sys/kernel/printk

7       4       1      7

 

可以通过echo 命令修改种个等级:

[root@ChenZhiFa/]# echo  4  4 1  7  > /proc/sys/kernel/printk

 

说明:直接使用printk( “kkkk”); 这样没有指明等级,这种未明确指定。

 

只有等级比较当前终端等级高才会在终端上显示出来。

以下进行X86 系统上的测试(在纯字符界面下测试有效,在图形界面中是无效)

在图形界面系统中按 Ctl + Alt + F2 (F2~F6键其中一个,不同电脑可能不同),可以进入到纯字符界面,要返回按Ctl +Alt + F1(不同电脑可能不同)

printk   ---  不支持浮点数,实际上驱动程序也不支持浮点运算。所有浮点运算应该放在用户空间。

 

c – 制作简单的linux内核模块

c – 制作简单的linux内核模块

我正在尝试升级我的守护进程代码,以便它不是一个可执行文件,它将是一个内核模块,但即使使用一个简单的例子我也很困难.

这是我试过的代码:

#include <linux/module.h>
#include <linux/kernel.h>

int init_module(void){
   printk("Hello world 1.\n");
   return 0;
}

void cleanup_module(void){
  printk(KERN_ALERT "Goodbye world 1.\n");
}

因为我知道我不能直接编译代码,所以我采取了一种方法来复制并稍微修改makefile,如下所示:

WARN    := -W -Wall -Wstrict-prototypes -Wmissing-prototypes
INCLUDE := -isystem /lib/modules/`uname -r`/build/include
CFLAGS  := -O2 -DMODULE -D__KERNEL__ ${WARN} ${INCLUDE}
CC      := gcc
OBJS    := ${patsubst %.c,%.o,${wildcard *.c}}

all: ${OBJS}

然后在命令行上执行“make”,期望安装程序:

这是我的输出:

gcc -O2 -DMODULE -D__KERNEL__ -W -Wall -Wstrict-prototypes -Wmissing-prototypes -isystem /lib/modules/`uname -r`/build/include   -c -o d.o d.c
    In file included from /lib/modules/2.6.21.5-smp/build/include/asm/thread_info.h:16,from /lib/modules/2.6.21.5-smp/build/include/linux/thread_info.h:21,from /lib/modules/2.6.21.5-smp/build/include/linux/preempt.h:9,from /lib/modules/2.6.21.5-smp/build/include/linux/spinlock.h:49,from /lib/modules/2.6.21.5-smp/build/include/linux/module.h:9,from d.c:1:
    /lib/modules/2.6.21.5-smp/build/include/asm/processor.h:82: error: 'CONfig_X86_L1_CACHE_SHIFT' undeclared here (not in a function)
    /lib/modules/2.6.21.5-smp/build/include/asm/processor.h:82: error: requested alignment is not a constant
    In file included from /lib/modules/2.6.21.5-smp/build/include/linux/sched.h:51,from /lib/modules/2.6.21.5-smp/build/include/linux/utsname.h:35,from /lib/modules/2.6.21.5-smp/build/include/asm/elf.h:12,from /lib/modules/2.6.21.5-smp/build/include/linux/elf.h:7,from /lib/modules/2.6.21.5-smp/build/include/linux/module.h:15,from d.c:1:
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:33:3: error: #error You lose.
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    /lib/modules/2.6.21.5-smp/build/include/linux/jiffies.h:225:31: error: division by zero in #if
    In file included from /lib/modules/2.6.21.5-smp/build/include/linux/module.h:21,from d.c:1:
    /lib/modules/2.6.21.5-smp/build/include/asm/module.h:62:2: error: #error unkNown processor family
    make: *** [d.o] Error 1

当我只处理两个记录良好的函数时,为什么要得到所有这些错误?我的makefile关闭了吗?请指教?

解决方法

没关系.我发现自己得到了答案:

https://www.kernel.org/doc/Documentation/kbuild/modules.txt

它指出:

To build against the running kernel use:

    $make -C /lib/modules/`uname -r`/build M=$PWD

    Then to install the module(s) just built,add the target
"modules_install" to the command:

    $make -C /lib/modules/`uname -r`/build M=$PWD modules_install

Linux 内核模块编程(三)--Kenel Model 测试

Linux 内核模块编程(三)--Kenel Model 测试

Kenel Model 测试

  本内核模块测试主要是由三个子系统组成:用户态测试,内核态测试和接口测试。内核态测试通过编写内核模块代码,直接对avl原文件进行测试,可以执行所有的指令。出于运行安全的考虑,用户态的进程无法直接访问硬件和内核的内存空间,而是通过字符设备(ioctl)进入内核态调用,用户态测试时可以看到内核态通过printk函数的输出。接口测试用于检测内核态与用户态之间的交互点,重点是要检查数据的交换,传递和控制管理过程,以及相互逻辑依赖关系等。总体结构如下:

      

   avl的测试包括下图几个方面,以add为例,具体说明avl函数接口测试的实现。

                     

 

一、 内核态

  Module中首先需要创建的文件有:avlat-test.h,avlat-add.c,Makefile.in 。其中avlat-test.h是包含所有测试的头文件(仿造spl内核模块编写即可),avlat-add.c是测试文件,Makefile.in是辅助编译生成文件(4.2中已提到)。

  增加结点有三种情况:增加单个结点,增加多个结点,增加已存在结点。对应于这三种情况,可以设计三个测试用例。

    

1.增加单个结点

static int
avlat_add_test_1(struct file *file, void *arg)
{
      avl_tree_t *avl_test_tree;
      avlat_t *avlat_node, *first_node, *last_node;
      uint32_t avl_value=5, rc = 0;
 
      // step1: initial avl
      avl_test_tree = kmalloc(sizeof(*avl_test_tree), GFP_KERNEL);
      avl_create(avl_test_tree, avl_test_compare, sizeof (avlat_t), offsetof(avlat_t, avl_node));
      
      if (avl_numnodes(avl_test_tree) != 0)
      {
             printk("Error: avlat_add_test_1-1.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }       
      
      // step2: add sigle avl node and check avl_numnodes
      avlat_node = kmalloc(sizeof(*avlat_node), GFP_KERNEL);
      avlat_node->avl_key=avl_value;
      avl_add(avl_test_tree, avlat_node);
 
      if (avl_numnodes(avl_test_tree) != 1)
      {
             printk("Error: avlat_add_test_1-2.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }
      
      // step3: check avl_first & avl_last
      first_node = avl_first(avl_test_tree);
      last_node = avl_last(avl_test_tree);
      
      if ((first_node->avl_key != last_node->avl_key) || (first_node->avl_key != avl_value))
      {
             printk("Error: avlat_add_test_1-3.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }
 
      // step4: remove avl node
      avl_remove(avl_test_tree, avlat_node);
      
      if (avl_numnodes(avl_test_tree) != 0)
      {
             printk("Error: avlat_add_test_1-4.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }       
      kfree(avlat_node);
 
      // step5: destroy avl node
      avl_destroy(avl_test_tree);
      kfree(avl_test_tree);
      
      return rc;
}

 

2.增加多个结点

  增加结点的测试用例为{10, 99, 178, 106, 7, 4, 44, 55, 1765, 200},如下图:

      

代码:

static int
avlat_add_test_2(struct file *file, void *arg)
{
      avl_tree_t *avl_test_tree;
      avlat_t *avlat_node[10], *first_node, *last_node;
      uint32_t key_value[10]={10, 99, 178, 106, 7, 4, 44, 55, 1765, 200};
      uint32_t i, num=10, rc=0;
 
      // step1: initial avl
      avl_test_tree = kmalloc(sizeof(*avl_test_tree), GFP_KERNEL);
      avl_create(avl_test_tree, avl_test_compare, sizeof (avlat_t), offsetof(avlat_t, avl_node));
 
      if (avl_numnodes(avl_test_tree) != 0)
      {
             printk("Error: avlat_add_test_2-1.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }
 
      // step2: add multiple avl node and check avl_numnodes
      for (i=0; i<num; i++) {
             avlat_node[i] = kmalloc(sizeof(*avlat_node[i]), GFP_KERNEL);
             avlat_node[i]->avl_key=key_value[i];
             avl_add(avl_test_tree, avlat_node[i]);
      }
 
      if (avl_numnodes(avl_test_tree) != num)
      {
             printk("Error: avlat_add_test_2-2.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }
 
      // step3: check avl_first
      first_node = avl_first(avl_test_tree);
      if (first_node->avl_key != 4)
      {
             printk("Error: avlat_add_test_2-3.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }
      
      // step4: check avl_last
      last_node = avl_last(avl_test_tree);
      if (last_node->avl_key != 1765)
      {
             printk("Error: avlat_add_test_2-4.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }
 
      // step5: remove first avl node
      avl_remove(avl_test_tree, avlat_node[1]);
 
      if (avl_numnodes(avl_test_tree) != (num-1))
      {
             printk("Error: avlat_add_test_2-5.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }
      kfree(avlat_node[1]);
 
      // step6: check avl_first
      first_node = avl_first(avl_test_tree);
      if (first_node->avl_key != 4)
      {
             printk("Error: avlat_add_test_2-6.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }
      
      // step7: check avl_last
      last_node = avl_last(avl_test_tree);
      if (last_node->avl_key != 1765)
      {
             printk("Error: avlat_add_test_2-7.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }
      
      // step8: remove all avl nodes
      for (i=0; i<num; i++) {
             if (i==1) {
                     continue;
             }       
             avl_remove(avl_test_tree, avlat_node[i]);
             kfree(avlat_node[i]);
      }
 
      if (avl_numnodes(avl_test_tree) != 0)
      {
             printk("Error: avlat_add_test_2-8.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }       
 
      // step9: destroy avl node
      avl_destroy(avl_test_tree);
      kfree(avl_test_tree);
 
      return rc;
}

 

3.增加已存在结点

增加已存在的结点的,会报错。

代码:

static int
avlat_add_test_3(struct file *file, void *arg)
{
      avl_tree_t *avl_test_tree;
      avlat_t *avlat_node, *first_node, *last_node;
      uint32_t avl_value=12, rc=0;
 
      // step1: initial avl
      avl_test_tree = kmalloc(sizeof(*avl_test_tree), GFP_KERNEL);
      avl_create(avl_test_tree, avl_test_compare, sizeof (avlat_t), offsetof(avlat_t, avl_node));
      
      if (avl_numnodes(avl_test_tree) != 0)
      {
             printk("Error: avlat_add_test_2-1.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }
 
      // step2: add sigle avl node and check avl_numnodes
      avlat_node = kmalloc(sizeof(*avlat_node), GFP_KERNEL);
      avlat_node->avl_key=avl_value;
      avl_add(avl_test_tree, avlat_node);
 
      if (avl_numnodes(avl_test_tree) != 1)
      {
             printk("Error: avlat_add_test_3-2.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }       
      
      /*// step3: add exists avl node and check avl_numnodes
      avl_add(avl_test_tree, avlat_node);
 
      if (avl_numnodes(avl_test_tree) != 1)
      {
             printk("Error: avlat_add_test_3-3.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }*/
 
      // step4: check avl_first & avl_last
      first_node = avl_first(avl_test_tree);
      last_node = avl_last(avl_test_tree);
      
      if ((first_node->avl_key != last_node->avl_key) || (first_node->avl_key != avl_value))
      {
             printk("Error: avlat_add_test_3-4.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }       
 
      // step5: remove avl node
      avl_remove(avl_test_tree, avlat_node);
      if (avl_numnodes(avl_test_tree) != 0)
      {
             printk("Error: avlat_add_test_3-5.\n");
             avlat_destroy_avl_tree(avl_test_tree);
             rc = ERRNO_INFO_NUM;
             return rc;
      }       
      kfree(avlat_node);
 
      // step6: destroy avl node
      avl_destroy(avl_test_tree);
      kfree(avl_test_tree);
 
      return rc;
}

 

二、用户态

cmd文件夹中的avlat文件夹中创建avlat.h和avlat.c,搭建用户态框架。可以直接通过虚拟机执行指令,主要指令用法如下:

    

 

三、用户态和内核态的通信

用户态和内核态的通信通过ioctl来实现,用户态通过ioctl接口设备进入内核态调用。在module文件夹中新建文件avlat-ctl.c,并在include中建立头文件avlat-ctl.h,来具体实现ioctl接口通信。

申请设备:

      

   

 

ioctl接口在内核态的主要实现关键代码:

static long
avlat_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int rc = 0;
 
    /* Ignore tty ioctls */
    if ((cmd & 0xffffff00) == ((int)''T'') << 8)
            return -ENOTTY;
 
    switch (cmd) {
            case AVLAT_CFG:
                   rc = avlat_ioctl_cfg(file, cmd, arg);
                   break;
            case AVLAT_CMD:
                   rc = avlat_ioctl_cmd(file, cmd, arg);
                   break;
            default:
                   avlat_print(file, "Bad ioctl command %d\n", cmd);
                   rc = -EINVAL;
                   break;
    }
 
    return rc;
}

 

ioctl接口在用户态的调用:

 

用户态进入内核态调用运行结果:

 

标注:此篇仅用以说明内核模块测试的用户态和内核态的交互通过代码具体实现的思路,不包含具体代码及代码指导。

linux-kernel – 交叉编译Linux内核模块

linux-kernel – 交叉编译Linux内核模块

我正在研究为ARM linux交叉编译内核模块.我安装了工具链.
但是我从各种各样的方法中得到了一些东西.
我想要构建的模块是gadgetfs.

我的主机上的内核版本是3.5.0-34-generic
 在目标上它是3.6.9-0.1

现在我实际需要下载和安装哪些内核源代码或头文件,以及在哪里?
我从kernel.org下载了linux-3.6.9.tar.bz2并将其解压缩.
在drivers / usb / gadget /中有一个Makefile,根据这个站点,我需要将这些行附加到它,然后运行make:

KDIR := /lib/modules/`uname -r`/build
PWD := `pwd`
obj-m := dummy_hcd.o gadgetfs.o
default: 
$(MAKE) -C $(KDIR) SUBDirs=$(PWD) modules

但我有什么需要取代uname -r?因为这会给我主机的内核版本.但我的目标版本不同. /lib/modules/3.6.9文件夹在哪里?
CROSS_COMPILE和ARCH都已设置.

解决方法

您需要在主机上为您的目标交叉编译(或下载预编译)匹配版本的Linux,并且配置正确,因为Linux没有 stable binary API.主机的内核版本不相关.

在主机上提供目标构建后,您可以通过构建模块

make -C kernel_build_dir M=`pwd` ARCH=arm CROSS_COMPILE=<...> modules

在该模块的目录下.

关于Linux内核模块编程linux内核模块编程 与应用层交互数据的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于 Linux内核模块介绍1.1.1 Linux内核模块概述、c – 制作简单的linux内核模块、Linux 内核模块编程(三)--Kenel Model 测试、linux-kernel – 交叉编译Linux内核模块的相关信息,请在本站寻找。

本文标签: