TEXT_OFFSET
内核在RAM中的起始位置相对于RAM起始地址偏移。值为0x00008000
./arch/arm/Makefile
118 textofs-y := 0x00008000
222 TEXT_OFFSET := $(textofs-y)
PAGE_OFFSE 内核镜像起始虚拟地址
。值为0xC0000000
/arch/arm/configs/s3c2410_defconfig
CONFIG_PAGE_OFFSET=0xC0000000
./arch/arm/include/asm/memory.h
34 #define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET)
PHYS_OFFSET RAM启始物理地址,对于2410来说值为0x30000000,RAM接在片选6上
arch/arm/mach-s3c2410/include/mach/memory.h
from arch/arm/mach-rpc/include/mach/memory.h
#define PHYS_OFFSET UL(0x30000000)
KERNEL_RAM_VADDR 内核在RAM中的虚拟地址
。值为0xC0008000
KERNEL_RAM_PADDR 内核在RAM中的物理地址
。值为0x30008000
arch/arm/kernel/head.S
29 #define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)
30 #define KERNEL_RAM_PADDR (PHYS_OFFSET + TEXT_OFFSET)
swapper_pg_dir 初始页表虚拟地址,值为0xC0004000,一般分配KERNEL_RAM_VADDR地址下16K作为页表,
因此,我们必须确保正确设置KERNEL_RAM_VADDR。arch/arm/kernel/head.S
44 .globl swapper_pg_dir
45 .equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000
VMALLOC_START 0xc4800000
VMALLOC_END 0xe0000000
MODULES_VADDR 0xbf000000
MODULES_END 0xc0000000
./arch/arm/include/asm/memory.h
51 #define MODULES_VADDR (PAGE_OFFSET - 8*1024*1024)
61 #ifdef CONFIG_HIGHMEM
62 #define MODULES_END (PAGE_OFFSET - PMD_SIZE)
63 #else
64 #define MODULES_END (PAGE_OFFSET)
65 #endif
arch/arm/kernel/vmlinux.lds
375 SECTIONS
376 {
377 . = 0xC0000000 + 0x00108000;
378 .init : { /* Init code and data */
379 _stext = .;
所以stext等于c0108000,对应物理地址30108000,
对_end再参看arch/arm/kernel/vmlinux.lds.S文件,它也在SECTIONS区,在233行定义
232 BSS_SECTION(0, 0, 0)
233 _end = .;
对于我移植的2.3.36内核,_end等于c0555b60,对应物理地址30555b60,在此语句后添加打印信息得到
_end - _stext=44db60,4.512608M大小,所以这一句功能就是把0x30108000 ~ 0x30555b60这段(4.512608M)空间保留下来。
arch/arm/include/asm/highmem.h:6:#define PKMAP_BASE (PAGE_OFFSET - PMD_SIZE)
./arch/arm/include/asm/memory.h
34 #define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET)
35 #define TASK_SIZE (UL(CONFIG_PAGE_OFFSET) - UL(0x01000000))
36 #define TASK_UNMAPPED_BASE (UL(CONFIG_PAGE_OFFSET) / 3)
arch/arm/boot/Makefile
24 ZRELADDR := $(zreladdr-y)
25 PARAMS_PHYS := $(params_phys-y)
arch/arm/mach-s3c2410/Makefile.boot
1 ifeq ($(CONFIG_PM_H1940),y)
2 zreladdr-y := 0x30108000
3 params_phys-y := 0x30100100
4 else
5 zreladdr-y := 0x30008000
6 params_phys-y := 0x30000100
7 endif
135 /*
136 * Physical vs virtual RAM address space conversion. These are
137 * private definitions which should NOT be used outside memory.h
138 * files. Use virt_to_phys/phys_to_virt/__pa/__va instead.
139 */
140 #ifndef __virt_to_phys
141 #define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)
142 #define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET)
143 #endif
144
145 /*
146 * Convert a physical address to a Page Frame Number and back
147 */
148 #define __phys_to_pfn(paddr) ((paddr) >> PAGE_SHIFT)
149 #define __pfn_to_phys(pfn) ((pfn) << PAGE_SHIFT)
150
151 /*
152 * Convert a page to/from a physical address
153 */
154 #define page_to_phys(page) (__pfn_to_phys(page_to_pfn(page)))
155 #define phys_to_page(phys) (pfn_to_page(__phys_to_pfn(phys)))
201 /*
202 * Drivers should NOT use these either.
203 */
204 #define __pa(x) __virt_to_phys((unsigned long)(x))
205 #define __va(x) ((void *)__phys_to_virt((unsigned long)(x)))
206 #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT)
207
208 /*
209 * Virtual <-> DMA view memory address translations
210 * Again, these are *only* valid on the kernel direct mapped RAM
211 * memory. Use of these is *deprecated* (and that doesn't mean
212 * use the __ prefixed forms instead.) See dma-mapping.h.
213 */
214 #ifndef __virt_to_bus
215 #define __virt_to_bus __virt_to_phys
216 #define __bus_to_virt __phys_to_virt
217 #define __pfn_to_bus(x) __pfn_to_phys(x)
218 #define __bus_to_pfn(x) __phys_to_pfn(x)
219 #endif
Linux在arch/$(ARCH)/kernel/vmlinux.lds中定义了.init段,当内核启动完毕,这个段中的内存会被释放掉供其他使用,vmlinux.lds部分内容如下:
- OUTPUT_ARCH(arm)
- ENTRY(stext)
- jiffies = jiffies_64;
- SECTIONS
- {
- . = 0xC0000000 + 0x00008000;
- .text.head : {
- _stext = .;
- _sinittext = .;
- *(.text.head)
- }
- .init : {
- *(.init.text) *(.cpuinit.text) *(.meminit.text)
- _einittext = .;
- __proc_info_begin = .;
- *(.proc.info.init)
- __proc_info_end = .;
- __arch_info_begin = .;
- *(.arch.info.init)
- __arch_info_end = .;
- __tagtable_begin = .;
- *(.taglist.init)
- __tagtable_end = .;
- . = ALIGN(16);
- __setup_start = .;
- *(.init.setup)
- __setup_end = .;
- __early_begin = .;
- *(.early_param.init)
- __early_end = .;
- __initcall_start = .;
- *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)
- __initcall_end = .;
- __con_initcall_start = .;
- *(.con_initcall.init)
- __con_initcall_end = .;
- __security_initcall_start = .;
- *(.security_initcall.init)
- __security_initcall_end = .;
- . = ALIGN(32);
- __initramfs_start = .;
- usr/built-in.o(.init.ramfs)
- __initramfs_end = .;
- . = ALIGN(4096);
- __per_cpu_load = .;
- __per_cpu_start = .;
- *(.data.percpu.page_aligned)
- *(.data.percpu)
- *(.data.percpu.shared_aligned)
- __per_cpu_end = .;
- __init_begin = _stext;
- *(.init.data) *(.cpuinit.data) *(.cpuinit.rodata) *(.meminit.data) *(.meminit.rodata)
- . = ALIGN(4096);
- __init_end = .;
- }
- /DISCARD/ : {
- *(.exit.text) *(.cpuexit.text) *(.memexit.text)
- *(.exit.data) *(.cpuexit.data) *(.cpuexit.rodata) *(.memexit.data) *(.memexit.rodata)
- *(.exitcall.exit)
- *(.ARM.exidx.exit.text)
- *(.ARM.extab.exit.text)
- }
- .text : {
- _text = .;
- __exception_text_start = .;
- *(.exception.text)
- __exception_text_end = .;
- . = ALIGN(8); *(.text.hot) *(.text) *(.ref.text) *(.devinit.text) *(.devexit.text) *(.text.unlikely)
- . = ALIGN(8); __sched_text_start = .; *(.sched.text) __sched_text_end = .;
- . = ALIGN(8); __lock_text_start = .; *(.spinlock.text) __lock_text_end = .;
- . = ALIGN(8); __kprobes_text_start = .; *(.kprobes.text) __kprobes_text_end = .;
- *(.fixup)
- *(.gnu.warning)
- *(.rodata)
- *(.rodata.*)
- *(.glue_7)
- *(.glue_7t)
- *(.got)
- }
- . = ALIGN((4096)); .rodata : AT(ADDR(.rodata) - 0) { __start_rodata = .; *(.rodata) *(.rodata.*) *(__vermagic) *(__markers_strings) *(__tracepoints_strings) } .rodata1 : AT(ADDR(.rodata1) - 0) { *(.rodata1) } .pci_fixup : AT(ADDR(.pci_fixup) - 0) { __start_pci_fixups_early = .; *(.pci_fixup_early) __end_pci_fixups_early = .; __start_pci_fixups_header = .; *(.pci_fixup_header) __end_pci_fixups_header = .; __start_pci_fixups_final = .; *(.pci_fixup_final) __end_pci_fixups_final = .; __start_pci_fixups_enable = .; *(.pci_fixup_enable) __end_pci_fixups_enable = .; __start_pci_fixups_resume = .; *(.pci_fixup_resume) __end_pci_fixups_resume = .; __start_pci_fixups_resume_early = .; *(.pci_fixup_resume_early) __end_pci_fixups_resume_early = .; __start_pci_fixups_suspend = .; *(.pci_fixup_suspend) __end_pci_fixups_suspend = .; } .builtin_fw : AT(ADDR(.builtin_fw) - 0) { __start_builtin_fw = .; *(.builtin_fw) __end_builtin_fw = .; } .rio_route : AT(ADDR(.rio_route) - 0) { __start_rio_route_ops = .; *(.rio_route_ops) __end_rio_route_ops = .; } __ksymtab : AT(ADDR(__ksymtab) - 0) { __start___ksymtab = .; *(__ksymtab) __stop___ksymtab = .; } __ksymtab_gpl : AT(ADDR(__ksymtab_gpl) - 0) { __start___ksymtab_gpl = .; *(__ksymtab_gpl) __stop___ksymtab_gpl = .; } __ksymtab_unused : AT(ADDR(__ksymtab_unused) - 0) { __start___ksymtab_unused = .; *(__ksymtab_unused) __stop___ksymtab_unused = .; } __ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) - 0) { __start___ksymtab_unused_gpl = .; *(__ksymtab_unused_gpl) __stop___ksymtab_unused_gpl = .; } __ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) - 0) { __start___ksymtab_gpl_future = .; *(__ksymtab_gpl_future) __stop___ksymtab_gpl_future = .; } __kcrctab : AT(ADDR(__kcrctab) - 0) { __start___kcrctab = .; *(__kcrctab) __stop___kcrctab = .; } __kcrctab_gpl : AT(ADDR(__kcrctab_gpl) - 0) { __start___kcrctab_gpl = .; *(__kcrctab_gpl) __stop___kcrctab_gpl = .; } __kcrctab_unused : AT(ADDR(__kcrctab_unused) - 0) { __start___kcrctab_unused = .; *(__kcrctab_unused) __stop___kcrctab_unused = .; } __kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) - 0) { __start___kcrctab_unused_gpl = .; *(__kcrctab_unused_gpl) __stop___kcrctab_unused_gpl = .; } __kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) - 0) { __start___kcrctab_gpl_future = .; *(__kcrctab_gpl_future) __stop___kcrctab_gpl_future = .; } __ksymtab_strings : AT(ADDR(__ksymtab_strings) - 0) { *(__ksymtab_strings) } __init_rodata : AT(ADDR(__init_rodata) - 0) { *(.ref.rodata) *(.devinit.rodata) *(.devexit.rodata) } __param : AT(ADDR(__param) - 0) { __start___param = .; *(__param) __stop___param = .; . = ALIGN((4096)); __end_rodata = .; } . = ALIGN((4096));
- _etext = .;
-
-
-
- . = ALIGN(8);
- .ARM.unwind_idx : {
- __start_unwind_idx = .;
- *(.ARM.exidx*)
- __stop_unwind_idx = .;
- }
- .ARM.unwind_tab : {
- __start_unwind_tab = .;
- *(.ARM.extab*)
- __stop_unwind_tab = .;
- }
- . = ALIGN(8192);
- __data_loc = .;
- .data : AT(__data_loc) {
- _data = .;
-
-
-
-
- *(.data.init_task)
- . = ALIGN(4096);
- __nosave_begin = .;
- *(.data.nosave)
- . = ALIGN(4096);
- __nosave_end = .;
-
-
-
- . = ALIGN(32);
- *(.data.cacheline_aligned)
-
-
-
- . = ALIGN(32);
- __start___ex_table = .;
- *(__ex_table)
- __stop___ex_table = .;
-
-
-
- *(.data) *(.ref.data) *(.devinit.data) *(.devexit.data) . = ALIGN(8); __start___markers = .; *(__markers) __stop___markers = .; . = ALIGN(32); __start___tracepoints = .; *(__tracepoints) __stop___tracepoints = .; . = ALIGN(8); __start___verbose = .; *(__verbose) __stop___verbose = .;
- CONSTRUCTORS
- _edata = .;
- }
- _edata_loc = __data_loc + SIZEOF(.data);
- .bss : {
- __bss_start = .;
- *(.bss)
- *(COMMON)
- _end = .;
- }
-
- .stab 0 : { *(.stab) }
- .stabstr 0 : { *(.stabstr) }
- .stab.excl 0 : { *(.stab.excl) }
- .stab.exclstr 0 : { *(.stab.exclstr) }
- .stab.index 0 : { *(.stab.index) }
- .stab.indexstr 0 : { *(.stab.indexstr) }
- .comment 0 : { *(.comment) }
- }
-
-
-
-
-
- ASSERT((__proc_info_end - __proc_info_begin), "missing CPU support")
- ASSERT((__arch_info_end - __arch_info_begin), "no machine record defined")
一:只要你写过模块程序hello world就对__init,__exit不会陌生,他们定义在include/linux/init.h中:
- #define __init __section(.init.text) __cold notrace
- #define __exit __section(.exit.text) __exitused __cold
__cold在include/linux/compiler-gcc4.h中定义:
#define __cold __attribute__((__cold__))
所以 #define __init __section(.init.text) __cold notrace等价于#define __init __attribute__((__section(.init.text))) __atrribute__是一个GNU C扩展,它主要用来声明一些特殊的属性,这些属性主要用来指示编译器进行特定方面的优化和更仔细的代码检查。GNU支持几十个属性,section是其中的一个。通常编译器将函数放在.text节,变量放在.data节或.bss节,使用section属性,可以让编译器将函数或变量放在指定的节中。那么前面对__init的定义便表示将它修饰的代码放在.init.text节。连接器可以把相同节的代码或数据安排在一起,比如__init修饰的所有代码都会被放在.init.text节里,初始化结束后就可以释放这部分内存。一般在程序的结尾都会有一句,例如module_init(hello_init);hello_init就是那个被__init修饰的模块初始化函数,在insmod的时候会调用module_init中的函数。__exit是在模块卸载时相应的内存释放。下边说一下__initdata,这个我在DMA的源码中看到过,定义在include/linux/init.h中:
#define __initdata _section(.init.data)
看上边的vmlinux.lds,__initdata段也在.init段中,说明初始化后他所修饰的函数占用的内存后会被释放掉。在阅读RTC源码时遇到的__devinit,__devexit。在include/linux/init.h中定义:
- #define __devinit __section(.devinit.text) __cold
- #define __devexit __section(.devexit.text) __exitused __cold
整个.init段释放memory的大小会在系统启动过程中打印出来:
二:subsys_initcall定义在include/linux/init.h中,定义如下:
#define subsys_initcall(fn) __define_initcall("4",fn,4)
这里出现了一个宏__define_initcall,他用于将指定的函数指针放到initcall.init节里,而对于具体的subsys_initcall宏,则是把fn放到.initcall.init的子节.initcall.init里。看上边vmlinux.lds这一部分:
- __initcall_start = .;
- *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)
- __initcall_end = .;
这里__initcall_start指向.initcall.init节的开始,__initcall_end指向它的结尾。而.initcall.init节又被分为几个子节。这个subsys_initcall宏便是将指定的函数指针放在了.initcall4.init子节。
三:
__attribute__((packed));比如下边这个结构体(在include/linux/usb/ch9.h中定义):
- struct usb_interface_descriptor {
- __u8 bLength;
- __u8 bDescriptorType;
- __u8 bInterfaceNumber;
- __u8 bAlternateSetting;
- __u8 bNumEndpoints;
- __u8 bInterfaceClass;
- __u8 bInterfaceSubClass;
- __u8 bInterfaceProtocol;
- __u8 iInterface;
- } __attribute__ ((packed));
这里的__attribute__ ((packed))告诉编译器,这个结构的元素都是1字节对齐的,不要再添加填充位了。如果不给编译器这个暗示,编译器就会依据你的平台类型在结构的每个元素之间添加一定的填充位。
四:
- #define likely(x) __builtin_expect(!!(x),1)
- #define unlikely(x) __builtin_expect(!!(x),0)
__builtin_expect是GCC里内建的一个函数:
long __builtin_expect(long exp, long c)
它的第一个参数exp为一个整型的表达式,返回值也是这个exp,它的第二个参数c的值必须是一个编译器的常量,那这个内建函数的意思就是exp的预期值为c,编译器可以根据这个信息适当的重排条件语句块的顺序,将符合这个条件的分支放在合适的地方。对于unlikely(x)就是告诉编译器x发生的可能性不大,那么这个条件块里语句的目标码可能就会被放到一个比较远的位置,以保证经常执行的目标码更紧凑,而likely相反。也就是说,如果你觉得if条件为1的可能性非常大时,可以在条件表达式外面包装一个likely(),如果可能性非常小,则用unlikely()包装。
五:
container_of(pointer,type,member);
我们可以通过一个叫container_of的宏反查member所在的数据结构。比如:
- struct A_t{
- int a;
- int b;
- };
- struct A_t A;
- int *b_p = &(A.b);
使用如下方法可以从数据结构的一个元素出发,得到整个数据结构的指针。
struct A_t * A_p = container_of(b_p, struct A_t, b);