1、程序的两大空间:内核层和用户层
正常写的代码都是运行在用户层,很难或者根本接触不到内核层。并且用户层看到的地址都是虚拟地址,所以用户层访问不了硬件。
内核层离硬件近,虽然它的地址也不是真是的物理地址,但是它能间接访问操作真实的物理地址。其中真实的物理地址代表着硬件。
2、内核层驱动的框架
#include "linux/kernel.h"
#include "linux/module.h"
//就相当于标准头文件
/*
static 修饰的函数
不能被其他文件调用!
仅在当前文件生效
static 全局变量
几乎用的很少
static 局部变量
延长生命周期
*/
//驱动的加载函数 -- insmod
//驱动加载会运行此函数
//这也算是我们驱动的入口函数
//类似 main !!!
static int __init led_init(void)
{
printk("hello i am led_init!\r\n");
return 0;
}
//驱动的 卸载函数 -- rmmod
//驱动的卸载 会运行此函数
//它就相当 进程的 钩子函数 !--atexit();
//这个函数作用主要是清理空间的!
static void __exit led_exit(void)
{
printk("hello i am led_exit!\r\n");
}
//驱动的加载函数和卸载函数必须声明
module_init(led_init);
//声明led_init为加载函数
module_exit(led_exit);
//声明 led_exit为卸载函数
MODULE_LICENSE("GPL");
//代表此程序遵循开源协议!
//如无此声明则无法加载驱动
3、如何编译内核的代码(Makefile)
obj-m += led.o #你的代码叫 ccc.c 此处你就要改成 ccc.o
KDIR:=/root/my_code/linux-3.5
#这个是Linux3.5 内核的位置
all:
make -C $(KDIR) M=$(PWD) modules
#不是执行次Makefile
#他是去将此文件 led.o 拿到 内核的位置
#用内核的 Makefile 将此文件编译生成内核的驱动模块!
#而且将此模块放到了当前路径!
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.markers *.order
4、如何使用驱动生成模块文件
insmod:
加载驱动
rmmod:
卸载驱动
lsmod:
查看当前已经加载的驱动
printk:
内核的输出打印,会自带开发板上电到的时间
系统的打印则没有
5、如何同时编译多个模块
例如:有三个 .c 文件
eg:led.c beep.c key.c
三个 .c 文件对应三个驱动,编译生成led.ko beep.ko key.ko
Makefile
多模块化编译的Makefile
obj-m += beep.o #你的代码叫 ccc.c 此处你就要改成 ccc.o
obj-m += led.o
obj-m += beep.o
obj-m += key.o
KDIR:=/root/my_code/linux-3.5
#这个是你Linux3.5 内核的位置
all:
make -C $(KDIR) M=$(PWD) modules
#不是执行次Makefile
#他是去将此文件 led.o 拿到 内核的位置
#用内核的 Makefile 将此文件编译生成内核的驱动模块!
#而且将此模块放到了当前路径!
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.markers *.order
最终生成了 四个 驱动 文件
led.ko beep.ko key.ko
6、多个文件生成一个驱动模块(一个驱动模块必然只能有一个加载函数和一个卸载函数)
obj-m := led.o
led-objs = leddrv.o test.o fun.o
KDIR:=/root/my_code/linux-3.5
#这个是你Linux3.5 内核的位置
all:
make -C $(KDIR) M=$(PWD) modules
#不是执行次Makefile
#他是去将此文件 led.o 拿到 内核的位置
#用内核的 Makefile 将此文件编译生成内核的驱动模块!
#而且将此模块放到了当前 路径!
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.markers *.order
它是依靠 leddrv.c test.c fun.c 生成 led.ko