使用模块

依赖关系

modutils标准工具集中的depmod工具可用于计算系统的各个模块之间的依赖关系。每次系统启动时或新模块安装后,通常都会运行该程序。找到的依赖关系保存在一个列表中。默认情况下,写入文件/lib/modules/version/modules.dep。格式:首先是目标模块的二进制文件名称,接下来是为正确执行目标模块,包含了所需代码的所有模块的文件名。

depmod分析所有可用的模块的二进制代码,对每个模块建立一个列表,包含所有已定义符号和未解决的引用,最后将各个模块的列表彼此进行比较。如果模块A包含的一个符号在模块B中是未解决的引用,则意味着模块B依赖模块A,接下来在依赖文件中以B:A的形式增加一项,即确认了上述事实。模块引用的大多数符号都定义在内核中,而不是定义在其他模块中。因此,在模块安装时产生了文件/lib/modules/version/System.map(同样使用depmod)。该文件列出了内核导出的所有符号。如果其中包含了某个模块中未解决的引用,那么该引用就不成问题了,在模块装载时引用将自动解决。如果未解决的引用无法在该文件或其他模块中找到,则模块不能添加到内核中,因为其中引用了外部函数,而又找不到实现。

自动加载

附加到每个模块一个小“数据库”。数据库的内容描述了该模块所支持的设备。数据库信息通过模块别名提供。模块别名是解决自动装载模块问题的基础。

插入和删除模块

模块的表示

struct module
{
    enum module_state state;//模块状态,在装载期间,状态是MODULE_STATE_COMING。在正常运行时,状态是MODULE_STATE_LIVE。在模块正在移除时,状态为MODULE_STATE_GOING。

    /* Member of list of modules */
    struct list_head list;//将所有加载模块保存到一个双链表中(表头为modules)

    /* Unique handle for this module */
    char name[MODULE_NAME_LEN];//该模块的名称,必须唯一

    /* Sysfs stuff. */
    struct module_kobject mkobj;
    struct module_param_attrs *param_attrs;
    struct module_attribute *modinfo_attrs;
    const char *version;
    const char *srcversion;
    struct kobject *holders_dir;

    /* Exported symbols */
    //导出的符号
    const struct kernel_symbol *syms;
    unsigned int num_syms;
    const unsigned long *crcs;

    /* GPL-only exported symbols. */
    //只适用于GPL的导出符号
    const struct kernel_symbol *gpl_syms;
    unsigned int num_gpl_syms;
    const unsigned long *gpl_crcs;

    /* unused exported symbols. */
    const struct kernel_symbol *unused_syms;
    unsigned int num_unused_syms;
    const unsigned long *unused_crcs;
    /* GPL-only, unused exported symbols. */
    const struct kernel_symbol *unused_gpl_syms;
    unsigned int num_unused_gpl_syms;
    const unsigned long *unused_gpl_crcs;

    /* symbols that will be GPL-only in the near future. */
    //用于将来只提供给GPL的导出符号
    const struct kernel_symbol *gpl_future_syms;
    unsigned int num_gpl_future_syms;
    const unsigned long *gpl_future_crcs;

    /* Exception table */
    //异常表
    unsigned int num_exentries;
    const struct exception_table_entry *extable;

    /* Startup function. */
    //在模块初始化时调用的函数
    int (*init)(void);

    /* If this is non-NULL, vfree after init() returns */
    void *module_init;//初始化使用的部分,在初始化完成后,可以丢弃 

    /* Here is the actual code + data, vfree'd on unload. */
    //核心数据与代码
    void *module_core;

    /* Here are the sizes of the init and core sections */
    unsigned long init_size, core_size;

    /* The size of the executable code in each section.  */
    unsigned long init_text_size, core_text_size;

    /* The handle returned from unwind_add_table. */
    void *unwind_info;

    /* Arch-specific module values */
    struct mod_arch_specific arch;

    unsigned int taints;    /* same bits as kernel:tainted *///如果模块会污染内核,则设置taints

#ifdef CONFIG_GENERIC_BUG
    /* Support for BUG */
    struct list_head bug_list;
    struct bug_entry *bug_table;
    unsigned num_bugs;
#endif

#ifdef CONFIG_MODULE_UNLOAD
    /* Reference counts */
    struct module_ref ref[NR_CPUS];//引用计数

