嵌入式Linux驱动开发(一)一个简单的Linux内核模块框架

2019-07-12 14:23发布

/* mymodtest.c */ #include #include #include static int __init mod_init(void) { return 0; } static void __exit mod_exit(void) { printk("cleanup module "); } module_init(mod_init); module_exit(mod_exit);

一、头文件

  1. linux/init.h
    在init.h头文件中包含了模块的初始化的宏定义 以及一些其他函数的初始化函数
    参考:http://blog.csdn.net/kokodudu/article/details/17361161
  2. linux/kernel.h
    kernel.h中包含了内核打印函数 printk函数 等
    参考:http://blog.csdn.net/kokodudu/article/details/17371103
  3. linux/module.h
    参考:http://blog.csdn.net/kokodudu/article/details/17358001

二、函数

  1. static int __init mod_init(void)
    这个函数是函数的入口,也就是说内核在加载这个模块的时候会去调用这个函数。
  2. static void __exit mod_exit(void)
    模块被卸载的时候会调用这个函数
  3. module_init(mod_init);
    这个函数的作用是为内核模块指定一个入口,在加载内核模块的时候就会调用这个函数的参数对应的函数,也就说第一个函数并不是默认的函数入口,它是我们自己定义的函数的入口,使用 module_init() 这个函数来指定我们自定义的内核模块加载入口
  4. module_exit
    作用同上,指定内核模块被卸载的时候调用的函数

三、关键字

  1. static
    在内核中的函数非常多,我们为我们自己的函数命名很可能就会和Linux其他牛人的重名,这就会导致在编译的时候会报重定义,所以我们就在这加上 staitc 关键字来修饰,来避免和其他函数重名的情况,从ANSI C的角度出发,这就会导致这个函数不能被外部文件调用,而我们知道,Linux 其实是一个宏内核,也就是说内核中的所有函数之间都是直接调用的,而我们这里这样定义会导致其他文件不能调用这个函数,那怎么办呢?其实不用担心,因为版本比较新一点的编译器已经为我们解决了这个问题,编译器在编译的时候就会帮我们处理这个问题,使得被 static 关键字修饰的函数能被外部文件调用。
  2. __init
    这个修饰词是两个下划线加上init( _ _ init),Linux效率非常高,其中一个原因就来源于它对内存的管理可以说是十分"吝啬",static int __init mod_init(void)这个函数其实只是在加载的时候需要,在加载完成之后就没有必要让它继续占用内存了,我们可以使用 __init来修饰它,使得它在完成自己的使命后不再占用内存

四、Makefile

obj-m := mymod.o #module name mymod-objs:= mymodtest.o KDIR :=/lib/modules/`uname -r`/build PWD:=$(shell pwd) default: make -C $(KDIR) M=$(PWD) modules clean: rm -rf *.o *.order *.symvers .cmd *.ko *.mod.c .tmp_versions

Makefile说明。

请记住是大写的Makefile而不是小写的makefile;
  • obj-m :指定模块, 格式: obj-m := <模块名>.o
  • mymod-objs :声称模块(modules)需要的目标文件, 格式: <模块名>-objs := <目标文件>
    ! 切 ! 记 !模块的名字不能取与目标文件相同的名字。如在这里模块名不能取成 mymod;
  • KDIR :这是我们正在运行的操作系统内核编译目录。也就是编译模块需要的环境
  • M= : 指定源文件的位置
  • PWD :这是当前工作路径,$(shell )是make的一个内置函数。用来执行shell命令。