一个模块引用另一个模块导出的函数
2014-02-27 20:21
246 查看
/*本篇由真胖子同志打造,原创辛苦,转载请标明出处http://blog.csdn.net/figtingforlove/article/details/20067463*/
编写一个内核模块,在模块中引用另一个模块的导出函数。编写提供plus导出函数的module_plus模块
(1)源程序
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
MODULE_LICENSE("GPL");
static int init(void)
{
printk("<0>""\nplus_init success!\n");
return 0;
}
static int exit(void)
{
printk("<0>""\nplus_exit success!\n");
return0;
}
int plus(int a, int b)
{
return(a+b);
}
EXPORT_SYMBOL(plus);
module_init(init);
module_exit(exit);
(2)Makefile文件
ifneq ($(KERNELRELEASE),)
obj-m :=module_plus.o
else
KERNELDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
Makefile编写的具体格式以后分析~~~~~~
(3)编译,加载,查看输出
make
sudoinsmod module_plus.ko
dmesg后可以看到终端显示“plus_initsuccess!”
lsmod可以看到如下显示,表明模块加载成功了
Module Size Used by
module_plus 12630 0
cat /proc/kallsyms查看内核符号表可以看到模块导出的函数
0000000000000000T plus [module_plus]
现在已经有了一个模块module_plus提供了一个导出函数,在编写另一个模块之前我们先看一下该模块的构成。
上述make编译在模块的目录下生成了几个重要的文件,看看他们都表示什么
module_plus.mod.c文件,也就是前面所说的由模块编译工具链生成的,在来看看那个有趣的声明
struct module __this_module
__attribute__((section(".gnu.linkonce.this_module"))) = {
.name = KBUILD_MODNAME,
.init = init_module,
#ifdef CONFIG_MODULE_UNLOAD
.exit = cleanup_module,
#endif
.arch = MODULE_ARCH_INIT,
};
此外它还提供了模块的版本信息和依赖关系.
Module.symvers文件提供了模块导出的函数信息,包括函数地址,函数名,所在模块目录
0x668f06c7 plus /home/l/ll/otn项目/项目测试代码/内核模块实例/模块符号导出实例/symbol/module_plus EXPORT_SYMBOL
编写module_test模块,引用plus函数,按照一般程序设计代码如下
(1)源程序
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
MODULE_LICENSE("GPL");
extern int plus(int,int);
static int hello_init()
{
printk(KERN_ALERT"Hello world\n");
inti ;
int c = 0;
c = plus(1,2);
printk(KERN_ALERT"c = %d\n",c);
return0;
}
static int hello_exit()
{
printk(KERN_ALERT"Goodbye world\n");
return0;
}
module_init(hello_init);
module_exit(hello_exit);
(2)编译make,可以看到一个警告说“plus”函数未定义
WARNING:"plus"[/home/l/ll/otn项目/项目测试代码/内核模块实例/模块符号导出实例/hello/hello.ko]undefined!
(3)加载sudoinsmod hello.ko
insmod:error inserting 'hello.ko': -1 Invalid parameters
说是有不可用的参数,why?看看输出信息dmesg
[2052.726996] hello: no symbol version for plus
[2052.727001] hello: Unknown symbol plus (err -22)
原来是找不到“plus”符号,奇怪ing,明明已经导出了啊,why?
。。。。。这时候想到刚才module_plus模块加载后查看内核符号表似的输出
cat /proc/kallsyms查看内核符号表可以看到模块导出的函数
0000000000000000T plus [module_plus]
奇怪的00000000000,也就是说内核符号表中该函数的地址竟然是0,也就是未知,难怪加载模块的时候找不到对应的函数代码。So?How?
Answer1 :想到module_plus模块不是生成了Mododule.symvers符号视图么,那么用这个文件替换当前hello模块目录下生成的Mododule.symvers文件
编译,success
加载,success
dmesg,[ 2278.175896] c = 3
好吧,他成功了,butwhy?
发挥伟大的大脑~~~~~~回顾一下前面的模块加载部分,在发现“为解决的引用”符号时,的处理函数
Answer2:既然内核符号表导出函数地址是0,而函数确实导出了,那我们就找到这个有加载的Module_plus模块中plus函数的地址
内核编程中关于查找导出符号的函数,具体的函数解释请看导出符号章节。
/*inclede/module.h*/
/*Find a symbol and return it, along with, (optional) crc and(optional) module which owns it */
const struct kernel_symbol *find_symbol(const char *name,struct module **owner,const unsigned long **crc,bool gplok,bool warn)
{
struct find_symbol_arg fsa;
fsa.name= name;
fsa.gplok= gplok;
fsa.warn= warn;
if(each_symbol(find_symbol_in_section, &fsa)) {
if(owner)
*owner= fsa.owner;
if(crc)
*crc= fsa.crc;
return fsa.sym;
}
DEBUGP("Failedto find symbol %s\n", name);
return NULL;
}
EXPORT_SYMBOL(find_symbol);
//得到符号表的函数符号地址
void*__symbol_get(const char *symbol)
{
struct module *owner;
const struct kernel_symbol *sym;
preempt_disable();
sym= find_symbol(symbol, &owner, NULL, true, true);
if(sym && strong_try_module_get(owner))
sym= NULL;
preempt_enable();
return sym ? (void *)sym->value : NULL;
}
EXPORT_SYMBOL(__symbol_get);
修改上述程序代码如下,我们用__symbol_get获取函数指针:
typedef int (*plus)(int a, int b);
//extern int plus(int,int);
static int hello_init()
{
printk(KERN_ALERT"Hello world\n");
plus p = NULL;
inti ;
p= (plus)__symbol_get("plus") ;
int c = 0;
//c= plus(1,2);
c = p(1,2);
printk(KERN_ALERT"c = %d\n",c);
return 0;
}
make,insmod,dmesg没有任何问题,问题解决,看来该方法可行。
[14534.773267]plus_init success!
[14681.389583]Hello world
[14681.389592]c = 3
总结一下,看来模块加载的时候会在处理引用的符号时根据依赖关系搜索该符号,得到所引用函数的地址。
在不做处理时,hello模块加载无法找到plus函数,不能建立模块间的依赖关系。hello.mod.c文件中
static const char __module_depends[]__used
__attribute__((section(".modinfo")))=
"depends=";
answer1:模块在加载的时候,根据Module.symvers文件,知道会用到那个导出的引用,并且得到模块module_plus
static const char __module_depends[]
__used__attribute__((section(".modinfo")))=
"depends=mosule_plus";
answer2 :在模块加载的时候,根据内核导出模块处理“为解决引用”符号的原则,找到到处符号表,重定位,找到符号地址~~find_symbol
大概就是这样,也许有误差,但不影响理解。But,如果此时卸载模块module_plusrmmodmodule_plus会提示ERROR:Module module_plus is inuse,我们已经卸载了引用它的模块了,为什么还卸载不了?那我们就再编写一个模块去读module_plus模块的状态,看看为什么。
(3)编写测试模块读另一个模块的状态
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/list.h>
#include<linux/cpumask.h>
static int __init mymod_init(void)
{
struct module *mod;
struct module_use *use;
int cpu;
//打印本模块的模块名和模块状态
printk(KERN_ALERT"[insmod mymod] name:%s, state:%d\n",THIS_MODULE->name,THIS_MODULE->state);
//遍历模块列表,查找target模块
list_for_each_entry(mod,THIS_MODULE->list.prev,list)
{
if(strcmp(mod->name,"module_plus")==0){
//1.打印模块的模块名、模块状态、引用计数
printk(KERN_ALERT"name:%s,state:%d, refcnt:%lu ",mod->name,mod->state,module_refcount(mod));
//1.打印出所有依赖module_plus的模块名(遍历mod->source_list)
if(!list_empty(&mod->source_list)){
list_for_each_entry(use,&mod->source_list,source_list)
printk(KERN_ALERT"%s\t",use->source->name);
}else
printk(KERN_ALERT"used by NULL\n");
/*
//2.将模块的引用计数变为0
for_each_possible_cpu(cpu){
per_cpu_ptr(mod->refptr, cpu)->decs = 0;
per_cpu_ptr(mod->refptr,cpu)->incs = 0;
}
//2.再看看module_plus的名称、状态、引用计数
printk(KERN_ALERT"name:%s,state:%d, refcnt:%lu\n",mod->name,mod->state,module_refcount(mod));
*/
}
}
return 0;
}
static int __exit mymod_exit(void)
{
printk(KERN_ALERT"[rmmodmymod] name:%s state:%d\n",THIS_MODULE->name,THIS_MODULE->state);
return0;
}
module_init(mymod_init);
module_exit(mymod_exit);
MODULE_AUTHOR("lilei");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Whymodule can not be removed");
先来分下下代码
/*linux/module.h*/
/*模块状态*/
208 enum module_state {
209 MODULE_STATE_LIVE, // 0
210 MODULE_STATE_COMING, // 1
211 MODULE_STATE_GOING, // 2
212 MODULE_STATE_UNFORMED, // 3
213};
/*模块引用计数*/
224 struct module_ref {
225 unsigned long incs;
226 unsigned long decs;
227} __attribute((aligned(2 * sizeof(unsigned long))));
228
/*kernel/module.c*/
/*获取模块的引用计数函数*/
774 unsigned long module_refcount(struct module *mod)
775{
776 unsigned long incs = 0, decs = 0;
777 int cpu;
778
779 for_each_possible_cpu(cpu)
780 decs += per_cpu_ptr(mod->refptr, cpu)->decs;
794 smp_rmb();
795 for_each_possible_cpu(cpu)
796 incs += per_cpu_ptr(mod->refptr, cpu)->incs;
797 return incs - decs;
798}
现在对代码1编译,加载模块,dmesg
[8276.079311] [insmod mymod] name:a, state:1
[8276.079317] name:module_plus, state:0,refcnt:1
[8276.079318] used by NULL
这里stat:1表示mod->state= MODULE_STATE_COMING,说明这个模块正在执行mod->init函数,还没有执行到mod->state= MODULE_STATE_LIVING。可以看到module_plusde的引用计数是1。简单的想,是不是把这个模块的这个值变为0,就可以了?试试吧,去掉注释把代码2加进来,老过程。。。
[8276.079311] [insmod mymod] name:a, state:1
[8276.079317] name:module_plus, state:0, refcnt:1
[8276.079318] used by NULL
[8276.079321] name:module_plus, state:0,refcnt:0
[8307.155957]
[8307.155957] plus_exit success!
[8358.543440] [rmmod mymod] name:a state:2
模块是成功卸载了,但是为什么?
可以看到,虽然这时的module_plus并没有其它模块依赖它了但refcnt=1。why?很容易想到是我们在hello模块中添加的代码有bug,应该是在某个地方将模块计数加1了。我们在编写module_plus的时候,通过调用__symbol_get查找符号
1886 void *__symbol_get(const char *symbol)
1887{
1888 struct module *owner;
1889 const struct kernel_symbol *sym;
1890
1891 preempt_disable();
1892 sym = find_symbol(symbol, &owner, NULL, true, true);
1893 if (sym && strong_try_module_get(owner))
1894 sym = NULL;
1895 preempt_enable();
1896
1897 return sym ? (void *)sym->value : NULL;
1898}
/*查看源代码find_symbol没有问题,再看看strong_try_module_get*/
190 static inline int strong_try_module_get(struct module *mod)
191{
192 BUG_ON(mod && mod->state ==MODULE_STATE_UNFORMED);
193 if (mod && mod->state == MODULE_STATE_COMING)
194 return -EBUSY;
195 if (try_module_get(mod))
196 return 0;
197 else
198 return -ENOENT;
199}
/*再看try_module_get*/
951 bool try_module_get(struct module *module)
952{
953 bool ret = true;
954
955 if (module) {
956 preempt_disable();
957
958 if (likely(module_is_live(module))) {
959 __this_cpu_inc(module->refptr->incs);
960 trace_module_get(module, _RET_IP_);
961 } else
962 ret = false;
963
964 preempt_enable();
965 }
966 return ret;
967}
惊喜的发现果然__this_cpu_inc(module->refptr->incs);对模块的引用计数加了1,不出所料(尼玛的不出,找了好久,才想到~。。~)这就是为什么refcnt=1了,怪不得不能卸载模块,所以说内核代码也不能随便引用,特别是在不了解他的细节的时候。现在了解了原因,那我们换个函数find_symbol试试。因为直接更改模块的这个变量值总觉得不是很规范。
static int hello_init()
{
printk(KERN_ALERT"Hello world\n");
plus p = NULL;
const struct kernel_symbol *sym;;
int c = 0;
sym = find_symbol("plus",THIS_MODULE, NULL, true, true);
if(sym) {
p= (plus*)sym->value;
c= p(1,2);
}else
sym= NULL;
p= NULL;
printk(KERN_ALERT"c = %d\n",c);
return 0;
}
编译,加载,dmesg
[14534.773267]plus_init success!
[14681.389583]Hello world
[14681.389592]c = 3
ok!noproblem ~~~正常卸载了,至于符合规范这回事,我也不知道,可这也算是一种方法吧。
2.6内核的解决~~请参考
http://blog.csdn.net/zhangskd/article/details/7945140
相关文章推荐
- 一个模块使用另一个模块导出的符号,该模块怎样编译?很经典!
- 使用IDLE在一个py文件中引用另一个py中的类或函数
- 内核中一个文件中的函数在另一个文件中被引用
- javascript 在一个函数参数中包含另一个函数的引用
- linux一个c文件要引用另一个c文件中的函数编译的时候,应该怎么编译?
- Linux内核一个模块调另一个模块的函数
- linux modules 一个模块调用另一个模块的函数
- node.js关于一个js文件引用另一个js文件里面的函数或者数组变量
- Linux内核模块导出后无法调用问题解决(模块间函数调用通讯)
- 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
- ASP一个常用的函数模块
- c++如何一个文件如何调用在同一个工程下的另一个文件的函数
- CreateProcess()函数用法一个应用程序中调用另一个应用程序的.exe
- 使用sqlite3的接口函数完成一个用户登录验证功能模块设计 要封装成独立函数,在独立的main中调用测试;
- C语言strstr()函数常用示例详解[求一个字符串在另一个字符串中出现的次数]
- 一个解决方案中有两个项目,一个项目调用另一个项目中的函数
- Golang 如何将一个函数作为另一个函数的输入值,清晰透彻的注释让你理解该如何阅读抽象的代码
- Python3基础 创建一个模块 导入并调用其中的函数 主代码与模块代码在同一文件夹下
- 在一个程序中控制另一个程序的函数调用
- 如何在一个程序集中引用另一个程序集Resource的问题