Linux下驱动模块学习

1.modutils中提供了相关的insmod,rmmod,modinfo工具
2.modprobe在识别出目标模块所依赖模块后也是调用insmod.
3.从外部看模块只是普通可重定位的目标文件。可重定位文件的函数都不会引用绝对地址,而只是指向代码中的相对地址,因此可以在内存
中的任意偏移地址加载。
4.加载模块只需要一个系统调用init_module,即可在内核中完成所有的操作。
5.nm工具可用于产生模块(或任意目标文件)中所有的外部函数列表。
# nm atmel_mxt_ts.ko
# nm a.out
U代表未解决的引用;T表示位于代码段;D表示位于数据段

6.配置 CONFIG_KALLSYMS 选项为y即可在内核中启用 kallsyms 功能:
有的符号是大写的,有的是小写。大写的符号是全局的。
b 符号在BSS段中
c 普通符号,是未初始化区域
d 符号在数据段中
g 符号针对小object,在初始化数据区
i 非直接引用其他符号的符号
n 调试符号
r 符号在只读存储区
s 符号针对小object,在未初始化数据区
t 符号在代码段
u 符号引用还未解决

7.modutils标准工具中的depmod工具可用于计算系统各个模块之间的依赖关系,每次启动或安装模块时就会运行该程序,默认会将
依赖关系保存到/lib/modules/version/module.dep中,表示格式B:A表示模块B依赖于模块A

8./proc/kallsyms中列出了内核导出的所有符号(早些版本应该是/lib/modules/version/System.map),模块中未解决引用的符号会在这里面找。

9.内核中模块的信息存放在.modinfo段中。模块中的信息可以使用modinfo来看。

10.模块的自动加载与热插拔
1)在用户空间完成模块加载比在内核空间完成模块加载更方便,内核将该工作委托给kmod进程。注意kmod并不是一个永久守护进程,内核会
按需启用它。

2)eg:mount -t vfat /dev/fd0 /mnt/floppy 若之前内核中没有vfat.ko,这时会先插入这个驱动模块,在mount返回后,所需的模块已经载
入内核了。执行过程:内核发现其数据结构中没有vfat的信息 --> 内核请求 --> modprobe --> 模块查找 --> request_module --> 加载模块
request_module
├── 为modeprob准备环境
├── 同时调用request_module的次数过多(50个)?==> return
└── call_usermodehelper
modprobe_path通常是/sbin/modprobe,但是可以通过/proc/sys/kernel/modprobe或sysctl改变。

3).有时候内核需要加载模块的时候可能会出现无法唯一确定哪个模块能提供所需的功能,因此产生模块别名(module alias)
eg:U盘插入到系统中,被主机控制器识别为新设备,我们知道需要装载的模块时usb-storage,但是内核是如何知道的呢:
附加在每个模块上都有一个“小小的数据库”,里面描述了该模块所支持的设备。对于USB设备,数据库的信息包括所支持的接口类型列表,
厂商ID或能够标识设备的任意类型信息。
数据库信息通过模块别名(module alias)提供,它是模块的通用标识。modules.h中的MODULE_INFO、MODULE_ALIAS宏,eg raid5.c
udevd实现模块的热插拔也是使用了这个信息。

4)比直接别名更重要的是设备数据库,内核使用宏MODULE_DEVICE_TABLE来实现这样的数据库。

5)add_uevent_var() 位于kobject_uevent.c中,可以用于向uevent中的插拔消息提供一个新的键值对!
通过比较MODALIAS值和各个模块提供的别名,udevd可以找到需要插入的模块。
①模块代码中使用MODULE_ALIAS指定别名;
②设备插入时的udev事件中使用下面方法上报设备的别名
add_uevent_var(env, "MODALIAS=virtio:d%08Xv%08X", dev->id.device, dev->id.vendor);
这个函数这个使用方法在内核中还存在,应该没有被遗弃!!