    /* What modules depend on me? */
    struct list_head modules_which_use_me;//依赖当前模块的模块

    /* Who is waiting for us to be unloaded */
    struct task_struct *waiter;//等待当前模块卸载的进程

    /* Destruction function. */
    void (*exit)(void);//析构函数
#endif

#ifdef CONFIG_KALLSYMS
    /* We keep the symbol and string tables for kallsyms. */
    Elf_Sym *symtab;//用于记录模块的所有符号信息
    unsigned long num_symtab;
    char *strtab;

    /* Section attributes */
    struct module_sect_attrs *sect_attrs;

    /* Notes attributes */
    struct module_notes_attrs *notes_attrs;
#endif

    /* Per-cpu data. */
    void *percpu;

    /* The command line arguments (may be mangled).  People like
       keeping pointers to this stuff */
    char *args;//指向装载期间传递给模块的命令行参数
#ifdef CONFIG_MARKERS
    struct marker *markers;
    unsigned int num_markers;
#endif
};

依赖关系和引用

为了正确管理依赖关系,内核需要引入另一个数据结构:

struct module_use
{
    struct list_head list;
    struct module *module_which_uses;
};

依赖关系的网络通过module_use和module数据结构的modules_which_use_me成员共同建立起来。对于每个使用了模块A中函数的模块B,都会创建一个module_use的新实例,该实例将添加到模块A的module实例中的modules_which_use_me链表。module_which_uses指向模块B的module实例。

内核提供了already_uses函数,来判断模块A是否需要另一个模块B(遍历模块B的modules_which_use_me链表)

use_module用于建立模块A和模块B之间的关系。

模块的二进制结构

模块使用ELF二进制格式,模块中包含了几个额外的段,普通的程序或库中不会出现。

  • __ksymtab、__ksymtab_gpl和__ksymtab_gpl_future段包含一个符号表,包括了模块导出的所有符号。__ksymtab段中导出的符号可以由内核的所有部分所用(不考虑许可证),__kysmtab_gpl中的符号只能由GPL兼容的部分使用,而__ksymtab_gpl_future中的符号未 来只能由GPL兼容的部分使用。
  • __kcrctab、__kcrctab_gpl和__kcrctab_gpl_future包含模块所有(只适用于GPL、或未来只适用于GPL)导出函数的校验和。__versions包含该模块使用的、来自于外部源代码的 所有引用的校验和。
  • __param存储了模块可接受的参数有关信息。
  • __ex_table用于为内核异常表定义新项,前提是模块代码需要使用该机制。
  • .modinfo存储了在加载当前模块之前,内核中必须先行加载的所有其他模块名称。换句话说, 该特定模块依赖的所有模块名称。此外,每个模块都可以保存一些特定的信息,可以使用用户空间工具modinfo查询,特别是开 发者的名字、模块的描述、许可证信息和参数列表。
  • .exit.text包含了在该模块从内核移除时,所需使用的代码(和可能的数据)。该信息并未保存在普通的代码段中,这样,如果内核配置中未启用移除模块的选项,就不必将该段载入 内存。
  • 初始化函数(和数据)保存在.init.text段。之所以使用一个独立的段,是因为初始化完成 后,相关的代码和数据就不再需要,因而可以从内存移除。
  • .gnu.linkonce.this_module提供了struct module的一个实例,其中存储了模块的名称(name)和指向二进制文件中的初始化函数和清理函数(init和cleanup)的指针。根据本段,内核即可判断特定的二进制文件是否为模块。如果没有该段,则拒绝装载文件。

模块的初始化函数和清理函数,保存在.gnu.linkonce.module段中的module实例中。该实例位于上述为每个模块自动生成的附加文件中。

内核为导出符号提供了两个宏:EXPORT_SYMBOL和EXPORT_SYMBOL_GPL。其目的在于将相应的符号放置到模块二进制映象的适当段中。

MODULE_ALIAS(alias)用于给模块指定备选名称(alias),在用户空间中可据此访问模块。

.modinfo段中总是会存储某些必不可少的版本控制信息,无论内核的版本控制特性是否启用。

插入模块

init_module系统调用是用户空间和内核之间用于装载新模块的接口。

asmlinkage long
sys_init_module(void __user *umod,unsigned long len,const char __user *uargs);//umod指向模块,len指定长度,uargs指定模块的参数

