【linux】驱动-2-内核模块


前言

  • 以野火i.M 6U为例

2. 内核模块

本章节笔记主要理解内核模块代码框架和原理,分析一个简单的内核模块例子。
须要明确的是模块驱动是两回事。html

2.1 内核模块概念

2.1.1 内核

内核,是一个操做系统的核心。是基于硬件的第一层软件扩充,提供操做系统的最基本的功能, 是操做系统工做的基础,决定着整个操做系统的性能和稳定性。
内核按照体系结构分为:微内核宏内核linux

  • 参考图:

2.1.2 内核模块机制的引入

提升系统灵活性,在调试驱动的时候不须要从新编译内核,也不须要从新启动内核,只须要插入须要调试的驱动便可。
内核模块的特色:框架

  • 模块自己不被编译入内核映像,这控制了内核的大小。
  • 模块一旦被加载,它就和内核中的其它部分彻底同样。

2.2 内核模块

内核模块编译后会获得一个 .koELF 文件。(ELF 文件能够百度一下,也能够参考野火的内核模块章节。)
ELF 文件:这类文件包含了代码和数据,能够被用来连接成可执行文件或共享目标文件,静态连接库也能够归为这一类。函数

2.2.1 内核模块参考例程

必须内容可分为如下几点:性能

  • 入口函数:当经过insmod或modprobe命令加载内核模块时,模块的加载函数就会自动被内核执行,完成本模块相关的初始化工做。
  • 出口函数:执行rmmod命令卸载模块时,模块卸载函数就会自动被内核自动执行,完成相关清理工做。
  • 协议:许可证声明描述内核模块的许可权限,若是模块不声明,模块被加载时,将会有内核被污染的警告。

非必须内容:ui

  • 模块参数:模块参数是模块被加载时,能够传值给模块中的参数。
  • 模块导出符号: 模块能够导出准备好的变量或函数做为符号,以便其余内核模块调用。
  • 模块的其余相关信息: 能够声明模块做者等信息。

hello_module.c操作系统

/** @file hello_module.c
* @brief 简要说明
* @details 详细说明
* @author lzm
* @date 2021-02-21 18:08:07
* @version v1.0
* @copyright Copyright By lizhuming, All Rights Reserved
*
**********************************************************
* @LOG 修改日志:
**********************************************************
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
// 入口函数:安装驱动时调用的函数
static int __init hello_init(void)
{
printk(KERN_EMERG "[ KERN_EMERG ] Hello Module Init\n");
printk( "[ default ] Hello Module Init\n");
return 0;
}
// 出口函数:卸载驱动时调用的函数
static void __exit hello_exit(void)
{
printk("[ default ] Hello Module Exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
//MODULE_LICENSE("GPL2");
MODULE_AUTHOR("embedfire ");
MODULE_DESCRIPTION("hello world module");
MODULE_ALIAS("test_module");

2.2.2 内核模块命令

insmod:插入模块:insmod+模块完整路径
modprobe:插入模块,insmod具有一样的功能,一样能够将模块加载到内核中,除此之外modprobe还能检查模块之间的依赖关系, 而且按照顺序加载这些依赖,能够理解为按照顺序屡次执行insmod
depmod:建立模块依赖文件。modprobe是怎么知道一个给定模块所依赖的其余的模块呢?在这个过程当中,depmod起到了决定性做用,当执行modprobe时, 它会在模块的安装目录下搜索module.dep文件,这是depmod建立的模块依赖关系的文件。
rmmod:删除模块:insmod+模块名称
lsmod:查看全部模块。
modinfo:显示模块中的几个宏的定义。如协议、做者等等。指针

2.2.3 系统自动加载模块 **

让系统自动加载模块须要用到命令depmod和modprobe调试

首先须要将咱们想要自动加载的模块统一放到 /lib/modules/内核版本 目录下,内核版本使用 uname -r 查询;
其次使用depmod创建模块之间的依赖关系,命令 depmod -a
这个时候咱们就能够在modules.dep中看到模块依赖关系,可使用以下命令查看:日志

cat /lib/modules/内核版本/modules.dep | grep calculation

最后在/etc/modules加上咱们本身的模块。

  • 注意在该配置文件中,模块不写成.ko形式表明该模块与内核紧耦合,有些是系统必需要跟内核紧耦合,好比mm子系统, 通常写成.ko形式比较好,若是出现错误不会致使内核出现panic错误,若是集成到内核,出错了就会出现panic。
    而后重启开发板,使用命令lsmod便可查看咱们的模块开机就被加载到内核了。

2.2.4 导出符号

实际上,符号指的就是内核模块中使用 EXPORT_SYMBOL 声明的函数和变量。当模块被装入内核后,它所导出的符号都会记录在公共内核符号表中。可供给其它模块使用。
导出方法:

  • 符号必须在模块文件的全局部分导出,不能在函数中使用。
  • _GPL使得导出的模块只能被GPL许可的模块使用。
  • 编译咱们的模块时,这两个宏会被拓展为一个特殊变量的声明,存放在ELF文件中。 具体也就是存放在ELF文件的符号表中:
    • st_name: 是符号名称在符号名称字符串表中的索引值;
    • st_value: 是符号所在的内存地址;
    • st_size: 是符号大小;
    • st_info: 是符号类型和绑定信息;
    • st_shndx: 表示符号所在section。
EXPORT_SYMBOL(name)
EXPORT_SYMBOL_GPL(name)  // name为要导出的标志

调用方法(例子):

extern int name;

2.2.5 模块参数

模块参数:模块参数是模块被加载时,能够传值给模块中的参数。

Linux内核提供一个宏来实现模块的参数传递:

#define module_param(name, type, perm) module_param_named(name, name, type, perm)
#define module_param_array(name, type, nump, perm) module_param_array_named(name, name, type, nump, perm)
  • name: 咱们定义的变量名;
  • type:参数的类型,目前内核支持的参数类型有byte,short,ushort,int,uint,long,ulong,charp,bool,invbool。其中charp表示的是字符指针,bool是布尔类型,其值只能为0或者是1;invbool是反布尔类型,其值也是只能取0或者是1,可是true值表示0,false表示1。变量是char类型时,传参只能是byte,char * 时只能是charp。
  • perm:表示的是该文件的权限,具体参数值见下表:
对应用户 字符 说明
当前用户 S_IRUSR 用户具备读权限
当前用户 S_IWUSR 用户具备写权限
当前用户组 S_IRGRP 当前用户组的其它用户拥有读权限
当前用户组 S_IWGRP 当前用户组的其它用户拥有写权限
其它用户 S_IROTH 其它用户具备读权限
其它用户 S_IWOTH 其它用户具备写权限

模块参数使用示例
模块源码:

static int nameA=0;
module_param(nameA,int,0);
static bool nameB=0;
module_param(nameB,bool,0644);

加载模块后,会在路径 /sys/module/模块名/parameters 下存在以模块参数为名的文件。(注:若文件权限为0,则没法查看该文件,也不会显示在该路径

参考

相关文章
相关标签/搜索