3.5.2. Linux内核API之模块机制
3.5.2.1. 数据结构
struct module
{
/*
* 模块当前状态,state取值有四种情况
* MODULE_STATE_LIVE: 指示模块当前正在使用
* MODULE_STATE_COMING: 指示模块当前正在被加载
* MODULE_STATE_GOING: 指示模块当前正在被卸载
* MODULE_STATE_UNFORMED, 指示模块伪被设置,未定型
*/
enum module_state_state;
struct list_head list; //指向模块链表的下一个模块
char name[MODULE_NAME_LEN]; //特定的模块名称
...
/*向内核空间导出的符号*/
const struct kernel_symbol *syms; //指向模块的符号表,表大小为num_syms
const unsigned long *crcs;
unsigned int num_syms; //模块中符号的个数
/*内核参数*/
struct kernel_param *kp;
unsigned int num_kp;
/*基于GPL-only的可移除符号*/
unsigned int num_gpl_syms;
const struct kernel_symbol *gpl_syms;
const unsigned long *gpl_crcs;
...
/*异常处理函数表*/
unsigned int num_exentries;
struct exception_table_entry *extable;
/*指向初始化方法的函数指针*/
int (*init)(void);
/*模块初始化时函数的内存地址*/
void *module_init;
/*模块的内存起始地址*/
void *module_core;
/*模块目标代码的初始化部分和执行部分所占内存空间大小*/
unsigned int init_size, core_size;
/*init和core段中可执行代码所在内存空间的大小*/
unsigned int init_text_size, core_text_size;
/*模块RO区域大小,包括text+rodata两部分区域*/
unsigned int init_ro_size, core_ro_size;
/*基于特定体系结构的模块值*/
struct mod_arch_specific arch;
/*标识内核是否加入了非自由软件的模块*/
...
#ifdef CONFIG_SMP
void *percpu; //每个CPU数据
unsigned int percpu_size;
#endif
...
#ifdef CONFIG_MODULE_UNLOAD
struct list_head source_list; //所有依赖于该模块的模块
struct list_head target_list; //所有该模块依赖的模块
voi (*exit)(void);
atomic_t refcnt; //记录模块被引用的次数
#endif
}
3.5.2.2. 函数接口
__module_address
__module_address函数功能描述: __module_address()根据给定的一个内存地址addr,获得该内存地址所在的模块
#include <linux/module.h>
/*
* addr: 表示内存地址
return: 如果内存地址addr在某一模块的地址空间中,则返回指向该
模块的结构体指针,否则返回NULL
*/
struct module *__module_address(unsigned long addr)
测试代码
int a_module(void) //此处定义一个自己添加的内核函数
{
return 0;
}
int __init __module_address_init(void)
{
struct module *ret;
unsigned long addr = (unsigned long)a_module;
/*调用__module_address函数之前,必须禁止中断,以防止模块在执行操作期间被释放*/
preempt_disable();
ret = __module_address(addr);
preempt_enable();
if(ret != NULL) {
printk("ret->name: %s\n", ret->name);
printk("ret->state: %s\n", ret->state);
printk("ret->core_size: %s\n", ret->core_size);
printk("refs of %s is %d \n", ret->name, module_refcount(ret));
}
return 0;
}
__module_text_address
__module_text_address函数功能描述: 该函数的功能是获得一个模块指针,它要满足两个条件: addr所表示的内存地址落在该模块的代码段中
#include <linux/module.h>
/*
* addr: 表示内存地址
return: 返回值是一个struct module类型的指针内,如果内存地址addr在某一模块的代码段中,则返回
指向该模块的指针
*/
struct module *__module_text_address(unsigned long addr)
测试代码
int __init __module_text_address_init(void)
{
unsigned long addr = (unsigned long)fun_a; //addr为函数fun_a的入口地址
struct module *ret;
preempt_disable();
ret = __module_text_address(addr);
preempt_enable();
if(ret != NULL) {
printk("ret->name: %s\n", ret->name); //输出模块名
printk("ret->state: %d\n", ret->state); //输出模块状态
printk("ret->core_size: %d\n", ret->core_size); //输出模块core段所占空间大小
}
}
__print_symbol
__print_symbol函数功能描述: 该函数的功能与sprint_symbol的函数功能是相似的,实际上,__print_symbol函数的实现中调用了函数sprint_symbol
该函数根据一个内存中的地址address查找一个内核符号,并将该符号的基本信息,例如符号名name, 它在内核符号表中的偏移offset和大小size等信息以 格式化串fmt形式输出,而sprint_symbol函数则是将这些信息放到文本缓冲区buffer中
#include <linux/kallsyms.h>
/*
* fmt:输出内核符号基本信息所依据的格式串
address: 内核符号中的某一地址
*/
void __print_symbol(const char *fmt, unsigned long address)
测试代码
int __init __print_symbol_init(void)
{
char *fmt;
unsigned long address;
char *name;
struct module *fmodule = NULL;
address = (unsigned long)__builtin_return_address(0); //当前函数的返回地址
fmt = "it is the first part, \n %s";
__print_symbol(fmt, address);
name = "psmouse";
fmodule = find_module(name); //查找模块名"psmouse"的模块
if(fmodule != NULL) {
printk("fmodule->name: %s\n", fmodule->name);
address = (unsigned long)fmodule->module_core;
fmt = "it is the second part, \n %s";
__print_symbol(fmt, address);
}
}
__symbol_get
__symbol_get函数的功能是根据给定的内核符号名symbol,获得该符号的内存地址,找到其所在的内核模块,并将该模块的引用计数加1
#include <linux/module.h>
/*
* symbol: 字符串常量,代表内核符号名
* return: 返回一个void类型指针,其值代表内核符号symbol的地址
*/
void *__symbol_get(const char *symbol)
测试代码
int __init __symbol_get_init(void)
{
const char *symbol_name;
void *addr;
symbol_name = "symbol_a";
addr = __symbol_get(symbol_name);
if(addr != NULL)
printk("the address of %s is : %lx\n", symbol_name, (unsigned long)addr);
}
__symbol_put
该函数根据给定的内核符号名symbol,找到其所在的内核模块,并将该模块的引用计数减1
#include <linux/module.h>
/*
* symbol: 字符串常量,代表内核符号名
*/
void __symbol_put(const char *symbol)
find_module
该函数用来获得一个指向模块的指针,他是根据给定的模块名字查找模块链表,如果找到一个与给定的模块名字相匹配的模块,则返回该模块指针
#include <linux/module.h>
/*
* name: 表示所要查找的模块名字
* return: 返回指向查找到的名字为name的模块
*/
struct module *find_module(const char *name)
测试代码
int __init find_module_init(void)
{
const char *name = "test_module";
struct module *fmodule = find_module(name);
if(fmodule != NULL) {
printk("fmodule->name: %s\n", fmodule->name);
printk("fmodule->state: %d\n", fmodule->state);
printk("fmodule->core_size: %d\n", fmodule->core_size);
printk("module_refcount(fmodule): %d\n", module_refcount(fmodule));
}
}
find_symbol
该函数通过给定的内核符号的名字,以及其他参数查找内核符号,并返回描述该符号的结构体指针
#include <linux/module.h>
/*
* name: 表示待查找的内核符号名字
* owner: 二级指针,指向所查找的内核符号所属的内核模块指针,是一个输出型参数
* crc: 二级指针,表示内核符号的crc值所在的地址,是一个输出型参数
* gplok: 如果为真,表示内核符号所属的模块支持GPL许可
* warn: 表示是否输出警告信息
*/
const struct kernel_symbol *find_symbol(const char *name, struct module **owner,
const unsigned long **src, bool gplok, bool warn)
测试代码
int __init find_symbol_init(void)
{
const char *name = "symbol_a";
struct kernel_symbol *ksymbol;
struct module *owner;
const unsigned long *crc;
bool gplok = true;
bool warn = true;
ksymbol = find_symbol(name, &owner, &crc, gplok, warn);
if(ksymbol != NULL) {
printk("ksymbol->value: %lx\n", ksymbol->value);
printk("ksymbol->name: %s\n", ksymbol->name);
}
if(owner != NULL) {
printk("owner->name: %s\n", owner->name);
}
}
module_is_live
该函数是判断模块mod是否处于活动状态
#include <linux/module.h>
/*
* mod: 模块结构体指针,结构体中包含模块的名称,状态,所属的模块链表等
* return: 1表示处于live
*/
static inline int module_is_live(struct module *mod)
测试代码
void __init module_is_alive_init(void)
{
int ret = module_is_alive(THIS_MODULE);
if(ret == 1) {
printk("state is live!\n");
}
}