二进制数据使用load_module传输到内核地址空间中。所有需要的重定位都会完成,所有的引用都会解决。参数转换为一种易于分析的形式(kernel_param)实例的表,用模块的所有必要信息创建module数据结构的一个实例。

在load_module函数中创建的modue实例都已经添加到全局的modules链表后,内核只需要调用模块的初始化函数并释放初始化数据占用的内存。

加载模块

load_module:

  • 从用户空间复制模块数据到内核地址空间中的一个临时内存位置。各ELF段的相对地址替换为该临时映像的绝对地址。
  • 查找各个段的位置。
  • 确保内核和模块中版本控制字符串和struct module的定义匹配。
  • 将存中的各个段分配到其在内存中的最终位置。
  • 重定位符号并解决引用。链接到模块符号的任何版本控制信息都会被注意到。
  • 处理模块的参数。
static struct module *load_module(void __user *umod,
                  unsigned long len,
                  const char __user *uargs)
{
    Elf_Ehdr *hdr;
    Elf_Shdr *sechdrs;
    char *secstrings, *args, *modmagic, *strtab = NULL;
    unsigned int i;
    unsigned int symindex = 0;
    unsigned int strindex = 0;
    unsigned int setupindex;
    unsigned int exindex;
    unsigned int exportindex;
    unsigned int modindex;
    unsigned int obsparmindex;
    unsigned int infoindex;
    unsigned int gplindex;
    unsigned int crcindex;
    unsigned int gplcrcindex;
    unsigned int versindex;
    unsigned int pcpuindex;
    unsigned int gplfutureindex;
    unsigned int gplfuturecrcindex;
    unsigned int unwindex = 0;
    unsigned int unusedindex;
    unsigned int unusedcrcindex;
    unsigned int unusedgplindex;
    unsigned int unusedgplcrcindex;
    unsigned int markersindex;
    unsigned int markersstringsindex;
    struct module *mod;
    long err = 0;
    void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
    struct exception_table_entry *extable;
    mm_segment_t old_fs;

    DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
           umod, len, uargs);
    if (len < sizeof(*hdr))
        return ERR_PTR(-ENOEXEC);

    /* Suck in entire file: we'll want most of it. */
    /* vmalloc barfs on "unusual" numbers.  Check here */
    if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL)
        return ERR_PTR(-ENOMEM);
    if (copy_from_user(hdr, umod, len) != 0) {//将模块的二进制数据载入内核内存
        err = -EFAULT;
        goto free_hdr;
    }

    /* Sanity checks against insmoding binaries or wrong arch,
           weird elf version */
    if (memcmp(hdr->e_ident, ELFMAG, 4) != 0
        || hdr->e_type != ET_REL
        || !elf_check_arch(hdr)
        || hdr->e_shentsize != sizeof(*sechdrs)) {
        err = -ENOEXEC;
        goto free_hdr;
    }

    if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr))
        goto truncated;

    /* Convenience variables */
    sechdrs = (void *)hdr + hdr->e_shoff;//指向二进制数据中各个存在的ELF段的相关信息(段表)
    secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;//包含段名称的字符串表在内存中的位置
    sechdrs[0].sh_addr = 0;

    for (i = 1; i < hdr->e_shnum; i++) {
        if (sechdrs[i].sh_type != SHT_NOBITS
            && len < sechdrs[i].sh_offset + sechdrs[i].sh_size)
            goto truncated;

        /* Mark all sections sh_addr with their address in the
           temporary image. */
        sechdrs[i].sh_addr = (size_t)hdr + sechdrs[i].sh_offset;//二进制代码中引用的所有段的地址改写为对应段在临时映像中的绝对地址

        /* Internal symbols and strings. */
        if (sechdrs[i].sh_type == SHT_SYMTAB) {//找到符号表
            symindex = i;
            strindex = sechdrs[i].sh_link;
            strtab = (char *)hdr + sechdrs[strindex].sh_offset;
        }
#ifndef CONFIG_MODULE_UNLOAD
        /* Don't load .exit sections */
        if (strncmp(secstrings+sechdrs[i].sh_name, ".exit", 5) == 0)
            sechdrs[i].sh_flags &= ~(unsigned long)SHF_ALLOC;
#endif
    }

    modindex = find_sec(hdr, sechdrs, secstrings,
                ".gnu.linkonce.this_module");
    if (!modindex) {
        printk(KERN_WARNING "No module found in object\n");
        err = -ENOEXEC;
        goto free_hdr;
    }
    mod = (void *)sechdrs[modindex].sh_addr;//在.gnu.linkonce.this_module段中,struct module的实例,该实例提供了模块的名称和指向初始化以及清理函数的指针,但其他成员仍然初始化为0

    if (symindex == 0) {
        printk(KERN_WARNING "%s: module has no symbols (stripped?)\n",
               mod->name);
        err = -ENOEXEC;
        goto free_hdr;
    }

    /* Optional sections */
    exportindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab");
    gplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl");
    gplfutureindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl_future");
    unusedindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused");
    unusedgplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused_gpl");
    crcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab");
    gplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl");
    gplfuturecrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl_future");
    unusedcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_unused");
    unusedgplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_unused_gpl");
    setupindex = find_sec(hdr, sechdrs, secstrings, "__param");
    exindex = find_sec(hdr, sechdrs, secstrings, "__ex_table");
    obsparmindex = find_sec(hdr, sechdrs, secstrings, "__obsparm");
    versindex = find_sec(hdr, sechdrs, secstrings, "__versions");
    infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo");
    pcpuindex = find_pcpusec(hdr, sechdrs, secstrings);
