内核模块(下)

简介: 内核模块(下)

模块信息

  • 初始化及其清理函数:<init.h>中的module_init和module_exit宏定义于定义init函数和exit函数
  • 导出符号:内核为导出符号提供两个宏:EXPORT_SYMBOL和EXPORT_SYMBOL_GPL。顾明思义,二者分别用于一般的号出符号和只用于GPL兼容代码的导出符号。同样,具目的在于将对应的符号放到模块二进制映象的适当段中。
/* For every exported symbol, place a struct in the __ksymtab section */
//导出符号
#define __EXPORT_SYMBOL(sym, sec)       \
  extern typeof(sym) sym;         \
  __CRC_SYMBOL(sym, sec)          \
  static const char __kstrtab_##sym[]     \
  __attribute__((section("__ksymtab_strings"), aligned(1))) \
  = VMLINUX_SYMBOL_STR(sym);        \
  extern const struct kernel_symbol __ksymtab_##sym;  \
  __visible const struct kernel_symbol __ksymtab_##sym  \
  __used              \
  __attribute__((section("___ksymtab" sec "+" #sym), unused)) \
  = { (unsigned long)&sym, __kstrtab_##sym }
#define EXPORT_SYMBOL(sym)          \
  __EXPORT_SYMBOL(sym, "")
#define EXPORT_SYMBOL_GPL(sym)          \
  __EXPORT_SYMBOL(sym, "_gpl")
#define EXPORT_SYMBOL_GPL_FUTURE(sym)       \
  __EXPORT_SYMBOL(sym, "_gpl_future")
#ifdef CONFIG_UNUSED_SYMBOLS
#define EXPORT_UNUSED_SYMBOL(sym) __EXPORT_SYMBOL(sym, "_unused")
#define EXPORT_UNUSED_SYMBOL_GPL(sym) __EXPORT_SYMBOL(sym, "_unused_gpl")
#else
#define EXPORT_UNUSED_SYMBOL(sym)
#define EXPORT_UNUSED_SYMBOL_GPL(sym)
#endif
#endif  /* __GENKSYMS__ */
  • 一般模块信息:模块许可证,开发者和描述,备选名称,基本版本控制
/* Generic info of form tag = "info" */
#define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info)
/* For userspace: you can also call me... */
#define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias)
/* Soft module dependencies. See man modprobe.d for details.
 * Example: MODULE_SOFTDEP("pre: module-foo module-bar post: module-baz")
 */
#define MODULE_SOFTDEP(_softdep) MODULE_INFO(softdep, _softdep)
/*
 * The following license idents are currently accepted as indicating free
 * software modules
 *
 *  "GPL"       [GNU Public License v2 or later]
 *  "GPL v2"      [GNU Public License v2]
 *  "GPL and additional rights" [GNU Public License v2 rights and more]
 *  "Dual BSD/GPL"      [GNU Public License v2
 *           or BSD license choice]
 *  "Dual MIT/GPL"      [GNU Public License v2
 *           or MIT license choice]
 *  "Dual MPL/GPL"      [GNU Public License v2
 *           or Mozilla license choice]
 *
 * The following other idents are available
 *
 *  "Proprietary"     [Non free products]
 *
 * There are dual licensed components, but when running with Linux it is the
 * GPL that is relevant so this is a non issue. Similarly LGPL linked with GPL
 * is a GPL combined work.
 *
 * This exists for several reasons
 * 1. So modinfo can show license info for users wanting to vet their setup
 *  is free
 * 2. So the community can ignore bug reports including proprietary modules
 * 3. So vendors can do likewise based on their own policies
 */
#define MODULE_LICENSE(_license) MODULE_INFO(license, _license)
/*
 * Author(s), use "Name <email>" or just "Name", for multiple
 * authors use multiple MODULE_AUTHOR() statements/lines.
 */
#define MODULE_AUTHOR(_author) MODULE_INFO(author, _author)
/* What your module does. */
#define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description)
#ifdef MODULE
/* Creates an alias so file2alias.c can find device table. */
#define MODULE_DEVICE_TABLE(type, name)         \
extern const typeof(name) __mod_##type##__##name##_device_table   \
  __attribute__ ((unused, alias(__stringify(name))))
#else  /* !MODULE */
#define MODULE_DEVICE_TABLE(type, name)
#endif

内核与模块存储字符串匹配成功时,模块才能加载。

SMP配置(是否启用);抢占配置(是否启用);使用编译器版本;特定于体系结构的常数。

插入模块

init_module系统调用是用户空间和内孩之间用于装载新模块的接口,通过load_module函数将二进制数据传输到内核地址空间中,具体源码如下:

/* Allocate and load the module: note that size of section 0 is always
   zero, and we rely on this for optional sections. */
//二进制数据使用load_module传输到内核地址空间中,锁有需要的重定位都会完成,所有iny都会解决
//在load_module中创建module实例已近添加到全局的modues链表后,内核内需要调用模块的初始化函数并释放
//初始化数据占用的内存空间
static int load_module(struct load_info *info, const char __user *uargs,
           int flags)
{
  struct module *mod;
  long err;
  char *after_dashes;
  err = module_sig_check(info);
  if (err)
    goto free_copy;
  err = elf_header_check(info);
  if (err)
    goto free_copy;
  /* Figure out module layout, and allocate all the memory. */
  mod = layout_and_allocate(info, flags);
  if (IS_ERR(mod)) {
    err = PTR_ERR(mod);
    goto free_copy;
  }
  /* Reserve our place in the list. */
  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;
  /* Now module is in final location, initialize linked lists, etc. */
  err = module_unload_init(mod);
  if (err)
    goto unlink_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);
  /* Fix up syms, so that st_value is a pointer to location. */
  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);
  /* Now copy in 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(info->debug, info->num_debug);
  /* Ftrace init must be called in the MODULE_STATE_UNFORMED state */
  ftrace_module_init(mod);
  /* Finally it's fully formed, ready to start executing. */
  err = complete_formation(mod, info);
  if (err)
    goto ddebug_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, unknown_module_param_cb);
  if (IS_ERR(after_dashes)) {
    err = PTR_ERR(after_dashes);
    goto bug_cleanup;
  } else if (after_dashes) {
    pr_warn("%s: parameters '%s' after `--' ignored\n",
           mod->name, after_dashes);
  }
  /* Link in to syfs. */
  err = mod_sysfs_setup(mod, info, mod->kp, mod->num_kp);
  if (err < 0)
    goto bug_cleanup;
  /* Get rid of temporary copy. */
  free_copy(info);
  /* Done! */
  trace_module_load(mod);
  return do_init_module(mod);
 bug_cleanup:
  /* module_bug_cleanup needs module_mutex protection */
  mutex_lock(&module_mutex);
  module_bug_cleanup(mod);
  mutex_unlock(&module_mutex);
  blocking_notifier_call_chain(&module_notify_list,
             MODULE_STATE_GOING, mod);
  /* we can't deallocate the module until we clear memory protection */
  unset_module_init_ro_nx(mod);
  unset_module_core_ro_nx(mod);
 ddebug_cleanup:
  dynamic_debug_remove(info->debug);
  synchronize_sched();
  kfree(mod->args);
 free_arch_cleanup:
  module_arch_cleanup(mod);
 free_modinfo:
  free_modinfo(mod);
 free_unload:
  module_unload_free(mod);
 unlink_mod:
  mutex_lock(&module_mutex);
  /* Unlink carefully: kallsyms could be walking list. */
  list_del_rcu(&mod->list);
  wake_up_all(&module_wq);
  /* Wait for RCU synchronizing before releasing mod->list. */
  synchronize_rcu();
  mutex_unlock(&module_mutex);
 free_module:
  /* Free lock-classes; relies on the preceding sync_rcu() */
  lockdep_free_key_range(mod->module_core, mod->core_size);
  module_deallocate(mod, info);
 free_copy:
  free_copy(info);
  return err;
}

在实现load_module时会出现异常,内核源代码中该函数:完成所有碰到异常问题,此函数可以完成任务如下:

  • 从用户空间复制模块数据到内核地址空间中的一个临时内存位置
  • 查找各个段位置
  • 确保内核和模块版本控制字符串和struct module的定义匹配问题
  • 将存在的各个段分配到其在内存中的最终位置
  • 重定位符号并解决引用,链接到模块符号任何版本控制信息的都会注意到
  • 处理模块的参数。

删除模块

从内垓移除块比入模块简单得多,系统调delete_module函数来实现移除模块,具体源码如下:

asmlinkage long sys_init_module(void __user *umod, unsigned long len,
        const char __user *uargs);
asmlinkage long sys_delete_module(const char __user *name_user,
        unsigned int flags);
目录
相关文章
|
3月前
|
安全 Linux 开发者
在Linux中,内核模块是什么以及如何加载和卸载它们?
在Linux中,内核模块是什么以及如何加载和卸载它们?
|
6月前
|
Linux
探索Linux操作系统的内核模块
本文将深入探讨Linux操作系统的核心组成部分——内核模块,揭示其背后的工作机制和实现方式。我们将从内核模块的定义开始,逐步解析其加载、卸载以及与操作系统其他部分的交互过程,最后探讨内核模块在系统性能优化中的关键作用。
|
存储 Linux 调度
Linux内核子系统 内核配置选项
Linux内核子系统 内核配置选项
|
编译器 Linux 开发者
内核模块(上)
内核模块
108 0
|
Linux KVM 虚拟化
Linux内核模块
在模块A编译好后会生成符号表文件Module.symvers, 里面有函数地址和函数名对应关系,把这个文件拷贝到需要调用的模块B的源代码下,替换模块B的该文件。 然后重新编译B模块.这样就能够让模块B调用模块A的函数,以后加载模块顺序也必须先A后B,卸载相反。
Linux内核模块
|
物联网 Linux 开发者
内核模块2 | 学习笔记
快速学习模块编写2
内核模块2 | 学习笔记
|
网络协议 物联网 Linux
内核模块4 | 学习笔记
快速学习内核模块4
内核模块4 | 学习笔记
|
Ubuntu 物联网 编译器
内核模块3 | 学习笔记
快速学习内核模块3
内核模块3 | 学习笔记
|
Linux
内核模块-实现一个简单的设备
上一篇文章讲了如何实现基于内核模块的“helloworld”,相信大家通过这个例子对于内核模块有了一个基本的了解。当然,内核模块绝不仅仅只能实现这点功能,其最大的应用就是实现硬件的驱动程序。其实,linux内核中很大一部代码都是硬件处理相关的,比如,设备-总线-驱动框架,USB框架、spi框架、i2c框架等等,对应于各种不同的硬件设备,相应的就会有设备驱动程序,从最简单的按键、LED驱动,到十分复杂的USB子系统驱动,可以好不夸张的说,Linux内核可以适配绝大多数的硬件设备。
177 3
|
缓存 IDE Linux
16.4 Linux内核(内核模块)的加载
GRUB 加载了内核之后,内核首先会再进行二次系统的自检,而不一定使用 BIOS 检测的硬件信息。这时内核终于开始替代 BIOS 接管 Linux 的启动过程了。
226 0
16.4 Linux内核(内核模块)的加载