Chinaunix首页 | 论坛 | 博客
  • 博客访问: 192665
  • 博文数量: 71
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 210
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-13 14:49
文章分类
文章存档

2017年(1)

2015年(5)

2014年(10)

2013年(55)

我的朋友

分类: LINUX

2013-10-08 16:39:18

最近在重新学习LDD3,做最基础的底层原理学习。在挂载Hello_world模块的测试中,我发现对于ARM构架的设备,模块挂载后,模块中包含的函数和静态变量的虚拟地址不在3G(0xC0000000)以上,而是3G以下一点的位置(大概在0xBF000000),所以我通过源码了解了一下其中的原因。

首先,我们必须知道模块的挂载过程,这些较为复杂,可以参考《深入Linux内核构架》的《第七章 模块》。简单的说就是module-init-tool中的insmod的程序通过系统调用(in kernle/module.c):
  1. SYSCALL_DEFINE3(init_module, void __user *, umod,
  2.         unsigned long, len, const char __user *, uargs)
 -------------------------------------------------------------------------------
    PS : 其中SYSCALL_DEFINE3是内核用于生成系统调用函数名的宏():
      
  1. #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
  2. ......
  3. #ifdef CONFIG_FTRACE_SYSCALLS
  4. #define SYSCALL_DEFINEx(x, sname, ...) \
  5. static const char *types_##sname[] = { \
  6. __SC_STR_TDECL##x(__VA_ARGS__) \
  7. }; \
  8. static const char *args_##sname[] = { \
  9. __SC_STR_ADECL##x(__VA_ARGS__) \
  10. }; \
  11. SYSCALL_METADATA(sname, x); \
  12. __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
  13. #else
  14. #define SYSCALL_DEFINEx(x, sname, ...) \
  15. __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
  16. #endif
  17. #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
  18. #define SYSCALL_DEFINE(name) static inline long SYSC_##name
  19. #define __SYSCALL_DEFINEx(x, name, ...) \
  20. asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)); \
  21. static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__)); \
  22. asmlinkage long SyS##name(__SC_LONG##x(__VA_ARGS__)) \
  23. { \
  24. __SC_TEST##x(__VA_ARGS__); \
  25. return (long) SYSC##name(__SC_CAST##x(__VA_ARGS__)); \
  26. } \
  27. SYSCALL_ALIAS(sys##name, SyS##name); \
  28. static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__))
  29. #else /* CONFIG_HAVE_SYSCALL_WRAPPERS */
  30. #define SYSCALL_DEFINE(name) asmlinkage long sys_##name
  31. #define __SYSCALL_DEFINEx(x, name, ...) \
  32. asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
  33. #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)
  1. void *module_alloc(unsigned long size)
  2. {
  3.     return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
  4.                 GFP_KERNEL, PAGE_KERNEL_EXEC, -1,
  5.                 __builtin_return_address(0));
  6. }
所有这个虚拟地址的范围就应该在:  MODULES_VADDR----MODULES_END

对于这两个宏的定义:(arch/arm/include/asm/memory.h)
  1. /*
  2. * PAGE_OFFSET - 内核映像(解压后)运行的起始虚拟地址
  3. * TASK_SIZE - 一个用户空间 task 的大小最大值.
  4. * TASK_UNMAPPED_BASE - the lower boundary of the mmap VM area
  5. */
  6. #define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET)
  7. #define TASK_SIZE (UL(CONFIG_PAGE_OFFSET) - UL(0x01000000))
  8. #define TASK_UNMAPPED_BASE (UL(CONFIG_PAGE_OFFSET) / 3)
  9. /*
  10. * The maximum size of a 26-bit user space task.
  11. */
  12. #define TASK_SIZE_26 UL(0x04000000)
  13. /*
  14. * The module space lives between the addresses given by TASK_SIZE
  15. * and PAGE_OFFSET - it must be within 32MB of the kernel text.
  16. */
  17. #ifndef CONFIG_THUMB2_KERNEL
  18. #define MODULES_VADDR (PAGE_OFFSET - 16*1024*1024)
  19. #else
  20. /* smaller range for Thumb-2 symbols relocation (2^24)*/
  21. #define MODULES_VADDR (PAGE_OFFSET - 8*1024*1024)
  22. #endif
  23. #if TASK_SIZE > MODULES_VADDR
  24. #error Top of user space clashes with start of module space
  25. #endif
  26. /*
  27. * The highmem pkmap virtual space shares the end of the module area.
  28. */
  29. #ifdef CONFIG_HIGHMEM
  30. #define MODULES_END (PAGE_OFFSET - PMD_SIZE)
  31. #else
  32. #define MODULES_END (PAGE_OFFSET)
  33. #endif
正常情况下PAGE_OFFSET会被定义为0xc0000000,所以  
 MODULES_VADDR----MODULES_END ---> 0xBF000000----0xC0000000

这样就解释了为什么模块挂载后的最终虚拟地址空间不在正常的内核虚拟地址空间0xC0000000之上了~~~~
阅读(889) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~