#ifdef ARCH_UNWIND_SECTION_NAME
    unwindex = find_sec(hdr, sechdrs, secstrings, ARCH_UNWIND_SECTION_NAME);
#endif

    /* Don't keep modinfo section */
    sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
#ifdef CONFIG_KALLSYMS
    /* Keep symbol and string tables for decoding later. */
    sechdrs[symindex].sh_flags |= SHF_ALLOC;
    sechdrs[strindex].sh_flags |= SHF_ALLOC;
#endif
    if (unwindex)
        sechdrs[unwindex].sh_flags |= SHF_ALLOC;

    /* Check module struct version now, before we try to use module. */
    if (!check_modstruct_version(sechdrs, versindex, mod)) {
        err = -ENOEXEC;
        goto free_hdr;
    }

    modmagic = get_modinfo(sechdrs, infoindex, "vermagic");
    /* This is allowed: modprobe --force will invalidate it. */
    if (!modmagic) {
        add_taint_module(mod, TAINT_FORCED_MODULE);
        printk(KERN_WARNING "%s: no version magic, tainting kernel.\n",
               mod->name);
    } else if (!same_magic(modmagic, vermagic)) {
        printk(KERN_ERR "%s: version magic '%s' should be '%s'\n",
               mod->name, modmagic, vermagic);
        err = -ENOEXEC;
        goto free_hdr;
    }

    /* Now copy in args */
    args = strndup_user(uargs, ~0UL >> 1);
    if (IS_ERR(args)) {
        err = PTR_ERR(args);
        goto free_hdr;
    }

    if (find_module(mod->name)) {
        err = -EEXIST;
        goto free_mod;
    }

    mod->state = MODULE_STATE_COMING;

    /* Allow arches to frob section contents and sizes.  */
    err = module_frob_arch_sections(hdr, sechdrs, secstrings, mod);
    if (err < 0)
        goto free_mod;

    if (pcpuindex) {
        /* We have a special allocation for this section. */
        percpu = percpu_modalloc(sechdrs[pcpuindex].sh_size,
                     sechdrs[pcpuindex].sh_addralign,
                     mod->name);
        if (!percpu) {
            err = -ENOMEM;
            goto free_mod;
        }
        sechdrs[pcpuindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
        mod->percpu = percpu;
    }

    /* Determine total sizes, and put offsets in sh_entsize.  For now
       this is done generically; there doesn't appear to be any
       special cases for the architectures. */
    layout_sections(mod, hdr, sechdrs, secstrings);//用于判断模块的那些段装载到内存的那些位置,或那列段必须从其临时地址复制到其它位置

    /* Do the allocs. *///内存分配
    ptr = module_alloc(mod->core_size);//module_alloc是一个特定于体系结构的函数,用于分配模块内存。大多数情况下,它通过直接调用vmalloc或其变体之一实现,换句话说,模块在内存中驻留的内存区域通常是通过页表映射,并非直接映射。
    if (!ptr) {
        err = -ENOMEM;
        goto free_percpu;
    }
    memset(ptr, 0, mod->core_size);
    mod->module_core = ptr;

    ptr = module_alloc(mod->init_size);
    if (!ptr && mod->init_size) {
        err = -ENOMEM;
        goto free_core;
    }
    memset(ptr, 0, mod->init_size);
    mod->module_init = ptr;

    /* Transfer each section which specifies SHF_ALLOC */
    DEBUGP("final section addresses:\n");
    for (i = 0; i < hdr->e_shnum; i++) {//复制数据到最终的位置
        void *dest;

        if (!(sechdrs[i].sh_flags & SHF_ALLOC))
            continue;

        if (sechdrs[i].sh_entsize & INIT_OFFSET_MASK)
            dest = mod->module_init
                + (sechdrs[i].sh_entsize & ~INIT_OFFSET_MASK);
        else
            dest = mod->module_core + sechdrs[i].sh_entsize;

        if (sechdrs[i].sh_type != SHT_NOBITS)
            memcpy(dest, (void *)sechdrs[i].sh_addr,
                   sechdrs[i].sh_size);
        /* Update sh_addr to point to copy in image. */
        sechdrs[i].sh_addr = (unsigned long)dest;
        DEBUGP("\t0x%lx %s\n", sechdrs[i].sh_addr, secstrings + sechdrs[i].sh_name);
    }
    /* Module has been moved. */
    mod = (void *)sechdrs[modindex].sh_addr;

    /* Now we've moved module, initialize linked lists, etc. */
    module_unload_init(mod);

    /* Initialize kobject, so we can reference it. */
    err = mod_sysfs_init(mod);
    if (err)
        goto cleanup;

    /* Set up license info based on the info section */
    set_license(mod, get_modinfo(sechdrs, infoindex, "license"));//查询模块许可证

    if (strcmp(mod->name, "ndiswrapper") == 0)
        add_taint(TAINT_PROPRIETARY_MODULE);
    if (strcmp(mod->name, "driverloader") == 0)
        add_taint_module(mod, TAINT_PROPRIETARY_MODULE);

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

    /* Fix up syms, so that st_value is a pointer to location. */
    err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex,
                   mod);//处理模块符号,解决引用和重定位
    if (err < 0)
        goto cleanup;

    /* Set up EXPORTed & EXPORT_GPLed symbols (section 0 is 0 length) */
    mod->num_syms = sechdrs[exportindex].sh_size / sizeof(*mod->syms);
    mod->syms = (void *)sechdrs[exportindex].sh_addr;
    if (crcindex)
        mod->crcs = (void *)sechdrs[crcindex].sh_addr;
    mod->num_gpl_syms = sechdrs[gplindex].sh_size / sizeof(*mod->gpl_syms);
    mod->gpl_syms = (void *)sechdrs[gplindex].sh_addr;
    if (gplcrcindex)
        mod->gpl_crcs = (void *)sechdrs[gplcrcindex].sh_addr;
    mod->num_gpl_future_syms = sechdrs[gplfutureindex].sh_size /
                    sizeof(*mod->gpl_future_syms);
    mod->num_unused_syms = sechdrs[unusedindex].sh_size /
                    sizeof(*mod->unused_syms);
    mod->num_unused_gpl_syms = sechdrs[unusedgplindex].sh_size /
                    sizeof(*mod->unused_gpl_syms);
    mod->gpl_future_syms = (void *)sechdrs[gplfutureindex].sh_addr;
    if (gplfuturecrcindex)
        mod->gpl_future_crcs = (void *)sechdrs[gplfuturecrcindex].sh_addr;

    mod->unused_syms = (void *)sechdrs[unusedindex].sh_addr;
    if (unusedcrcindex)
        mod->unused_crcs = (void *)sechdrs[unusedcrcindex].sh_addr;
    mod->unused_gpl_syms = (void *)sechdrs[unusedgplindex].sh_addr;
    if (unusedgplcrcindex)
        mod->unused_crcs = (void *)sechdrs[unusedgplcrcindex].sh_addr;

