Linux Module的来龙去脉浅析

来源:互联网 发布:python退出程序 编辑:程序博客网 时间:2024/06/03 18:56

 

本文的目的在分析清楚Linux下module的来龙去脉,相关的code均来自于Linux Kernel 2.6.23. 这个版本现在来说是比较老的版本了,以后有时间我会重新review一下最新的Kernel版本并更新之。但是我个人认为应该大同小异。

 

先看一个最简单的Hello World Example,该例子来自于LDD[1]第二章节,为了方便阅读,这里我原文拷贝了这个例子:

#include <linux/init.h> 

#include <linux/module.h> 

MODULE_LICENSE("Dual BSD/GPL"); 

static int hello_init(void) 

    printk(KERN_ALERT "Hello, world\n"); 

    return 0; 

 

static void hello_exit(void) 

    printk(KERN_ALERT "Goodbye, cruel world\n"); 

}

  

module_init(hello_init); 

module_exit(hello_exit);

 

其Makefile文件内容为:

# If KERNELRELEASE is defined, we've been invoked from the 

# kernel build system and can use its language. 

ifneq ($(KERNELRELEASE),) 

    obj-m := hello.o  

# Otherwise we were called directly from the command 

# line; invoke the kernel build system. 

else 

    KERNELDIR ?= /lib/modules/$(shell uname -r)/build 

    PWD  := $(shell pwd)  

default: 

    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules  

endif 

 

M:是Linux Kernel自己的compiling system中支持的一个macro,详见makefile文件。有兴趣的朋友不妨去看看Linux Kernel Makefile文件中是如何定义使用M的。

###

# External module support.

# When building external modules the kernel used as basis is considered

# read-only, and no consistency checks are made and the make

# system is not used on the basis kernel. If updates are required

# in the basis kernel ordinary make commands (without M=...) must

# be used.                                                                                

#

# The following are the only valid targets when building external

# modules.

# make M=dir clean     Delete all automatically generated files

# make M=dir modules   Make all modules in specified dir

# make M=dir              Same as 'make M=dir modules'

# make M=dir modules_install

#                      Install the modules built in the module directory

#                      Assumes install directory is already created

 

Kernel Symbol Table

The table contains the addresses of global kernel items—functions and variables—that are needed to implement modularized drivers。

include/linux/module.h文件中关于EXPORT_SYMBOL的定义:

/* 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")))            \

      = MODULE_SYMBOL_PREFIX #sym;                          \

      static const struct kernel_symbol __ksymtab_##sym       \

      __attribute_used__                             \

      __attribute__((section("__ksymtab" sec), unused))     \

      = { (unsigned long)&sym, __kstrtab_##sym }    

这里简单说明一下:如果sym是函数名,sym 等于 &sym。如果sym是variable,这&sym就是这个variable的address了。

 

#define EXPORT_SYMBOL(sym)                                   \

      __EXPORT_SYMBOL(sym, "")

 

如果我们的Module也要提供给其他的module可以调用的function,我们同样需要EXPORT_SYMBOL将其添加到Linux Kernel Symbol table中。这样其他的module在loading时才可以找到之。

 

这种table的方式也是Linux Kernel中广泛使用的一种方式。后面要讲的Module parameter也是如此。简而言之就是记录function(或variable) name和在object(或link后) file中的address,需要的时候按照name来lookup,找到后取其address来进行相应的替换。

Module的Compiling

仅仅是compiling,没有link。所以如果该module调用了某些Linux Kernel的function,此时并不会去link这些function。

module_init

module_init macro定义在include/linux/init.h中,module_init最后相当于:__define_initcall("6s",fn,6s)。全部expand后其实就是将module_init(hello_init)中的function address存储在__section__(".initcall" 6s".init")。此section的定义请参考vmlinux.lds.S link script。

当含有module_init的文件直接compile并link into the Linux Kernel,该init function在Linux Kernel Initializing的时候会被调用:start_kernel ()-> rest_init() -> kernel_init()-> do_basic_setup() -> do_initcalls()。

当含有module_init的文件在module中被按照module的compile 和 link时,在install该module的时候被调用。

所以无论该驱动程序是否以module的方式实现,都可以直接使用含有module_init来定义init function。

 

Module的loading过程:insmod命令

首先通过vmalloc分配kernel memory,然后copy xxx.ko中的text/data到刚分配的memory中,接下来就是进行类似link要做的事情,resolve本module中用到的Linux Kernel的function。最后就是调用module的init function。所谓resolve的本质无非是在Kernel symbol table中找到对应的entry,并将其地址更新到之前copy的text中。

参见:sys_init_module()

 

Module Parameters

include/linux/moduleparam.h:

#define __module_param_call(prefix, name, set, get, arg, perm)             \

      /* Default value instead of permissions? */                 \

      static int __param_perm_check_##name __attribute__((unused)) =  \

      BUILD_BUG_ON_ZERO((perm) < 0 || (perm) > 0777 || ((perm) & 2)); \

      static char __param_str_##name[] = prefix #name;           \

      static struct kernel_param const __param_##name                  \

      __attribute_used__                                    \

    __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \

      = { __param_str_##name, perm, set, get, arg }

 

#define module_param_call(name, set, get, arg, perm)                         \

      __module_param_call(MODULE_PARAM_PREFIX, name, set, get, arg, perm)

 

/* Helper functions: type is byte, short, ushort, int, uint, long,

   ulong, charp, bool or invbool, or XXX if you define param_get_XXX,

   param_set_XXX and param_check_XXX. */

#define module_param_named(name, value, type, perm)                   \

      param_check_##type(name, &(value));                         \

      module_param_call(name, param_set_##type, param_get_##type, &value, perm); \

      __MODULE_PARM_TYPE(name, #type)

 

#define module_param(name, type, perm)                       \

      module_param_named(name, name, type, perm)

蓝色字体:{ __param_str_##name, perm, set, get, arg }中的__param_str_##name记录了这个参数的名字,arg记录了这个参数的地址。

之后当我们insmod的时候,如果带有parameters:如LDD[1] 2.8 章节中insmod hellop howmany=10 whom="Mom",那么Linux kernel会去该Module中的parameter table中找到对应变量的地址,并修改其值。

具体请看Kernel代码:sys_init_module() ->load_module()-> parse_args() -> parse_one() ->params[i].set(val, &params[i]);

 

原创粉丝点击