《Linux4.0设备驱动开发详解》笔记--第四章:Linux内核模块

时间:2021-02-26 17:57:30

4.1 内核模块程序结构

  • 加载:insmod,modprobe(同时加载模块的依赖模块)
  • 卸载:rmmod
  • 查看已加载的模块及模块间的依赖关系:lsmod,实际上是分析/proc/modules文件
  • 已加载的模块信息放在/sys/module目录下,没加载一个模块就会在该目录下生成一个以模块名命名的目录,“tree -a”可获取目录树
  • 模块许可声明:申明许可权限,否则将收到内核被污染警告,一般申明为MODULE_LICENSE(“GPL v2”)语句申明采用GPL v2.
  • 模块参数(可选):模块被加载的时候可以传递给它的值,它本身对对应模块的内部的全局变量
  • 模块导出符(可选):其他模块可以使用模块导出的函数和变量
  • 模块作者等信息声明

4.2 模块加载函数

  • Linux内核模块加载函数一般以__init标识申明,如
static int __init initialization_function(void)
{
/*初始化代码*/
}
module_init(initialization_function);

初始化成功返回0,否则返回错误码。

  • request_module(const char* fmt, …):灵活加载内核模块
  • 数据也可以定义为__initdata,只是在初始化的阶段需要的数据,结束后释放占用的内存

4.4 模块卸载函数

一般以__exit标识申明,如

static void __exit cleanup_function(void)
{
/*释放代码*/
}
module_exit(cleanup_function);

被直接编译进内核的模块的卸载函数会被省略,不编译进内核,因为模块被内置了,也就不会被卸载。

4.5 模块参数

  • module_param(参数名,参数类型,参数读写权限):为模块定义一个参数,如下定义一个int参数和char指针参数
static char *book_name = "dissecting Linux Deice Driver";
module_param(book_name, char, S_IRUGO);

static int book_num = 4000;
module_param(book_num, int, S_IRUGO);

“insmod (或modprobe) 模块名 参数名 = 参数值”,不传递则用缺省值,模块被内置时用bootloader通过bootargs里设置“模块名.参数 = 值”给内置模块传递参数。

  • 参数数组:module_param_array(数组名,数组类别,数组长度,参数读写权限)
  • /sys/module下有已加载模块命名的目录,当“参数读写权限”为0,则此参数不存在sysfs文件系统下对应的节点,否则此模块的目录下会出现parameter目录,其中包含以参数名命令的文件节点,文件权限与设定的权限一致。
  • 允许insmod和modprobe命令时,用逗号隔开输入的数组元素
  • 例:定义两个参数的模块
/*======================================================================
A kernel module: book
This example is to introduce module params

The initial developer of the original code is Baohua Song
<author@linuxdriver.cn>. All Rights Reserved.
======================================================================*/

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");

static char *book_name = "dissecting Linux Device Driver";
static int num = 4000;

static int book_init(void)
{
printk(KERN_INFO " book name:%s\n",book_name);
printk(KERN_INFO " book num:%d\n",num);
return 0;
}
static void book_exit(void)
{
printk(KERN_INFO " Book module exit\n ");
}
module_init(book_init);
module_exit(book_exit);
module_param(num, int, S_IRUGO);
module_param(book_name, charp, S_IRUGO);

MODULE_AUTHOR("Song Baohua, author@linuxdriver.cn");
MODULE_DESCRIPTION("A simple Module for testing module params");
MODULE_VERSION("V1.0");

通过insmod加参数和不加参数实验,在/var/log/messages文件中查看内核的输出
在/sys/module/book/parameters目录下输入tree查看参数文件节点

4.6 导出符号

  • /proc/kallsyms文件记录了符号及符号所在的内存地址
  • 导出符号:EXPORT_SYSBOL(符号名),EXPORT_SYSBOL_GPL(符号名)(只适用于包含GPL许可权的模块)
  • 例:
/*======================================================================
A simple kernel module to introduce export symbol

The initial developer of the original code is Baohua Song
<author@linuxdriver.cn>. All Rights Reserved.
======================================================================*/

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");

int add_integar(int a,int b)
{
return a+b;
}

int sub_integar(int a,int b)
{
return a-b;
}

EXPORT_SYMBOL(add_integar);
EXPORT_SYMBOL(sub_integar);

4.7 模块申明与描述

  • MODULE_AUTHOR(author);
  • MODULE_DESCRIPTION(description);
  • MODULE_VERSION(version_string);
  • MODULE_DEVICE_TABLE(table_info);
  • MODULE_ALIAS(alternate_name);
  • 对于USB, PCI等设备驱动,通常创建一个MODULE_DEVICE_TABLE来表明驱动模块支持的设备

4.8 模块的使用计数

  • try_module_get(&module)和module_put(&module):模块计数管理接口
  • try_module_get(&module):增加模块使用计数,返回0,调用失败,希望使用的的模块不存在或正在被卸载
  • module_put(&module):减少模块使用计数
  • 模块的使用计数一般不由模块本身管理,由内核更底层的代码(总线驱动或者此类设备共用的核心模块)来实现,以简化驱动开发
  • 当设备正在使用的时候,模块不可以被卸载

4.9 模块编译

  • Makefile
  • 模块包含多个.c文件,则Makefile写法
obj-m := modulename.o
modulename-objs := file1.o file2.o ...