#ifdef CONFIG_MODVERSIONS
    if ((mod->num_syms && !crcindex) ||
        (mod->num_gpl_syms && !gplcrcindex) ||
        (mod->num_gpl_future_syms && !gplfuturecrcindex) ||
        (mod->num_unused_syms && !unusedcrcindex) ||
        (mod->num_unused_gpl_syms && !unusedgplcrcindex)) {
        printk(KERN_WARNING "%s: No versions for exported symbols."
               " Tainting kernel.\n", mod->name);
        add_taint_module(mod, TAINT_FORCED_MODULE);
    }
#endif
    markersindex = find_sec(hdr, sechdrs, secstrings, "__markers");
    markersstringsindex = find_sec(hdr, sechdrs, secstrings,
                    "__markers_strings");

    /* Now do relocations. */
    for (i = 1; i < hdr->e_shnum; i++) {
        const char *strtab = (char *)sechdrs[strindex].sh_addr;
        unsigned int info = sechdrs[i].sh_info;

        /* Not a valid relocation section? */
        if (info >= hdr->e_shnum)
            continue;

        /* Don't bother with non-allocated sections */
        if (!(sechdrs[info].sh_flags & SHF_ALLOC))
            continue;

        if (sechdrs[i].sh_type == SHT_REL)
            err = apply_relocate(sechdrs, strtab, symindex, i,mod);
        else if (sechdrs[i].sh_type == SHT_RELA)
            err = apply_relocate_add(sechdrs, strtab, symindex, i,
                         mod);
        if (err < 0)
            goto cleanup;
    }