11.模块的插入和删除
1)两个系统调用,init_module() 和 delete_module(), 可以使用man 2 来看
init_module
├── load_module
├── 将模块插入到内核链表
├── mod->init
└── 释放初始化数据/代码占用的区域

sys_delete_module
├── find_module
├── 确认模块未被使用
├── mod->exit
└── free_module

2)模块状态:
enum module_state {
MODULE_STATE_LIVE, /*模块正常运行时*/
MODULE_STATE_COMING, /*模块装载期间*/
MODULE_STATE_GOING, /*模块正在移除*/
MODULE_STATE_UNFORMED, /* Still setting it up. */
};

12.license_is_gpl_compatible()用来判断给定许可证是否GPL兼容
13.在模块自身和所依赖的所有其它模块都已经编译完成之前,模块中的有些段是无法生成的
14.模块的初始化和清理函数保存在.gnu.linkonce.module段中的module实例中,该实例位于每个模块自动生成的附加文件中。名为module.mod.c中
15.使用EXPORT_SYMBOL()导出符号,它会在__ksymtab段中产生一个结构。此宏包含的__CRC_SYMBOL()来实现版本控制,主语模块的版本控制是per-symbol
的。
16. 模块信息常用宏
MODULE_INFO 一般模块信息
MODULE_LICENSE 模块许可证
MODULE_AUTHOR 模块作者
MODULE_DESCRIPTION 模块描述
MODULE_ALIAS 指定模块别名(udevd使用它执行热插拔加载模块)

17.版本控制
1)基本版本控制
module.mod.c中MODULE_INFO(vermagic, VERMAGIC_STRING)
#define VERMAGIC_STRING \
UTS_RELEASE " " \ /*字符串形式的内核版本*/
MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT \
MODULE_VERMAGIC_MODULE_UNLOAD MODULE_VERMAGIC_MODVERSIONS \
MODULE_ARCH_VERMAGIC \
MODULE_RANDSTRUCT_PLUGIN
展开就是:"4.14.0 SMP preempt mod_unload aarch64"

内核和模块中都会存储VERMAGIC_STRING的一份副本。只有这两份副本匹配时,模块才能加载。这意味着模块和内核的下列方面必须一致才能加载:
①SMP配置(是否启用)
②抢占配置(是否启用)
③使用的编译器版本
④特定于体系结构的常数

注意:基本版本控制中内核的版本虽然会存储,但是在进行比较时会忽略。因此内核版本不同的模块,只要剩余版本字符串匹配,这个检查就不影
响模块的装载。

2)per-symbol的版本控制
在EXPORT_SYMBOL()中有个__CRC_SYMBOL()来产生此symbol的crc,加载的时候会比较
版本控制函数check_version()

18.内核不仅在设备插入与移除时向用户空间提供消息,实际上内核在很多一般事件发生时都会发送消息。模块的插入与移除也会.
设备模型的每一个部分都可以向用户层发送注册和撤销注册事件。

19.将module.info.c文件编译成目标文件,并使用ld将其与模块现存的.o目标文件链接起来,结构命名为module.ko,这就是最终的模块!

20.不仅设备驱动程序可以编译成模块,内核中除了最基础部分外都可以编译成模块。

原文地址:https://www.cnblogs.com/hellokitty2/p/9738993.html

时间: 2024-09-21 11:01:37

Linux下驱动模块学习的相关文章

Linux下编程学习一

本篇主要记录一些在学习LINUX下编程时,, C和C++语言的一些基础的常识, 一. 函数指针 void MyFun(int x); 函数声明 void (*FunP)(int ); 函数指针声明 下面是C和C++中的指针定义  int (*pt2Function)(float, char, char) = NULL; // C普通函数指针 int (TMyClass::*pt2Member)(float, char, char) = NULL; // C++中指向静态函数的指针 int (TM

linux下coredump学习

