1.内核有很多模块构成,称为内核模块;
2.驱动程序是一种内核模块,通常是直接访问硬件的内核模块;
3.应用程序无法直接访问底层,只能进行系统调用。
4.内核模块中不能调用c库函数,只能调用其他内核模块导出的函数。
比如:printk()
,在头文件<linux/kernel.h>中,用法和printf类似,但是不能处理浮点数,内核中,一般不处理浮点数。
(1)应用程序通常在/usr/include
(2)驱动程序头文件路径
5.驱动有两种形态
(1)直接编译到内核;
在内核启动时,该驱动就被自动加载了,并且不能卸载。
Makefile中是obj-m:=hello.o
(2)编译成模块*.ko文件,如果只以 . ko文件,可以不开发源代码。
Makefile中是obj-y:=hello.o
在需要时加载(insmod
)到内核中,不用时可以卸载(rmmod
)掉。lsmod
查看加载的模块
6.加载卸载
7.
(1)如果以*.ko模块
存在,可以在需要时加载到内核,不用时卸载调
驱动有一个加载函数和卸载函数
当模块被加载到内核时,会自动调用加载函数
当模块从内核中移除时,会自动调用卸载函数
(2)
如果驱动被直接编译
到内核,内核启动时会自动调用加载函数,不能卸载,此时卸载没有用。
__init
是说明将该函数放到一个特殊的段中,当内核启动时,自动调用该函数,只执行一次,当内核启动完成,可以将该段空间释放掉。
__exit
是说明将该函数放弃,不要编译。
8.
我们在开发驱动时,多数应该使用*.ko形式,只有当已经测试完肯定没有问题了,又很重要的驱动,才会将他们直接编译到内核中。
9.如何编译模块
由于驱动是在内核的一部分,我们需要用到内核源代码编译系统。
(1)在ubuntu
上编译
这种情况下我们需要ubuntu
的内核源代码编译系统。
在/usr/src/目录下,使用uname -r,显示的是哪个目录再进入。
在/usr/src/linux-headers-4.15.0-126-generic下面有Ubuntu内核的部分源码;
同时在/lib/modules/4.15.0-126-generic/build下面也有。
通常使用在/lib/modules/4.15.0-126-generic/build的编译系统来编译
代码示例
#include<linux/kernel.h>
#include<linux/module.h>
MODULE_LICENSE("GPL"); //指定本模块所遵循的协议(GPL协议)
int __init init_module(void)
{
printk("hello world\n");
return 0;
}
void __exit cleanup_module(void)
{
printk("goodbye world\n");
}
Makefile示例
obj-m:=hello.o //指定编译那个文件(hello.c)
编译命令:
make -C /lib/modules/4.15.0-126-generic/build M=$(pwd) modules
-C是指定编译系统的路径
-M是表示要回到当程序所在路径。
执行结果
ubuntu中的printk的信息不会直接打印出来,需要我们运行dmesg
来查看。
10.写一个复杂的makefile
ifeq ($(KERNELRELEASE),) #判断$(KERNELRELEASE)是否定义,为空就是未定义,他是在内核源码中被定义,现在为空;内核源码目录中的Makefile会定义KERNELRELEASE
KERNELDIR ?= /lib/modules/$(shell uname -r)/build #KERNELDIR 记录了内核源码的路径
PWD := $(shell pwd) # PWD记录当前路径
default:
$(MAKE) -C $(KERNELDIR) M= $(PWD) modules #$(MAKE)就是执行make,M=$(PWD)意思是当生成模块时,回到当前目录继续编译,因为我们的*.c在当前目录;
clean:
rm -rf .*.cmd *.ko *.o *.o. *.mod.* module* Module* .tmp*
else
obj-m := hello.o
endif
ifneq ($(KERNELRELEASE),)
obj-m:=hello.o
else
KDIR := /lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.symvers *.cmd *.cmd.o
endif
现在直接make就行了。
Makefile参考链接
(2)在开发板上如何编译
将Makefile里ubuntu的编译系统的路径修改成开发板上对应的内核源代码的路径.
将*.ko复制到开发板的根文件系统,运行insmod就可以加载了。
运行lsmod查看加载的模块;
开发板上直接rmmod hello移除不了,这个是busybox的问题。