#ifdef CONFIG_MARKERS
    mod->markers = (void *)sechdrs[markersindex].sh_addr;
    mod->num_markers =
        sechdrs[markersindex].sh_size / sizeof(*mod->markers);
#endif

        /* Find duplicate symbols */
    err = verify_export_symbols(mod);

    if (err < 0)
        goto cleanup;

    /* Set up and sort exception table */
    mod->num_exentries = sechdrs[exindex].sh_size / sizeof(*mod->extable);
    mod->extable = extable = (void *)sechdrs[exindex].sh_addr;
    sort_extable(extable, extable + mod->num_exentries);

    /* Finally, copy percpu area over. */
    percpu_modcopy(mod->percpu, (void *)sechdrs[pcpuindex].sh_addr,
               sechdrs[pcpuindex].sh_size);

    add_kallsyms(mod, sechdrs, symindex, strindex, secstrings);

#ifdef CONFIG_MARKERS
    if (!mod->taints)
        marker_update_probe_range(mod->markers,
            mod->markers + mod->num_markers, NULL, NULL);
#endif
    err = module_finalize(hdr, sechdrs, mod);
    if (err < 0)
        goto cleanup;

    /* flush the icache in correct context */
    old_fs = get_fs();
    set_fs(KERNEL_DS);

    /*
     * Flush the instruction cache, since we've played with text.
     * Do it before processing of module parameters, so the module
     * can provide parameter accessor functions of its own.
     */
    if (mod->module_init)
        flush_icache_range((unsigned long)mod->module_init,
                   (unsigned long)mod->module_init
                   + mod->init_size);
    flush_icache_range((unsigned long)mod->module_core,
               (unsigned long)mod->module_core + mod->core_size);

    set_fs(old_fs);

    mod->args = args;
    if (obsparmindex)
        printk(KERN_WARNING "%s: Ignoring obsolete parameters\n",
               mod->name);

    /* Size of section 0 is 0, so this works well if no params */
    err = parse_args(mod->name, mod->args,
             (struct kernel_param *)
             sechdrs[setupindex].sh_addr,
             sechdrs[setupindex].sh_size
             / sizeof(struct kernel_param),
             NULL);
    if (err < 0)
        goto arch_cleanup;

    err = mod_sysfs_setup(mod,
                  (struct kernel_param *)
                  sechdrs[setupindex].sh_addr,
                  sechdrs[setupindex].sh_size
                  / sizeof(struct kernel_param));
    if (err < 0)
        goto arch_cleanup;
    add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs);
    add_notes_attrs(mod, hdr->e_shnum, secstrings, sechdrs);

    /* Size of section 0 is 0, so this works well if no unwind info. */
    mod->unwind_info = unwind_add_table(mod,
                        (void *)sechdrs[unwindex].sh_addr,
                        sechdrs[unwindex].sh_size);

    /* Get rid of temporary copy */
    vfree(hdr);

    /* Done! */
    return mod;

 arch_cleanup:
    module_arch_cleanup(mod);
 cleanup:
    module_unload_free(mod);
    module_free(mod, mod->module_init);
 free_core:
    module_free(mod, mod->module_core);
 free_percpu:
    if (percpu)
        percpu_modfree(percpu);
 free_mod:
    kfree(args);
 free_hdr:
    vfree(hdr);
    return ERR_PTR(err);

 truncated:
    printk(KERN_ERR "Module len %lu truncated\n", len);
    err = -ENOEXEC;
    goto free_hdr;
}

