2.2 函数:__module_text_address( )
文件包含:
#include <linux/module.h>
函数定义:
在内核源码中的位置:linux-3.19.3/kernel/module.c
函数定义格式:struct module *__module_text_address( unsigned long addr )
函数功能描述:
该函数的功能是获得一个模块指针,它要满足条件: addr所表示的内存地址落在该模块的代码段中。
输入参数说明:
addr:表示内存地址。
返回参数说明:
返回值是一个struct module类型的指针,如果内存地址addr在某一模块的代码段内,则返回指向该模块的指针,如果addr不在模块的地址空间内或者它不在代码段内,则返回NULL。
关于结构体struct module的定义,请参见本章中f ind_module( )函数的分析。
实例解析:
编写测试文件:__module_text_address.c
头文件及全局变量声明如下:
#include <linux/module.h> #include <linux/init.h> MODULE_LICENSE("GPL"); static int __init __module_text_address_init(void); static void __exit __module_text_address_exit(void); int fun_a(void) { return 0; } static int var_b = 4;
模块初始化函数:
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( ); //允许抢占 /* 如果查找成功,则输出该模块的信息 */ printk("it's about fun_a:\n"); if( ret ! = NULL ) { printk("ret->name: %s\n", ret->name); //输出模块名 printk("ret->state: %d\n", ret->state); //输出模块状态 /* 输出模块core段所占空间大小 */ printk("ret->core_size: %d\n", ret->core_size); } else { printk("fun_a is not in text area! \n"); } addr = (unsigned long)&var_b; //addr为静态全局变量var_b的地址 preempt_disable( ); ret = __module_text_address(addr) ; preempt_enable( ); /* 如果查找成功,则输出该模块的信息 */ printk("\nit's about var_b:\n"); 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段所占空间大小 } else { printk("var_b is not in text area! \n"); } return 0; }
模块退出函数:
void __exit __module_text_address_exit(void) { printk("module exit ok! \n"); }
模块初始化及退出函数调用:
module_init(__module_text_address_init); module_exit(__module_text_address_exit);
实例运行结果及分析:
首先编译模块,执行命令insmod __module_text_address.ko插入模块,然后执行命令dmesg -c,会出现如图2-2所示的结果。
图2-2 插入__module_text_address模块后系统输出信息
结果分析:
在该测试程序中,定义了一个函数fun_a( ),它存在于程序空间的代码段中,定义了一个全局静态变量var_b,它将位于程序空间中的数据段。
程序示例中调用__module_text_address( )时,为了防止模块被释放,需要禁止抢占,宏preempt_disable( )和preempt_enable( )分别用来实现禁止内核抢占和允许内核抢占。
首先将fun_a( )的入口地址作为实参传递给__module_text_address( )函数,该函数是判断所给的实参地址是否位于某一模块代码段中的,并且返回相应的模块指针。从输出信息可知,ret不为空,并且ret->name为“__module_text_address”, ret->state为1, ret->core_size为12501字节。以上说明fun_a( )的入口地址确实位于某一模块的代码段,而且该模块为当前正被加载的模块__module_text_address。
然后将全局静态变量var_b的地址作为实参传递给__module_text_address( )函数,从输出信息可知,由输出语句为“var_b is not in text area! ”可知ret为空。这说明var_b所在的内存单元并不在模块的代码段,因为var_b是全局静态变量,它是存在于数据段中的。
函数fun_a( )和变量var_b在模块插入到内核后,是作为内核符号存在的,在虚拟文件系统proc的kallsyms文件中有对它们相关的描述。
在图2-3显示的两行中,关于fun_a的显示,第二列为“t”,它表示符号fun_a位于代码段。关于var_b的显示,第二列为“d”,它表示var_b位于数据段。这与上面的测试结果是一致的。
图2-3 fun_a和var_b在kallsyms文件中的相关信息