Linux内核模块开发

1.LINUX内核模块基础

1.1 什么是内核模块

  • Linux内核的整体结构非常庞大,其包含的组件也非常多,如何使用这些组件呢?方法1:把所有的组件都编译进内核文件,即:zImage或bzImage,但这样会导致一个问题:占用内存过多有没有一种机制能让内核文件本身并不包含某组件,而是在该组件需要被使用的时候,动态地添加到正在运行的内核中呢?
  • 内核模块具有如下特点:
    • 1)模块本身并不被编译进内核文件(zImage或者bzImage);
    • 2)可以根据需求,在内核运行期间动态的安装或卸载。

1.2 安装与卸载

  • 安装 insmod 例:insmod /home/dnw_usb.ko
  • 卸载 rmmod 例:rmmod dnw_usb
  • 查看 lsmod  例:lsmod  lsmod是列举当存在内存中的所有模块名称,占用大小,有多少个用户正在使用等信息。

2.内核模块设计

2.1 范例代码分析

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


static int hello_init()
{
    printk(KERN_WARNING"Hello world!\n");
    return 0;	
}

static void hello_exit()
{
    printk(KERN_WARNING"hello exit!\n");	
}

module_init(hello_init);
module_exit(hello_exit);
  • 1.对比我们的应用程序可以发现,这段代码没有main()函数,大家都知道main()是一个入口函数,难道我们的模块代码不需要入口函数 吗?肯定是要的,这个入口函数由module_init()来指明,当使用insmod函数来加载一个模块时,module_init()所指明的函数将会被得到调用,也就是hello_init()将会得到执行。
  • 2.同时当使用rmmod去卸载一个模块时,module_exit()宏所指定的函数将得到调用。
  • 3.需要包含头文件<linux/init.h>和<linux/module.h>
  • 4.函数前面加了static后表示该函数失去了全局可见性,只在该函数所在的文件作用域内可见,其他作用域不能调用此函数
  • 5.打印信息使用的不是printf而是printk,一般提示性的信息会使用KERN_WARING,中间不需要逗号。

2.2Makefile编写

obj-m := helloworld.o

KDIR := /home/linux/workdir/kernel/linux-mini2440

all:
    make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
    
clean:
    rm -f *.o *.ko *.order *.symvers
  • obj-m 表示生产文件的名字
  • 如果有多个.c文件则需要采用如下的写法:
    • obj-m := helloworld.o
    • helloworld-objs := file1.o file2.o file3.o
  • 定义一个Makefile的变量KDIR,保存开发板上运行内核代码的路径,这里根据个人情况编写;
  • 生成目标,make -C $(KDIR)表示进入到内核代码里面,M=${PWD}表明模块代码的路径,就是当前路径,modules表示执行的命令是Make modules,最后指明交叉工具链
  • 清除产生文件

2.3内核模块运行

  • 当开发板采用NFS文件系统时,可以直接把生成的helloworld.ko文件拷贝到文件系统中,然后执行insmod:有些情况会打印出2句警告信息,然后再打印出Hello world!。
  • 再执行rmmod,发现报错了,提示没有****目录,原因在于当卸载一个模块时,在lib/modules/下面有和内核版本一致的目录,如果没有可以新建:mkdir -p /lib/modules/$(uname -r)再卸载,可以看到打印出了hello exit!。

3.内核模块可选信息

1.模块申明

  • MODULE_LICENSE(”遵守的协议”)申明该模块遵守的许可证协议,如:“GPL“、”GPL v2“等
  • MODULE_AUTHOR(“作者”)申明模块的作者
  • MODULE_DESCRIPTION(“模块的功能描述")申明模块的功能
  • MODULE_VERSION("V1.0")申明模块的版本

2.模块参数

  • 在应用程序中int main(int argc, char** argv)argc表示命令行输入的参数个数,argv中保存输入的参数。
  • 1)那么内核模块中可以通过命令行输入参数么?答案:可以
  • 2)参数怎么传入,传入后保存在哪里?
    • 通过宏module_param指定保存模块参数的变量。模块参数用于在加载模块时传递参数给模块
    • module_param(name,type,perm)
      • name:变量的名称
      • type:变量类型,bool:布尔型 int:整型 charp:字符串型
      • perm是访问权限。 S_IRUGO:读权限   S_IWUSR:写权限
#include <linux/init.h>
#include <linux/module.h>

int a = 3;
char *p;

static int hello_init()
{
    printk(KERN_WARNING"hello world!\n");
    printk("a = %d\n",a);	
	printk("p = %s\n", p);
    return 0;
}

static void hello_exit()
{
    printk(KERN_WARNING"hello exit!\n");        	
}

MODULE_LICENSE("GPL");

module_param(a, int, S_IRUGO | S_IWUSR);
module_param(p, charp, S_IRUGO | S_IWUSR);

module_init(hello_init);
module_exit(hello_exit);

3.符号输出

  • 内核模块中实现的函数需要被另外一个内核模块调用时,需要用到模块符号输出。
  • 内核模块符号的输出使用的宏:
    • EXPORT_SYMBOL(符号名)
    • EXPORT_SYMBOL_GPL(符号名),说明:其中EXPORT_SYMBOL_GPL只能用于包含GPL许可证的模块。
#include <linux/init.h>
#include <linux/module.h>

extern add(int a, int b);

int a = 3;
char *p;

static int hello_init()
{
    printk(KERN_WARNING"hello world!\n");
    printk("a = %d\n",a);	
	printk("p = %s\n", p);
    return 0;
}

static void hello_exit()
{
	add(1, 3);
	printk(KERN_WARNING"hello exit!\n");        	
}

// 模块信息声明
MODULE_LICENSE("GPL");

// 模块参数声明
module_param(a, int, S_IRUGO | S_IWUSR);
module_param(p, charp, S_IRUGO | S_IWUSR);

// 模块函数声明
module_init(hello_init);
module_exit(hello_exit);
#include <linux/init.h>
#include <linux/module.h>

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

static int add_init()
{
	return 0;
}

static void add_exit()
{
    
}

// 模块信息声明
MODULE_LICENSE("GPL");

// 模块符号输出
EXPORT_SYMBOL(add);

// 模块函数声明
module_init(add_init);
module_exit(add_exit);
obj-m := helloworld.o add.o

KDIR := /home/S4_a/part3/lesson3/lesson-2440/linux-mini2440

all:
	make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
	
clean:
	rm -f *.o *.ko *.order *.symvers *.mod.c
  • 定义一个Makefile的变量KDIR,保存开发板上运行内核代码的路径,这里根据个人情况编写。
  • 生成目标,make -C $(KDIR)表示进入到内核代码里面,M=${PWD}表明模块代码的路径,就是当前路径,modules表示执行的命令是Make modules,最后指明交叉工具链。
  • 清除一些中间文件。

猜你喜欢

转载自blog.csdn.net/qq_22847457/article/details/90752859