最近在重新学习LDD3,做最基础的底层原理学习。在挂载Hello_world模块的测试中,我发现对于ARM构架的设备,模块挂载后,模块中包含的函数和静态变量的虚拟地址不在3G(0xC0000000)以上,而是3G以下一点的位置(大概在0xBF000000),所以我通过源码了解了一下其中的原因。
首先,我们必须知道模块的挂载过程,这些较为复杂,可以参考《深入Linux内核构架》的《第七章 模块》。简单的说就是module-init-tool中的insmod的程序通过系统调用(in kernle/module.c):
- SYSCALL_DEFINE3(init_module, void __user *, umod,
-
unsigned long, len, const char __user *, uargs)
-------------------------------------------------------------------------------
PS : 其中SYSCALL_DEFINE3是内核用于生成系统调用函数名的宏():
- #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
-
......
-
#ifdef CONFIG_FTRACE_SYSCALLS
-
#define SYSCALL_DEFINEx(x, sname, ...) \
-
static const char *types_##sname[] = { \
-
__SC_STR_TDECL##x(__VA_ARGS__) \
-
}; \
-
static const char *args_##sname[] = { \
-
__SC_STR_ADECL##x(__VA_ARGS__) \
-
}; \
-
SYSCALL_METADATA(sname, x); \
-
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
-
#else
-
#define SYSCALL_DEFINEx(x, sname, ...) \
-
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
-
#endif
-
-
#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
-
-
#define SYSCALL_DEFINE(name) static inline long SYSC_##name
-
-
#define __SYSCALL_DEFINEx(x, name, ...) \
-
asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)); \
-
static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__)); \
-
asmlinkage long SyS##name(__SC_LONG##x(__VA_ARGS__)) \
-
{ \
-
__SC_TEST##x(__VA_ARGS__); \
-
return (long) SYSC##name(__SC_CAST##x(__VA_ARGS__)); \
-
} \
-
SYSCALL_ALIAS(sys##name, SyS##name); \
-
static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__))
-
-
#else /* CONFIG_HAVE_SYSCALL_WRAPPERS */
-
-
#define SYSCALL_DEFINE(name) asmlinkage long sys_##name
-
#define __SYSCALL_DEFINEx(x, name, ...) \
-
asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
-
-
#endif /* CONFIG_HAVE_SYSCALL_WRAPPERS */
所以生成的系统调用名字为:sys_init_module
-------------------------------------------------------------------------------------------
在这个系统调用中,按模块的各个sections分配空间,而关于模块最终地址空间的调用过程是:
load_module-->layout_and_allocate-->move_module-->module_alloc_update_bounds-->module_alloc
所有关键就在module_alloc其中的实现,而这个函数的实现是构架依赖的:
(1)有些构架是直接调用vmalloc,所有模块最终的虚拟地址空间应该是在内核的虚拟地址空间中的。
(2)还有些构架(X86和ARM等)调用的是:__vmalloc_node_range
例如ARM中:(arch/arm/kernel/module.c)
- void *module_alloc(unsigned long size)
-
{
-
return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
-
GFP_KERNEL, PAGE_KERNEL_EXEC, -1,
-
__builtin_return_address(0));
-
}
所有这个虚拟地址的范围就应该在: MODULES_VADDR----MODULES_END
对于这两个宏的定义:(arch/arm/include/asm/memory.h)
- /*
-
* PAGE_OFFSET - 内核映像(解压后)运行的起始虚拟地址
-
* TASK_SIZE - 一个用户空间 task 的大小最大值.
-
* TASK_UNMAPPED_BASE - the lower boundary of the mmap VM area
-
*/
-
#define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET)
-
#define TASK_SIZE (UL(CONFIG_PAGE_OFFSET) - UL(0x01000000))
-
#define TASK_UNMAPPED_BASE (UL(CONFIG_PAGE_OFFSET) / 3)
-
-
/*
-
* The maximum size of a 26-bit user space task.
-
*/
-
#define TASK_SIZE_26 UL(0x04000000)
-
-
/*
-
* The module space lives between the addresses given by TASK_SIZE
-
* and PAGE_OFFSET - it must be within 32MB of the kernel text.
-
*/
-
#ifndef CONFIG_THUMB2_KERNEL
-
#define MODULES_VADDR (PAGE_OFFSET - 16*1024*1024)
-
#else
-
/* smaller range for Thumb-2 symbols relocation (2^24)*/
-
#define MODULES_VADDR (PAGE_OFFSET - 8*1024*1024)
-
#endif
-
-
#if TASK_SIZE > MODULES_VADDR
-
#error Top of user space clashes with start of module space
-
#endif
-
-
/*
-
* The highmem pkmap virtual space shares the end of the module area.
-
*/
-
#ifdef CONFIG_HIGHMEM
-
#define MODULES_END (PAGE_OFFSET - PMD_SIZE)
-
#else
-
#define MODULES_END (PAGE_OFFSET)
-
#endif
正常情况下PAGE_OFFSET会被定义为0xc0000000,所以
MODULES_VADDR----MODULES_END ---> 0xBF000000----0xC0000000
这样就解释了为什么模块挂载后的最终虚拟地址空间不在正常的内核虚拟地址空间0xC0000000之上了~~~~
阅读(220) | 评论(0) | 转发(0) |