话说Linux内核模块

首先,一个内核模块,就是一个ELF文件。

内核本身也是一个ELF文件。

一个模块,可能是一个协议,一个文件系统,一个驱动,一种具体的应用,等等任何东西。也可能是多种东西的组合。

内核在运行时,通过动态加载/卸载内核模块,可以动态扩展/删减内核的功能。


每个模块在编译时(假设模块名为xxx),会自动生成一个xxx.mod.c的文件,该文件会链接到xxx模块中去。

下面是此文件的一个示例:

#include <linux/module.h>
#include <linux/vermagic.h>
#include <linux/compiler.h>

MODULE_INFO(vermagic, VERMAGIC_STRING);

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,
};

static const char __module_depends[]
__used
__attribute__((section(".modinfo"))) =
"depends=";


MODULE_INFO(srcversion, "0FE0F65AABA6F6A780CD6F7");

__this_module.init指向模块的初始化函数init_module,在模块被加载后,此函数即被内核调用。

__this_module.exit = cleanup_module指向模块的清理函数cleanup_module,在模块被卸载时,此函数即被内核调用。

模块通过如下方式声明初始化函数及清理。

module_init(xxx_init);

module_exit(xxx_exit);

这个__this_module.init中指向的函数名称不同,为什么呢?

来看看如下module_init及module_exit的定义就知道了。

可见,module_init及module_exit为传入的函数,分别定义了一个别名,即init_module和cleanup_module。

/* Each module must use one module_init(). */
#define module_init(initfn)					\
	static inline initcall_t __inittest(void)		\
	{ return initfn; }					\
	int init_module(void) __attribute__((alias(#initfn)));

/* This is only required if you want to be unloadable. */
#define module_exit(exitfn)					\
	static inline exitcall_t __exittest(void)		\
	{ return exitfn; }					\
	void cleanup_module(void) __attribute__((alias(#exitfn)));


struct module中还包含了一些别的内容,包括模块名称,引用计数等。其中,引用计数一般是通过try_module_get增加的。
这个引用计数通常会影响模块的卸载。
sys_delete_module中会通过module_refcount获取要卸载的模块的引用计数。

要想让引用计数能正常工作,我们编写模块代码时,通常是在某个结构体的赋值代码中,加入如下一行。取决于模块的功能类型,这个结构体可能是file_operations、platform_driver、proto、proto_ops等。

.owner = THIS_MODULE

以下情形会影响模块的引用计数。
模块间的依赖关系。例如,模块A依赖B。A加载时,内核要解析A引用的外部符号。见module.c中的resolve_symbol。如果发现A引用的某个外部符号在B中,就调用use_module,记录A对B的依赖。use_module会增加B的引用计数,同时将A加入到B的使用者列表中去。
各种业务流程方面也会导致引用计数变化,好在内核框架代码已经为我们做好了这一切。
例如,当用户调用C库函数open打开一个文件时,__dentry_open中会调用fops_get获取要打开的文件的file_operations。而fops_get的定义如下,这就实现了对目标模块的引用计数的增加。
#define fops_get(fops) \
(((fops) && try_module_get((fops)->owner) ? (fops) : NULL))

再如,当用户创建一个socket时,__sock_create函数内会有如下代码。对实现此protocol family的模块,增加引用计数。

/*
 * We will call the ->create function, that possibly is in a loadable
 * module, so we have to bump that loadable module refcnt first.
 */
  if (!try_module_get(pf->owner))
      goto out_release;




看到没有,这些业务流程,都使用了owner成员。

所以,编写模块代码时,要是把“.owner  = THIS_MODULE”这句话漏了可就歇菜了 ^_^




再来观察__module_depends的定义,可见推测其内容是在加载时动态生成。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值