目录
本专栏文章将有70篇左右,欢迎+关注,查看后续文章。
7.1 概述
模块:
内核组件,或驱动程序。
内核源码的许可证:
GNU GPL v2
7.2 使用模块
7.2.1 添加和删除
modutils工具包中有:
1. rmmod:卸载模块。
2. insmode:加载单一模块。
3. modprobe:加载模块及其依赖模块。
模块文件:一种可重定位文件(relocatable)。
可重定位的文件不会引用绝对地址,而是使用相对地址。
因此可重定位的文件能被链接器在内存中的任意地址加载和链接。
静态链接器:ld 命令。
将编译后的多个目标文件(.o文件)链接成可执行文件或者库文件。
动态链接器:ld.so
在程序运行时加载共享库到进程的地址空间,并解析程序中的符号引用。
nm 命令:
作用:查看目标文件的符号表(如函数,全局变量)
使用举例:
# nm test.o
0000000000000000 T func
0000000000000000 B i
0000000000000019 T main
U malloc
0000000000000000 D z
其中:
T:Text
U:Unresolve。未解决的引用
D:Data
内核如何找到已加载的模块中未定义的函数?
读取 /proc/kallsyms 内容,其中包括函数及其内存地址。
CONFIG_KALLSYMS_ALL:
将所有符号信息导出到内核镜像的内核符号表中。
7.2.2 依赖关系
依赖关系:一个模块使用的函数定义在其他模块中。
depmod 命令:(busybox 中的一个命令)
作用:
1. 为每个模块都计算依赖关系。
2. 将依赖关系写入 /lib/modules/3.10.0-uc0/modules.dep
modprobe 命令:
作用:读取 modules.dep 得到模块的依赖关联,并加载模块及其依赖模块。
modules.builtin 文件:
路径:/lib/modules/3.10.0-uc0/modules.builtin
内容:
包含所有编译进内核镜像中的模块名和路径(而非独立模块)。
使用:
内核启动时,会读取该文件,自动初始化内置模块。
modules.order文件
路径:/lib/modules/3.10.0-uc0/modules.order
内容:
记录了内核模块的加载顺序。
使用:内核启动时,读取该文件按照顺序加载模块,可保证依赖关系正确。
System.map:
包含了内核导出的所有符号。
7.2.3 查询模块信息
modinfo 命令
来源:modutils工具包。
作用:查询模块信息。
原理:读取了模块的.modinfo段,得到模块信息。
使用举例:
# modinfo /lib/modules/3.10.0-uc0/kernel/net/ipv4/ip_gre.ko
filename: /lib/modules/3.10.0-uc0/kernel/net/ipv4/ip_gre.ko
license: GPL
alias: netdev-gretap0
depends: gre
vermagic: 3.10.0-uc0 mod_unload ARMv7
parm: log_ecn_error:Log packets received with corrupted ECN
7.2.4 自动加载
模块加载方式分为:
1. 用户层:modprobe / insmod 命令。
2. 内核:内核函数 request_module,该函数将调用用户层的 modprobe 命令。
request_module
-> call_modprobe
-> call_usermodehelper_setup():
call_usermodehelper_setup():
作用:内核使用该函数调用用户空间的程序。
当接入USB 存储卡,内核如何知道加载 usb-storage 模块,而不是usb-eth 模块?
1. 读取USB设备描述符中Class Code(类别),厂商ID等。
2. 读取模块别名(ALIAS)。
MODULE_ALIAS宏:
作用:生成模块别名。别名将保存在模块文件 .modinfo 段。
一个模块可同时支持多个别名,如:
MODULE_ALIAS("ip6t_SNPT");
MODULE_ALIAS("ip6t_DNPT");
比别名更直观的是设备数据库:
struct platform_device_id my_driver_ids[] = {
{
.name = "s3c2410-i2c",
.driver_data = TYPE_S3C2410,
}, {
.name = "s3c2440-i2c",
.driver_data = TYPE_S3C2440,
},
};
MODULE_DEVICE_TABLE(platform,my_driver_ids);
将my_driver_ids导出到用户空间,就可以知道加载设备什么驱动了。
#define MODULE_DEVICE_TABLE(type, name) \
extern const struct type##_device_id __mod_##type##__##name##_device_table
将生成一个对应ELF符号。
scripts/mod/file2alias.c会对PCI/USB等总线来解析设备表,并生成MODULE_ALIAS项。
模块的别名:
作用:模块自动装载的基础。
有些 bus 的 match 函数可能会比较 alias 和 device->name 是否一致。