Linux内核模块原理

  • 什么是Linux内核模块

模块是向内核添加新功能的一种方法,当需要为内核编写设备驱动程序,扩展内核功能时,就可以通过编写内核模块扩展内核,模块可以动态的加载和移除,所以无须重新编译内核或者重启操作系统,现在5.X内核源码和之前研究的2.6.X关于内核模块的改动比较大,所以基于当前最新的5.X内核源码重新研究下内核模块的实现。

  • 模块的内核表示

Module结构的成员较多,这里我提取了部分重要的成员注释说明

 

struct module {
	enum module_state state; //模块的状态

	//链接到内核模块链表的元素
	struct list_head list;

	//模块名称
	char name[MODULE_NAME_LEN];

    //导出到内核的模块符号
    const struct kernel_symbol *syms;
	const s32 *crcs;
	unsigned int num_syms;

    //模块核心代码在地址空间的位置信息
    struct module_layout core_layout __module_layout_align;

    //模块初始化代码地址信息
	struct module_layout init_layout;
    
    //模块初始化函数
    int (*init)(void);


    //模块是否会污染内核
    unsigned long taints;

    //本模块被其它模块依赖的链表
	struct list_head source_list;
	
    //本模块依赖哪些其它模块
	struct list_head target_list;

	//模块销毁时调用的函数
	void (*exit)(void);

    //模块被引用的次数
	atomic_t refcnt;
}
  • 插入内核模块过程

内核执行流程:

  1. 用户空间insmod发起加载模块系统调用
  2. init_module函数,它复制模块二进制代码到内核地址空间,初始化load_info结构体(包含模块二进制内容)

  3. load_module 检查模块有效性(elf头部检查、签名检查),解决引用、修复重定位项等

  4. do_init_module 设置module状态等信息,并执行模块初始化函数,至此模块已加载完毕

init_module说明

SYSCALL_DEFINE3(init_module, void __user *, umod,
		unsigned long, len, const char __user *, uargs)
{
	int err;
	struct load_info info = { };
	
	//如果禁止模块加载则返回错误代码并停止加载过程
	err = may_init_module();
	if (err)
		return err;

	
	pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n",
	       umod, len, uargs);

	//从用户空间拷贝模块二进制代码到内核空间
	err = copy_module_from_user(umod, len, &info);
	if (err)
		return err;

	//执行模块实际加载工作
	return load_module(&info, uargs, 0);
}

 

load_module说明

