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, ¶ms[i]);
- Linux Module的来龙去脉浅析
- Linux system call的来龙去脉浅析
- Linux日志的来龙去脉
- Linux Kernel中断子系统来龙去脉浅析
- Linux Kernel时间子系统之来龙去脉浅析
- Linux日志处理的来龙去脉
- Linux内核2.6 的来龙去脉
- Linux Character Device Driver and RTC驱动来龙去脉浅析
- 摸清Linux日志处理的来龙去脉
- Linux Kernel之Deferred work(Softirq、tasklet、Work queues)来龙去脉浅析
- Linux Kernel之ARM 平台下电源管理常用机制来龙去脉浅析
- linux 图形界面 来龙去脉
- 伊朗核问题的来龙去脉
- MFC程序的来龙去脉
- MFC程序的来龙去脉
- MFC程序的来龙去脉
- MFC程序的来龙去脉
- 对话框的来龙去脉
- Linux2.6 内核的 Initrd 机制解析
- javascript原型链上constructor属性的丢失问题
- HDU 4629 Burning
- jquery中使用submit提交按钮
- google面试题及我的算法(1)——交叉换位
- Linux Module的来龙去脉浅析
- JSP乱码解决方案
- javascript中用构造器创建对象与字面量创建对象的区别
- C++学习过程1--简单程序
- poj 1922
- 理解javascript中的function与new Function的区别
- arm linux kernel 从入口到start_kernel 的代码分析
- mysql就是个坑
- Spring之AOP实例