第四季-专题5-内核模块开发

专题5-内核模块开发

第1课-内核模块基础

什么是内核模块?

Linux内核的整体结构非常庞大,其中包含的组件也非常多,如何使用这些组件呢,方法1:把所有的组件都编译到内核文件中,即:zImage或者bzImage,但是这样会导致一个问题:内存占用过多。

有没有一种机制能让内核文件本身并不包含某组件,而是在该组件需要被使用的时候,动态地添加到正在运行的内核呢?

内核模块本身并不会被编译到内核文件(zImage或者bzImage);可以根据需求,在内核运行期间动态的安装护着卸载。

如何使用内核模块

安装:insmod   例如:insmod /home/dnw_usb.ko(usb下载线驱动程序)

卸载:rmmod   例如:rmmod dnw_usb

查看:lsmod    例如:lsmod

第2课-内核模块设计  

  1. 范例代码分析

#include <linux/init.h>

#include <linux/module.h>

static int hello_init()

{

    printk(KERN_WARNING"Hello world! ");

    return 0;      

}

static void hello_exit()

    printk(KERN_WARNING"hello exit! ");

}

module_init(hello_init);

module_exit(hello_exit);

范例代码中没有main函数,在我们正常的应用程序中,main函数起到的是一个入口的作用。我们内核函数中没有面函数,但是也是同样有入口的。这里用module_init这个宏定义来表示入口,module_init这里指向hello_init,当我们输入insmod时就会调用这一个函数了。module_exit这个宏定义表示出口,或者卸载函数(也称为出口函数)。这是内核函数的两个要素。第三个要素就是我们用的两个头文件,任何模块都是要用的。

头文件:linux/init.h; linux/module.h

加载函数:module_init

写在函数:module_exit

操作注意:

当我们把模块导入开发板后,使用命令:insmod hellowordl.ko 进行模块安装,这是会显示hello world!,说明安装正确。但是在卸载时却会出问题,这是因为在/lib/modules下没有对应的文件夹,我们使用命令:mkdir –p /lib/modules/$(uname -r)进行建立即可。这样使用命令:rmmod helloworld就会显示hello exit!,说明模块正常卸载。

     

知识点插入:网关是什么意思?

大家都知道,从有个房间走到另一个房间,必然经过一扇门。同样,从一个网络向另一个网络发送信息,也必须经过一道“关口”,这道关口就是网关。顾名思义,网关(Gateway)就是一个网络连接到另一个网络的“关口”。

按照不同的分类标准,网关有很多种。TCP/IP协议里的网关是最常用的,在这里我们所讲的“网关”均指TCP/IP协议下的网关。

那么网关到底是什么呢?网关实质上是一个网络通向其他网络的IP地址。比如有网络A和网络B,网络A的IP地址范围:“192.168.1.1~192.168.1.154”,子网掩码为:“255.255.255.0”。网络B的IP地址范围是:“192.168.2.1~192.168.2.254”,子网掩码为:“255.255.255.0”。在没有路由器的情况下,两个网络之间是不能进行TCP/IP通讯的,即使是两个网络连接在同一台交换机(或者集线器)上,TCP/IP协议也会根据子网掩码(255.255.255.0)判定两个网络中的主机处在不同的网络里。而要实现两个网络之间的通信,就必须通过网关。如果网络A中的主机发现数据包额目的的主机不在本地网络中,就必须把数据转发给他自己的网关,再有网关转发给网络B的网关,网络B的网关在转发给网络B的某个主机。这就是网络之间的通讯。

所以说,只有设置好网关的IP地址,TCP/IP协议才能实现不同网络之间的通信。那么这个IP地址是哪台机器的IP地址呢?网关的IP地址是具有路由器功能的设备的IP地址,具有路由器功能的设备有路由器、启动了路由协议的服务器(实质上相当于一台路由器)、代理服务器(也相当于一台路由器)。

  1. 编译内核模块

编写makefile,编译内核模块

obj-m := helloworld.o           

%hello-objs :=file1.0 file2.0 file3.o(在有多个文件时使用这条指令,hello对应这个模块的名字)%

