makefile :
#ubuntu的内核源码树,如果要编译在虚拟机中安装的模块就用这个 KERN_VER = $(shell uname -r) KERN_DIR = /lib/modules/$(KERN_VER)/build # 开发板的linux内核的源码树目录,自定义 #KERN_DIR = /home/yt/drivers/kernel #将module_test.c编译成一个模块 obj-m += module_test.o #第一个命令 all: make -C $(KERN_DIR) M=`pwd` modules .PHONY: clean clean: make -C $(KERN_DIR) M=`pwd` modules clean KERN_DIR,变量的值就是我们用来编译这个模块的内核源码树的目录obj-m += module_test.o,这一行就表示我们要将module_test.c文件编译成一个模块make -C $(KERN_DIR) M=pwd modules 这个命令用来实际编译模块,工作原理就是:利用make -C进入到我们指定的内核源码树目录下,然后在源码目录树下借用内核源码中定义的模块编译规则去编译这个模块,编译完成后把生成的文件还拷贝到当前目录下,完成编译总结:模块的编译要借助内核源码的体系来完成
module.c
#include <linux/module.h> // module_init module_exith宏在这里定义 #include <linux/init.h> // __init __exit宏在这里定义 // 模块安装函数 static int __init chrdev_init(void) { printk(KERN_INFO "chrdev_init helloworld init\n"); #设置打印级别为7 //printk("<7>" "chrdev_init \n"); return 0; } // 模块下载函数 static void __exit chrdev_exit(void) { printk(KERN_INFO "chrdev_exit \n"); } module_init(chrdev_init); module_exit(chrdev_exit); // MODULE_xxx这种宏作用是用来添加模块描述信息 MODULE_LICENSE("GPL"); // 描述模块的许可证 MODULE_AUTHOR("klyer"); // 描述模块的作者 MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息 MODULE_ALIAS("xxx"); // 描述模块的别名信息 头文件,头文件路径是内核源码树的 /include/…,比如#include <linux/module.h>是在/include/linux/module.hmodule_init宏和insmod命令绑定,即insmod----调用module_init宏----调用chrdev_init函数module_exit宏和rmmod命令绑定,即rmmod----调用module_exit宏----调用chrdev_exit函数__init,本质上是个宏定义, #define __init __section(.init.text) __cold notrace这个__init的作用就是将被他修饰的函数放入.init.text段中去(本来默认情况下函数是被放入.text段中)。 __exit和它差不多:
#define __exit __section(.exit.text) __exitused __cold 模块中常用宏,代码中已经注释,一般模块许可证不能少printk函数,和printf功能类似,区别是printf是应用层使用的C库函数,不能在内核中使用。printk函数是内核中使用的,并且多了打印级别设置的功能