(本节笔记的实验代码,在这里)
一. 内核模块基础
1. 基本概念
Linux内核的整体结构非常庞大,使用组件的方法分为两种,一是把组件编译进内核文件(zImage或uImage),但会导致占用内存过多;二是把需要用到的内核组件在需要用到的时候通过动态的方式加载进内核。
内核模块本身并不被编译进内核文件,可以根据需求,在内核运行期间动态地安装或卸载,因此,可以更加有效的节省内存空间。
2. 安装于卸载
2.1 安装 insmod
例如:insmod /home/dnw_usb.ko
2.2 卸载 rmmod
例如:rmmod dnw_usb //注意不是dnw_usb.ko
2.3 查看 lsmod
例如:lsmod
二. 内核模块设计
1. 内核模块三个要素
1.1 头文件 <linux/init.h> <linux/module.h>
1.2 加载函数 module_init
1.3 卸载函数 module_exit
2. 范例代码 touch helloworld.c
#include <linux/init.h>
#include <linux/module.h>
static int hello_init()
{
printk(KERN_WARNING"hello world!\n"); //提示性信息以便用WRNING优先级
return 0;
}
static void hello_exit()
{
printk(KERN_WARNING"hello exit!\n");
}
module_init(hello_init);
module_exit(hello_exit);
3. 编写Makefile,编译内核模块
3.1 touch Makefile
obj-m := helloworld.o
KDIR := /arm/linux 2.6.30.4 //模块所依赖的宿主机上内核源码目录
all:
make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
rm -f *.o *.ko *.order *.symvers
『
obj-m := hello.o //若是file1.o file2.o file3.o等多个文件
hello-obj := file1.o file2.o file3.o
......
』
3.2 编译和使用内核模块
#make
#cp helloworld.ko /arm/rootfs
//启动开发板并通过启动NFS rootfs
#insmod helloworld.ko
hello world!
#rmmod helloworld.ko
hello exit!
三. 内核模块可选项
1. 模块声明
1.1 MODULE_LICENSE(“遵守的协议”)
申明该模块遵守的许可证协议,如:“GPL”、“GPL v2”等。
1.2 MODULE_AUTHOR(“作者”)
申明模块的作者。
1.3 MODULE_DESCRIPTION(“模块的功能描述”)
申明模块的功能
1.4 MODULE_VERSION(“版本”)
申明模块的版本,如“V1.0”等。
1.5范例代码
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL"); //声明遵守的许可证协议
static int hello_init()
{
printk(KERN_WARNING"helloworld!\n");
return 0;
}
static void hello_exit()
{
printk(KERN_WARNING"helloexit!\n");
}
module_init(hello_init);
module_exit(hello_exit);
2. 模块参数
类似于int main(int argc, char** 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(s,charp,S_IRUGO);
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
int a = 3;
char *p;
module_param(a, int, S_IRUGO | S_IWUSR); //声明模块参数
module_param(p, charp, S_IRUGO | S_IWUSR);
static int hello_init()
{
printk(KERN_WARNING"helloworld!\n");
printk("a = %d\n",a);
return 0;
}
static void hello_exit()
{
printk(KERN_WARNING"helloexit!\n");
}
module_init(hello_init);
module_exit(hello_exit);
在加载的时候输入:#insmod helloworld.ko a=10
#rmmod helloworld
#insmod hellorld.ko p=abcd
#rmmod helloworld
3. 符号输出
在一个模块内(a.ko)使用了另外一个模块(b.ko)内的函数,除了a.ko中要用extern引用需要调用的b.ko中的函数以外,b.ko要声明该函数要被其他模块调用(EXPORT_SYMBOL(函数名))。
在嵌入式Linux系统中,必须先装载 b.ko,然后才能装载 a.ko,卸载也是,必须先卸载a.ko,然后在卸载b.ko,因为a.ko依赖于b.ko。
touch helloworld.c
『
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
extern add(int a, int b); //声明add函数
static int hello_init()
{
printk(KERN_WARNING"helloworld!\n");
a = add(1, 4) //引用add.ko中的add函数,只能在hello_init中调用
printk("a = %d\n",a);
return 0;
}
static void hello_exit()
{
printk(KERN_WARNING"helloexit!\n");
}
module_init(hello_init);
module_exit(hello_exit);
』
touch add.c
『
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
int add(int a, int b)
{
return a+b;
}
static int hello_init()
{
return 0;
}
static void hello_exit()
{
}
EXPORT_SYMBOL(add); //声明add()会被其他模块加载
module_init(hello_init);
module_exit(hello_exit);
』
touch Makefile(注意PWD为大写)
『
obj-m := helloworld.o add.o
KDIR := /arm/linux 2.6.30.4
all:
make -C $(KDIR) M=$(PWD) modules ARCH=armCROSS_COMPILE=arm-linux-
clean:
rm -f *.o *.ko *.order *.symvers