不知大家有没有想过,在一个内核模块代码中,会用到printk
函数,而这个函数不是我们实现的,它是内核代码的一部分,但我们为什么能够编译通过呢?
我们的代码之所以能够编译通过,是因为对模块的编译 仅仅是编译,并没有链接 。
编译出来的.ko
文件是一个普通的ELF
文件 ,使用file
命令和nm
命令,我们可以看到相关的信息:
# file vser.ko
vser.ko ELF 32-bit LSB relocatable, Intel 80386, vserion 1 (SYSV), BuildID[sha1]=0x09ca747e6f75c65v19a5da9102113v98d7cea24, not stripped
# nm vser.ko
......
00000004 d port
U printk
00000000 t vser_exit
00000000 t vser_init
vser_init
和vser_exit
分别是模块的入口函数和出口函数,使用nm
命令查看模块目标文件的符号信息时,可以看到vser_exit
和vser_init
的符号类型是t
,表示它们是 函数 。
而printk
的 符号类型是U
,表示它是一个 未决符号 。意思是说在编译阶段不知道这个符号的地址,因为它被定义在其他文件中,没有放在模块代码一起编译。
那printk函数的地址问题怎么解决呢?答案是用EXPORT_SYMBOL
宏将printk
导出即可。
EXPORT_SYMBOL导出符号
大致原理:利用EXPORT_SYMBOL
宏生成一个特定的结构并放在ELF
文件的一个特定段中,在 内核的启动过程中,会将符号的确切地址填充到这个结构的特定成员中 。
模块加载时,加载程序将去处理未决符号,在特殊段中搜索符号的名字,如果找到,则将获得的地址填充在被加载模块的相应段中,这样符号的地址就可以确定。
使用这种方式处理未决符号,其实相当于把链接的过程推后,进行了动态链接,和普通的应用程序使用共享库函数的道理是类似的 。可以发现,内核将会有大量的符号导出,为模块提供了丰富的基础设施。
-
内核
+关注
关注
3文章
1362浏览量
40221 -
Linux
+关注
关注
87文章
11222浏览量
208886 -
函数
+关注
关注
3文章
4304浏览量
62416 -
编译
+关注
关注
0文章
653浏览量
32803
发布评论请先 登录
相关推荐
评论