static int load_module(struct load_info *info, const char __user *uargs,
		       int flags)
{
	struct module *mod;
	long err = 0;
	char *after_dashes;

	 //模块签名验证
	err = module_sig_check(info, flags);
	if (err)
		goto free_copy;

	//检查ELF 头部和区段有效性
	err = elf_validity_check(info);
	if (err) {
		pr_err("Module has invalid ELF structures\n");
		goto free_copy;
	}

	 //设置模块结构各个区段在内核地址空间的位置
	err = setup_load_info(info, flags);
	if (err)
		goto free_copy;

	
	//检查模块是否在内核黑名单内,如果是则禁止加载
	if (blacklisted(info->name)) {
		err = -EPERM;
		pr_err("Module %s is blacklisted\n", info->name);
		goto free_copy;
	}

	//设置所有节区在内核临时内存地址空间的位置
	err = rewrite_section_headers(info, flags);
	if (err)
		goto free_copy;

	//检查模块版本
	if (!check_modstruct_version(info, info->mod)) {
		err = -ENOEXEC;
		goto free_copy;
	}

	//为内核模块分配内存
	mod = layout_and_allocate(info, flags);
	if (IS_ERR(mod)) {
		err = PTR_ERR(mod);
		goto free_copy;
	}

	audit_log_kern_module(mod->name);

	//判断模块是否已经加载,若没有则将模块添加到内核模块链表中
	err = add_unformed_module(mod);
	if (err)
		goto free_module;

#ifdef CONFIG_MODULE_SIG
	mod->sig_ok = info->sig_ok;
	if (!mod->sig_ok) {
		pr_notice_once("%s: module verification failed: signature "
			       "and/or required key missing - tainting "
			       "kernel\n", mod->name);
		add_taint_module(mod, TAINT_UNSIGNED_MODULE, LOCKDEP_STILL_OK);
	}
#endif

	/* To avoid stressing percpu allocator, do this once we're unique. */
	err = percpu_modalloc(mod, info);
	if (err)
		goto unlink_mod;

	//初始化模块依赖引用链表并增加引用计数
	err = module_unload_init(mod);
	if (err)
		goto unlink_mod;

	init_param_lock(mod);

	/*
	 * Now we've got everything in the final locations, we can
	 * find optional sections.
	 */
	err = find_module_sections(mod, info);
	if (err)
		goto free_unload;

	err = check_module_license_and_versions(mod);
	if (err)
		goto free_unload;

	/* Set up MODINFO_ATTR fields */
	setup_modinfo(mod, info);

	//修复模块未解决引用
	err = simplify_symbols(mod, info);
	if (err < 0)
		goto free_modinfo;

	//处理重定位
	err = apply_relocations(mod, info);
	if (err < 0)
		goto free_modinfo;

	err = post_relocation(mod, info);
	if (err < 0)
		goto free_modinfo;

	flush_module_icache(mod);

	//拷贝模块参数到module->args
	mod->args = strndup_user(uargs, ~0UL >> 1);
	if (IS_ERR(mod->args)) {
		err = PTR_ERR(mod->args);
		goto free_arch_cleanup;
	}

	dynamic_debug_setup(mod, info->debug, info->num_debug);

	/* Ftrace init must be called in the MODULE_STATE_UNFORMED state */
	ftrace_module_init(mod);

	//模块已准备完成,设置模块状态标识等
	err = complete_formation(mod, info);
	if (err)
		goto ddebug_cleanup;

	err = prepare_coming_module(mod);
	if (err)
		goto bug_cleanup;

	/* Module is ready to execute: parsing args may do that. */
	after_dashes = parse_args(mod->name, mod->args, mod->kp, mod->num_kp,
				  -32768, 32767, mod,
				  unknown_module_param_cb);
	if (IS_ERR(after_dashes)) {
		err = PTR_ERR(after_dashes);
		goto coming_cleanup;
	} else if (after_dashes) {
		pr_warn("%s: parameters '%s' after `--' ignored\n",
		       mod->name, after_dashes);
	}

	//链接到/sysfs文件系统
	err = mod_sysfs_setup(mod, info, mod->kp, mod->num_kp);
	if (err < 0)
		goto coming_cleanup;

	if (is_livepatch_module(mod)) {
		err = copy_module_elf(mod, info);
		if (err < 0)
			goto sysfs_cleanup;
	}

	//释放加载模块过程中分配的临时内存
	free_copy(info);

	/* Done! */
	trace_module_load(mod);

	return do_init_module(mod);

do_init_module说明

static noinline int do_init_module(struct module *mod)
{
	int ret = 0;
	struct mod_initfree *freeinit;

	//为内核初始化代码分配内存
	freeinit = kmalloc(sizeof(*freeinit), GFP_KERNEL);
	if (!freeinit) {
		ret = -ENOMEM;
		goto fail;
	}
	freeinit->module_init = mod->init_layout.base;

	/*
	 * We want to find out whether @mod uses async during init.  Clear
	 * PF_USED_ASYNC.  async_schedule*() will set it.
	 */
	current->flags &= ~PF_USED_ASYNC;

	do_mod_ctors(mod);
	
	//启动模块,如果存在初始化函数则执行它
	if (mod->init != NULL)
		ret = do_one_initcall(mod->init);
	if (ret < 0) {
		goto fail_free_freeinit;
	}
	if (ret > 0) {
		pr_warn("%s: '%s'->init suspiciously returned %d, it should "
			"follow 0/-E convention\n"
			"%s: loading module anyway...\n",
			__func__, mod->name, ret, __func__);
		dump_stack();
	}

	//设置模块运行标识
	mod->state = MODULE_STATE_LIVE;
	blocking_notifier_call_chain(&module_notify_list,
				     MODULE_STATE_LIVE, mod);

 

至此模块已加载到Linux内核,以后再分享将如何使用内核提供的模块机制来编写自己的内核模块,内核模块编写这块资料还是比较多,而且内核关于这块的变动不大,各个内核版本几乎是兼容的。

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值