移除模块

sys_delete_module

asmlinkage long
sys_delete_module(const char __user *name_user, unsigned int flags)
{
    struct module *mod;
    char name[MODULE_NAME_LEN];
    int ret, forced = 0;

    if (!capable(CAP_SYS_MODULE))
        return -EPERM;

    if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
        return -EFAULT;
    name[MODULE_NAME_LEN-1] = '\0';

    if (mutex_lock_interruptible(&module_mutex) != 0)
        return -EINTR;

    mod = find_module(name);//遍历所有注册模块的链表,找到匹配的module实例
    if (!mod) {
        ret = -ENOENT;
        goto out;
    }

    if (!list_empty(&mod->modules_which_use_me)) {//确保其他模块没有使用该模块
        /* Other modules depend on us: get rid of them first. */
        ret = -EWOULDBLOCK;
        goto out;
    }

    /* Doing init or already dying? */
    if (mod->state != MODULE_STATE_LIVE) {
        /* FIXME: if (force), slam module count and wake up
                   waiter --RR */
        DEBUGP("%s already dying\n", mod->name);
        ret = -EBUSY;
        goto out;
    }

    /* If it has an init func, it must have an exit func to unload */
    if (mod->init && !mod->exit) {
        forced = try_force_unload(flags);
        if (!forced) {
            /* This module can't be removed */
            ret = -EBUSY;
            goto out;
        }
    }

    /* Set this up before setting mod->state */
    mod->waiter = current;

    /* Stop the machine so refcounts can't move and disable module. */
    ret = try_stop_module(mod, flags, &forced);
    if (ret != 0)
        goto out;

    /* Never wait if forced. */
    if (!forced && module_refcount(mod) != 0)
        wait_for_zero_refcount(mod);

    /* Final destruction now noone is using it. */
    if (mod->exit != NULL) {
        mutex_unlock(&module_mutex);
        mod->exit();
        mutex_lock(&module_mutex);
    }
    free_module(mod);//释放模块占用的内存空间

 out:
    mutex_unlock(&module_mutex);
    return ret;
}

自动化与热插拔

kmod实现的自动加载

request_module内核函数

热插拔

通过内核传递的消息,自动加载所需的模块。

版本控制

基本思想是:使用函数或过程的参数,生成一个CRC校验和。