KDIR := /home/free/part2/linux   %来发版上运行这个代码的位置,特指电脑的根文件系统的存储代码的位置%

all:

       make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm

clean:

       rm -f *.o *.ko *.order *.aymvers

第3课-内核模块可选项

1. 模块申明

(1)MODULE_LICENSE(“遵守的协议”)

申明该模块遵守的许可证协议,如:”GPL”、”GPL v2”等

(2)MODULE_AUTHOR(“作者”)

申明模块的作者

(3)MODULE_DESCRIPTION(“模块的功能描述")

申明模块的功能

(4)MODULE_VERSION("V1.0")

申明模块的版本

例子:

在我们之前编译的helloworld.ko内核模块中,我们当运行它的时候会显示一些提示信息,主要指的是协议不合法等,我们在头文件下面加上:

MODULE_LICENSE(“GPL”);

再编译运行后,就不会显示之前的提示信息,这样的模块就是合法的了。

2. 模块参数

在应用程序中

int main(int argc, char** argv)

argc表示命令行输入的参数个数,argv中保存输入的参数

当然在内核模块中也可以通过命令来传递参数,而且也有保存的位置。通过宏module_param指定保存模块参数的变量。模块参数用于在加载模块时传递参数给模块。

module_param(name,type,perm)

name:变量的名称

type:变量类型,bool:布尔型int:整型charp:字符串型

perm是访问权限。S_IRUGO:读权限S_IWUSR:写权限

例子:

int a = 3;

char *st;

module_param(a,int, S_IRUGO);

module_param(st,charp, S_IRUGO);

例子:

#include <linux/init.h>

#include <linux/module.h>

MODULE_LICENSE(“GPL”);

int a = 3;

module_param(a,int, S_IRUGO| S_IRUGO); //两个参数一起使用

/* int *p;

module_param(p,charp, S_IRUGO| S_IRUGO);

static int hello_init()

{

    printk(KERN_WARNING"Hello world! ");

       printk("a=%d ",a);

   // printk("p=%s ",p);

    return 0;      

}

static void hello_exit()

{

    printk(KERN_WARNING"hello exit! ");

}

module_init(hello_init);

module_exit(hello_exit);

在运行新的模块的时候,可以直接运行:insmod helloworld.ko a=10,这样在下面就会多显示a=10。

  1. 符号输出

内核符号的导出使用宏

EXPORT_SYMBOL(符号名)

EXPORT_SYMBOL_GPL(符号名

其中EXPORT_SYMBOL_GPL只能用于包含GPL许可证的模块。

例子-      导出内核模块

我们再创建一个模块add.ko做简单的加法运算:

#include <linux/init.h>

#include <linux/module.h>

MODULE_LICENSE("GPL");

Int add(int a,int b)

{

       return a+b;

}

static int add_init()

{

    return 0;      

}

static void add_exit()

{

}

EXPORT_SYMBOL(add); //必须有这不,这才能显示它导出供其他模块使用。

module_init(hello_init);

module_exit(hello_exit);

对应的helloworld.ko改为:

#include <linux/init.h>

#include <linux/module.h>

static int hello_init()

{

    printk(KERN_WARNING"Hello world! ");

    return 0;      

}

static void hello_exit()

{

       add(1,5);

    printk(KERN_WARNING"hello exit! ");

}

module_init(hello_init);

module_exit(hello_exit);

makefile文件变为:

obj-m := helloworld.o add.o          

%hello-objs :=file1.0 file2.0 file3.o(在有多个文件时使用这条指令,hello对应这个模块的名字)%

KDIR := /home/free/part2/linux   %来发版上运行这个代码的位置,特指电脑的根文件系统的存储代码的位置%

all:

       make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm

clean:

       rm -f *.o *.ko *.order *.aymvers

我们将编译好的两个模块都导入到开发板中,先安装add.ko,再安装helloworld.ko,我们会看到正常运行,否则后面的模块是安装不上去的。

原文地址:https://www.cnblogs.com/free-1122/p/10902176.html