参照 https://www.cnblogs.com/alantu2018/p/8468879.html 1.查看linux下coredump是否开启 在linux上coredump默认是关闭的,可以通过ulimit -c查看,如果输出为0,则代表coredump没有开启. 可以使用 ulimit -c unlimited开启,再用ulimit -c查看,结果为unlimited: 但这种操作只能对当前终端有效,想让coredump持久开启,需修改vim /etc/security/limits

Linux下git学习笔记(一)

我是跟着廖雪峰大神的博客学习git的,他讲的非常容易懂 (http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000),然后自己跟着学的时候并不是很顺利,向github提交代码的时候老是报错,不过最后google很久之后给解决了. 先回顾下今天学的吧 一:在Linux上安装Git sudo apt-get install git 因为Git是分布式版本控制系统,所以,每个机器都必须自报家门:

Linux下Python学习笔记 1:数据类型

一.开发环境 开发Python的环境选择很多,windows.Linux.MacOs都可以,除了Windows需要在Python官网上下载安装包,并配置环境变量.在Linux和MacOs上都是自带Python的,关于Python的版本大多都是2.7.* 本文选择了Ubuntu环境来学习Python,主要原因是为了顺便熟悉Linux的环境与命令,开发工具选择的是Vim,当然更多其他的选择有Eclipse.Sublime.pycharm等等,根据个人喜好选择即可, 另:文章部分内容来自Imooc和5

Linux下防火墙学习

1.防火墙浅解iptables最大的优点是可以配置有状态的防火墙,带有连接跟踪的防火墙就称为带有状态机制的防火墙,相比非状态防火墙而言更安全,因为可以编写更缜密的安全过滤策略.有状态的防火墙能够指定并记住为发送或接收信息包所建立的连接状态,防火墙可以从信息包的连接跟踪状态获得该信息.在决定过滤新的信息包时,防火墙所使用的这些状态信息可以增加其效率和速度.有四种有效状态,分别为:ESTABLISHED.INVALID.NEW和RELATED.ESTABLISHED:指该信息包属于已建立的连接,该连

linux下的学习之路下的小困难

centos下源码安装python3wget --no-check-certificate https://www.python.org/ftp/python/3.6.2/Python-3.6.2.tgzpython3的环境依赖安装yum -y install xz tar gcc make tk-devel wget sqlite-devel zlib-devel readline-devel openssl-devel curl-devel tk-devel gdbm-devel xz-de

Linux下Python学习笔记 2:条件判断、循环

一.条件判断 if 语句 比如,输入用户年龄,根据年龄打印不同的内容,在Python程序中,可以用if语句实现: 注意: Python代码的缩进规则.具有相同缩进的代码被视为代码块,上面的3行 print 语句就构成一个代码块(但不包括第4行的print).如果 if 语句判断为 True,就会执行这个代码块. 缩进请严格按照Python的习惯写法:4个空格,不要使用Tab,更不要混合Tab和空格,否则很容易造成因为缩进引起的语法错误. 在ubuntu下的vim中需要设置tab默认为4个空格,可

linux下Makefile学习

1. 基本概念 关于程序的编译和链接: 编译 链接源码------中间代码------可执行文件.c .o编译过程中,主要检查语法是否正确,函数与变量声明是否正确,若函数未被声明,编译器会给出警告,但可以生成obj文件:链接时,主要链接函数和全局变量,负责管理中间目标文件,寻找函数的实现,若找不着,报链接错误码. 大多数情况下,由于源文件较多,编译生成的中间目标文件太多,在链接时要指出的中间目标文件太多,这对编译很不方便,所以可给中间目标文件打个包,window下称为库文件,即.lib文件:UN

Linux下Makefile学习笔记

makefile 可以用于编译和执行多个C/C++源文件和头文件. (1) #include "file.h" 和 #include <file.h> 的区别 #include "file.h" 会先在当前目录下查找file.h,然后才在系统头文件目录中进行查找: #include <file.h>会先查找系统头文件目录,默认是不会在当前目录下查找的. (2) 关于在头文件中使用#ifndef 和 #define 头文件的主要作用在于多个代码