Linux内核入门到放弃-模块-《深入Linux内核架构》笔记的更多相关文章

  1. Linux从入门到放弃、零基础入门Linux(第四篇):在虚拟机vmware中安装centos7.7

    如果是新手,建议安装带图形化界面的centos,这里以安装centos7.7的64位为例 一.下载系统镜像 镜像文件下载链接https://wiki.centos.org/Download 阿里云官网 ...

  2. Linux从入门到放弃、零基础入门Linux(第三篇):在虚拟机vmware中安装linux(二)超详细手把手教你安装centos6分步图解

    一.继续在vmware中安装centos6.9 本次安装是进行最小化安装,即没有图形化界面的安装,如果是新手,建议安装带图形化界面的centos, 具体参考Linux从入门到放弃.零基础入门Linux ...

  3. Linux内核入门到放弃-进程管理和调度-《深入Linux内核架构》笔记

    进程优先级 硬实时进程 软实时进程 普通进程 O(1)调度.完全公平调度器 抢占式多任务处理(preemptive multitasking):各个进程都分配到一定的时间段可以执行.时间段到期后,内核 ...

  4. Linux从入门到放弃(为做一个开发+运维的全能性人才而奋斗)

    Linux?听说是一个操作系统,好用吗?” “我也不知道呀,和windows有什么区别?我能在Linux上玩LOL吗” “别提了,我用过Linux,就是黑乎乎一个屏幕,鼠标也不能用,不停地的敲键盘,手 ...

  5. Linux从入门到放弃

    Ch.0 几点Linux常识 Linux严格区分大小写,不像windows中命令是不区分大小写的 Linux中所有内容以文件形式保存,包括硬件 Linux不靠扩展名区分文件类型,所有扩展名只是为了方便 ...

  6. Linux内核入门到放弃-网络-《深入Linux内核架构》笔记

    网络命名空间 struct net { atomic_t count; /* To decided when the network * namespace should be freed. */ a ...

  7. Linux内核入门到放弃-页面回收和页交换-《深入Linux内核架构》笔记

    概述 可换出页 只有少量几种页可以换出到交换区,对其他页来说,换出到块设备上与之对应的后备存储器即可,如下所述. 类别为 MAP_ANONYMOUS 的页,没有关联到文件,例如,这可能是进程的栈或是使 ...

  8. Linux内核入门到放弃-内核活动-《深入Linux内核架构》笔记

    中断 中断类型 同步中断和异常.这些由CPU自身产生,针对当前执行的程序 异步中断.这是经典的中断类型,由外部设备产生,可能发生在任意时间. 在退出中断中,内核会检查下列事项. 调度器是否应该选择一个 ...

  9. Linux内核入门到放弃-无持久存储的文件系统-《深入Linux内核架构》笔记

    proc文件系统 proc文件系统是一种虚拟的文件系统,其信息不能从块设备读取.只有在读取文件内容时,才动态生成相应的信息. /proc的内容 内存管理 系统进程的特征数据 文件系统 设备驱动程序 系 ...

随机推荐

  1. git版本控制工具的使用

    目录 git版本管理工具使用 一丶Git的下载与安装 1.windows下的git的下载与安装 2.linux下的git安装 二丶常用命令 三丶Git仓库 1.配置仓库信息 2.仓库的创建于管理 四丶 ...

  2. 隔离 docker 容器中的用户

    笔者在前文<理解 docker 容器中的 uid 和 gid>介绍了 docker 容器中的用户与宿主机上用户的关系,得出的结论是:docker 默认没有隔离宿主机用户和容器中的用户.如果 ...

  3. 服务注册中心之ZooKeeper系列(二) 实现一个简单微服务之间调用的例子

    上一篇文章简单介绍了ZooKeeper,讲了分布式中,每个微服务都会部署到多台服务器上,那服务之间的调用是怎么样的呢?如图: 1.集群A中的服务调用者如何发现集群B中的服务提供者呢? 2.集群A中的服 ...

  4. .Net语言 APP开发平台——Smobiler学习日志:如何调用API进行短信发送

    最前面的话:Smobiler是一个在VS环境中使用.Net语言来开发APP的开发平台,也许比Xamarin更方便 一.目标样式 我们要实现上图中的效果,需要如下的操作: 二.发送短信代码 VB: Pr ...

  5. WPF中Datagrid控件添加行号

    /// <summary> /// 导入Excel文件按钮 /// </summary> /// <param name="sender">&l ...

  6. 37.QT-QTSingleApplication-程序只运行一个实例

    QTSingleApplication由Qt官方提供的,用于实现只启动一个实例,并在启动时可以向向另一个实例通信(依赖于QtNetwork模块) QTSingleApplication下载路径:链接: ...

  7. Java学习笔记之——IO

    一. IO IO读写 流分类: 按照方向:输入流(读),输出流(写) 按照数据单位:字节流(传输时以字节为单位),字符流(传输时以字符为单位) 按照功能:节点流,过滤流 四个抽象类: InputStr ...

  8. springboot之配置文件

    springboot在加载配置文件的时候是有先后顺序的,了解加载配置文件的先后顺序,可以减少编写程序出现错误 1 springboot加载配置文件的先后顺序如下: SpringApplication将 ...

  9. CentOS 7安装指南

    CentOS 7安装指南(U盘版) 一.准备阶段 1.下载CentOS7镜像文件(ISO文件)到自己电脑,官网下载路径: http://isoredirect.centos.org/centos/7/ ...

  10. iOS----------弹窗动画

    - (void)animationAlert:(UIView *)view { CAKeyframeAnimation *popAnimation = [CAKeyframeAnimation ani ...