Linux核心啟動第二階段之setup_arch函式分析
阿新 • • 發佈:2018-12-01
轉自:http://blog.chinaunix.net/uid-20672257-id-2383451.html
執行setup_arch()函式
回到start_kernel當中,569行,呼叫setup_arch函式,傳給他的引數是那個未被初始化的內部變數command_line。這個setup_arch()函式是start_kernel階段最重要的一個函式,每個體系都有自己的setup_arch()函式,是體系結構相關的,具體編譯哪個體系的setup_arch()函式,由頂層Makefile中的ARCH變數決定:
它首先通過檢測出來的處理器型別進行處理器核心的初始化,然後通過 bootmem_init()函式根據系統定義的 meminfo 結構進行記憶體結構的初始化,最後呼叫paging_init()開啟 MMU,建立核心頁表,對映所有的實體記憶體和 IO空間。
start_kernel ()
--> setup_arch ()
--> paging_init ()
--> bootmem_init ()
--> alloc_bootmem_low_pages ()
第569行setup_arch(&command_line)在arch/arm/kernel/setup.c定義如下:
767 void __init setup_arch(char **cmdline_p)
768 {
769 struct tag *tags = (struct tag *)&init_tags;
770 struct machine_desc *mdesc;
771 char *from = default_command_line;
772
773 unwind_init();
774
775 setup_processor();
776 mdesc = setup_machine(machine_arch_type);
777 machine_name = mdesc->name; //machine_name在126行定義static const char *machine_name;
778
779 if (mdesc->soft_reboot) //這個變數初始值為"h",如果這裡設定成softboot,它會將這個初始值變為"s"
780 reboot_setup("s");
781
782 if (__atags_pointer) //檢查BootLoader是否傳入引數
783 tags = phys_to_virt(__atags_pointer);
784 else if (mdesc->boot_params)//machine descriptor中設定的啟動引數地址(arch/arm/mach-s3c2410/mach-smdk2410.c)
785 tags = phys_to_virt(mdesc->boot_params);
786
787 #if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
788 /*
789 * If we have the old style parameters, convert them to
790 * a tag list.
791 */
792 if (tags->hdr.tag != ATAG_CORE)//核心引數列表第一項必須是ATAG_CORE型別,如果不是,則需要轉換成新的核心引數型別,新的核心引數型別用下面 struct tag結構表示,由bootloader[u-boot-1.1.5]傳遞到實體地址0x30000100處的引數型別是tag list結構,在u-boot-1.1.6後是採用了新的核心引數型別struct tag結構
793 convert_to_tag_list(tags);//此函式完成新舊引數結構轉換,將引數結構轉換為tag list結構
794 #endif
795 if (tags->hdr.tag != ATAG_CORE)//如果沒有核心引數
796 tags = (struct tag *)&init_tags;//則選用預設的核心引數,init_tags檔案中有定義。
797
798 if (mdesc->fixup) //用核心引數列表填充meminfo,fixup函數出現在註冊machine_desc中,即MACHINE_START、MACHINE_END定義中,這個函式,有些板子有,但在2410中沒有定義這個函式。
799 mdesc->fixup(mdesc, tags, &from, &meminfo);
800
801 if (tags->hdr.tag == ATAG_CORE) {
802 if (meminfo.nr_banks != 0) //說明記憶體被初始化過
803 squash_mem_tags(tags);//如果是tag list,那麼如果系統已經建立了預設的meminfo.nr_banks,清除tags中關於MEM的引數,以免再次被初始化
804 save_atags(tags);
805 parse_tags(tags);//做出一些針對各個tags的處理
806 }
807 //下面是記錄核心程式碼的起始,結束虛擬地址
808 init_mm.start_code = (unsigned long) _text;
809 init_mm.end_code = (unsigned long) _etext;
810 init_mm.end_data = (unsigned long) _edata;
811 init_mm.brk = (unsigned long) _end;
812
813 /* parse_early_param needs a boot_command_line */
814 strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
815
816 /* populate cmd_line too for later use, preserving boot_command_line */
817 strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
818 *cmdline_p = cmd_line;
819
820 parse_early_param(); //解釋命令列引數,見後
821
822 arm_memblock_init(&meminfo, mdesc);
823
824 paging_init(mdesc);
825 request_standard_resources(&meminfo, mdesc);//將裝置實體登記註冊到匯流排空間連結串列
826
827 #ifdef CONFIG_SMP
828 smp_init_cpus();
829 #endif
830 reserve_crashkernel(); //要配置CONFIG_KEXEC,否則為空函式,2410中沒有配置
831
832 cpu_init(); //對用到的IRQ, ABT, UND模式的SP都作了初始化
833 tcm_init(); //在ARM中是個空函式
834
835 /* 根據arch/arm/mach-s3c2410/mach-smdk2410.c中machine_desc結構,
設定各種特定於體系結構的指標
836 * Set up various architecture-specific pointers
837 */
838 arch_nr_irqs = mdesc->nr_irqs; //在arch/arm/kernel/irq.c第171行,由nr_irqs = arch_nr_irqs ? arch_nr_irqs : NR_IRQS;引用,而nr_irqs在init_IRQ、asm_do_IRQ、set_irq_flags中都有引用
839 init_arch_irq = mdesc->init_irq; //在init_IRQ函式第165行呼叫
840 system_timer = mdesc->timer;
841 init_machine = mdesc->init_machine;
842
843 #ifdef CONFIG_VT
844 #if defined(CONFIG_VGA_CONSOLE)
845 conswitchp = &vga_con;
846 #elif defined(CONFIG_DUMMY_CONSOLE)
847 conswitchp = &dummy_con;
848 #endif
849 #endif
850 early_trap_init(); //見後
851 }
852
769行tag資料結構在arch/arm/include/asm/setup.h中定義如下:
struct tag {
struct tag_header hdr;
union {
struct tag_core core;
struct tag_mem32 mem;
struct tag_videotext videotext;
struct tag_ramdisk ramdisk;
struct tag_initrd initrd;
struct tag_serialnr serialnr;
struct tag_revision revision;
struct tag_videolfb videolfb;
struct tag_cmdline cmdline;
/*
* Acorn specific
*/
struct tag_acorn acorn;
/*
* DC21285 specific
*/
struct tag_memclk memclk;
} u;
};
其中init_tags在arch/arm/kernel/setup.c檔案下定義如下
662 static struct init_tags {
663 struct tag_header hdr1;
664 struct tag_core core;
665 struct tag_header hdr2;
666 struct tag_mem32 mem;
667 struct tag_header hdr3;
668 } init_tags __initdata = {
669 { tag_size(tag_core), ATAG_CORE },
670 { 1, PAGE_SIZE, 0xff },
671 { tag_size(tag_mem32), ATAG_MEM },
672 { MEM_SIZE, PHYS_OFFSET },
673 { 0, ATAG_NONE }
674 };
675
676 static void (*init_machine)(void) __initdata;
770行完整的machine_desc結構描述如下:arch/arm/include/asm/mach/arch.h
13 struct tag;
14 struct meminfo;
15 struct sys_timer;
16
17 struct machine_desc {
18 /*
19 * Note! The first four elements are used
20 * by assembler code in head.S, head-common.S
21 */
22 unsigned int nr; /* 開發板的機器型別ID */
23 unsigned int nr_irqs; /* number of IRQs */
24 unsigned int phys_io; /* 起始IO實體地址 */
25 unsigned int io_pg_offst; /* byte offset for io
26 * page tabe entry */
27
28 const char *name; /* 開發板名稱 */
29 unsigned long boot_params; /* tagged list核心啟動引數的地址*/
30
31 unsigned int video_start; /* start of video RAM */
32 unsigned int video_end; /* end of video RAM */
33
34 unsigned int reserve_lp0 :1; /* never has lp0 */
35 unsigned int reserve_lp1 :1; /* never has lp1 */
36 unsigned int reserve_lp2 :1; /* never has lp2 */
37 unsigned int soft_reboot :1; /* soft reboot */
38 void (*fixup)(struct machine_desc *,
39 struct tag *, char **,
40 struct meminfo *);
41 void (*reserve)(void);/* reserve mem blocks */
42 void (*map_io)(void);/*IO對映函式(在這裡修改時鐘頻率)*/
43 void (*init_irq)(void); /*中斷初始化函式*/
44 struct sys_timer *timer; /* system tick timer */
45 void (*init_machine)(void);
46 };
771行default_command_line在setup.c檔案129行中定義如下:
static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
其中CONFIG_CMDLINE在“.config”配置檔案中定義的。
773行arch/arm/kernel/unwind.c
439 int __init unwind_init(void)
440 {
441 struct unwind_idx *idx;
442
443 /* Convert the symbol addresses to absolute values */
444 for (idx = __start_unwind_idx; idx < __stop_unwind_idx; idx++)
445 idx->addr = prel31_to_addr(&idx->addr);
446
447 pr_debug("unwind: ARM stack unwinding initialised\n");
448
449 return 0;
450 }
struct unwind_idx {
unsigned long addr;
unsigned long insn;
};
776行,machine_arch_type在arch/arm/tools/gen-mach-types中第69行定義,
printf("#define machine_arch_type\t__machine_arch_type\n")
它最後會被轉換成一個頭檔案include/generated/mach-types.h,包含在arch/arm/include/asm/mach- types.h檔案中,__machine_arch_type在arch/arm/kernel/head-common.S定義
27 .long __machine_arch_type @ r5
並通過arch/arm/kernel/head-common.S第60行命令
60 str r1, [r5] @ Save machine type
將bootloader通過r1傳遞過來的機器碼儲存到__machine_arch_type。
下面我們來看setup_machine函式,在在arch/arm/kernel/setup.c檔案下定義如下
391 static struct machine_desc * __init setup_machine(unsigned int nr)
392 {
393 struct machine_desc *list;
394
395 /*
396 * locate machine in the list of supported machines.
397 */
398 list = lookup_machine_type(nr);
399 if (!list) {
400 printk("Machine configuration botched (nr %d), unable "
401 "to continue.\n", nr);
402 while (1);
403 }
404
405 printk("Machine: %s\n", list->name);
406
407 return list;
408 }
在這個函式中就是查詢你是什麼版本的處理器架構,最後就是呼叫了lookup_processor_type這個函式,它在彙編部分也提到過,在arch/arm/kernel/head-common.S定義
230 ENTRY(lookup_machine_type)
231 stmfd sp!, {r4 - r6, lr}
232 mov r1, r0
233 bl __lookup_machine_type
234 mov r0, r5
235 ldmfd sp!, {r4 - r6, pc}
236 ENDPROC(lookup_machine_type)
可見最後呼叫的是__lookup_machine_type,這個函式在彙編中我們已經分析過了。這裡再來分析一下:
核心中對於每種支援的開發板都會使用巨集MACHINE_START、MACHINE_END來定義一個machine_desc結構,它定義開發板相關的一些屬性及函式,比如機器型別ID、起始I/O實體地址、Bootloader傳入的引數的地址、中斷初始化函式、I/O對映函式等,比如對於 SMDK2410開發板,在arch/arm/mach-s3c2410/mach-smdk2410.c中定義如下:
MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch
* to SMDK2410 */
/* Maintainer: Jonas Dietsche */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = smdk2410_map_io,
.init_irq = s3c24xx_init_irq,
.init_machine = smdk2410_init,
.timer = &s3c24xx_timer,
MACHINE_END
而巨集MACHINE_START、MACHINE_END在arch/arm/include/asm/mach/arch.h中定義如下:
52 #define MACHINE_START(_type,_name) \
53 static const struct machine_desc __mach_desc_##_type \
54 __used \
55 __attribute__((__section__(".arch.info.init"))) = { \
56 .nr = MACH_TYPE_##_type, \
57 .name = _name,
58
59 #define MACHINE_END \
60 };
所以以下展開後如下:
static const struct machine_desc __mach_desc_SMDK2410
__used
__attribute__((__section__(".arch.info.init"))) = {
.nr = MACH_TYPE_SMDK2410,
.name = SMDK2410,
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = smdk2410_map_io,
.init_irq = s3c24xx_init_irq,
.init_machine = smdk2410_init,
.timer = &s3c24xx_timer,
};
對照前面完整的machine_desc結構描述就能找到系統定義開發板相關的一些屬性及函式
所有machine_desc結構體初始化後被編譯連線到“.arch.info.init”段中,在連線核心時,它們被組織在一起,開始地址為 __arch_info_begin,結束地址為__arch_info_end,從連線指令碼“arch/arm/kernel /vmlinux.lds.S”中可以看出:
37 __arch_info_begin = .; //machine_desc結構體的開始地址
38 *(.arch.info.init) //所有machine_desc都在這裡面
39 __arch_info_end = .; //machine_desc結構體的開始地址
不同的machine_desc結構用於不同的開發板,U-BOOT呼叫核心時,會在r1暫存器中給出開發板的標記(機器型別ID);對於S3C2410、 S3C2440開發板,U-Boot傳入的機器型別ID為MACH_TYPE_SMDK2410、MACH_TYPE_S3C2440,它們對應的 machine_desc結構分別在arch/arm/mach-s3c2410/mach-smdk2410.c和
arch/arm/mach-s3c2440/mach-smdk2440.c中定義,現在再來看看__lookup_machine_type函式。它在arch/arm/kernel/head-common.S中定義如下:
196 4: .long . //當前行編譯連結後的虛擬地址
197 .long __arch_info_begin //machine_desc結構體的開始地址(虛擬地址)
198 .long __arch_info_end //machine_desc結構體的結束地址(虛擬地址)
211 __lookup_machine_type:
212 adr r3, 4b //r3 = 196行的實體地址
213 ldmia r3, {r4, r5, r6} /*[r3]->r4,[r3+4]->r5,[r3+8]->r6,
r4 = 196行的虛擬地址,
r5 = __arch_info_begin (VA)
r6 = __arch_info_end (VA)*/
214 sub r3, r3, r4 @r3=(196行的實體地址)-(196行的虛擬地址),即實體地址與虛擬地址的差值
215 add r5, r5, r3 @ r5= __arch_info_begin(實體地址),虛擬地址轉實體地址
216 add r6, r6, r3 @ r6= __arch_info_end(實體地址)
217 1: ldr r3, [r5, #MACHINFO_TYPE] @ r5是machine_desc結構體地址
218 teq r3, r1 @ matches loader number? r1是Bootloader呼叫核心時傳入的機器型別ID
219 beq 2f @ found 相等則跳轉到224行
220 add r5, r5, #SIZEOF_MACHINE_DESC @ 否則跳轉到下一個machine_desc結構體
其中MACHINFO_TYPE,SIZEOF_MACHINE_DESC 在arch/arm/kernel/asm-offsets.c中定義:
DEFINE(MACHINFO_TYPE, offsetof(struct machine_desc, nr));
DEFINE(SIZEOF_MACHINE_DESC, sizeof(struct machine_desc));
offsetof在include/linux/stddef.h中定義:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
可以看出nr是函式setup_machine傳遞過來的引數,這裡得到的是machine_desc結構體地址。
221 cmp r5, r6 是否已經比較完所有的machine_desc結構體
222 blo 1b 沒有則繼續比較(lo:無符號小於)
223 mov r5, #0 @ 比較完畢,但沒有匹配的machine_desc結構體,r5=0
224 2: mov pc, lr 返回
225 ENDPROC(__lookup_machine_type)
上面程式碼功能說明:
__lookup_machine_type 函式將這個r1暫存器中的機器型別ID與“.arch.info.init”段中machine_desc結構中的nr成員比較,如果相等則表示找到了匹配的machine_desc結構,於是返回它的地址(存於r5中),如果__arch_machine_begin和 __arch_machine_end間所有machine_desc結構的nr成員都不等於r1暫存器中的值,則返回0(r5等於0)
在配置選單時,選中兩個開發板即可:
System Type--->
S3C2410 Machines--->
[*] SMDK2410/A9M2410
S3C2440 Machines--->
[*] SMDK2440
第780行reboot_setup函式在arch/arm/kernel/process.c定義如下:
198 static char reboot_mode = 'h';
199
200 int __init reboot_setup(char *str)
201 {
202 reboot_mode = str[0];
203 return 1;
204 }
205
206 __setup("reboot=", reboot_setup);
reboot這個引數在引導引數中設定。
第782行__atags_pointer是bootloader傳遞引數的實體地址,見第一階段啟動原始碼arch/arm/kernel/head-common.S第61行(str r2, [r6]),此處r6就是__atags_pointer的地址,__atags_pointer在28處有定義,它將bootloader通過r2傳遞過來的地址引數存放到 __atags_pointer。
第783、784行,由於MMU單元已開啟,此處__atags_pointer是實體地址,需要轉換成虛擬地址才能訪問,因為此時CPU訪問的都是虛擬地址,在這裡如果bootloader沒有傳遞地址引數則就使用arch/arm/mach-s3c2410 /mach-smdk2410.c檔案中MACHINE_START、MACHINE_END定義的boot_params引數,對2410定義如下:
.boot_params = S3C2410_SDRAM_PA + 0x100,
S3C2410_SDRAM_PA在arch/arm/mach-s3c2410/include/mach/map.h中定義如下
#define S3C2410_CS6 (0x30000000)
#define S3C2410_CS7 (0x38000000)
#define S3C2410_SDRAM_PA (S3C2410_CS6)
所以.boot_params引數地址是0x30000100位置
801 if (tags->hdr.tag == ATAG_CORE) {
802 if (meminfo.nr_banks != 0) //說明記憶體被初始化過
803 squash_mem_tags(tags);//如果是tag list,那麼如果系統已經建立了預設的meminfo.nr_banks,清除tags中關於MEM的引數,以免再次被初始化
804 save_atags(tags);
805 parse_tags(tags);//做出一些針對各個tags的處理
806 }
803行squash_mem_tags定義如下(arch/arm/kernel/setup.c)
static void __init squash_mem_tags(struct tag *tag)
{
for (; tag->hdr.size; tag = tag_next(tag))
if (tag->hdr.tag == ATAG_MEM)
tag->hdr.tag = ATAG_NONE;
}
這個函式功能是清除tags中關於MEM的引數,以免再次被初始化
804行save_atags定義如下(arch/arm/kernel/atags.c)
#define BOOT_PARAMS_SIZE 1536
static char __initdata atags_copy[BOOT_PARAMS_SIZE];
void __init save_atags(const struct tag *tags)
{
memcpy(atags_copy, tags, sizeof(atags_copy));
}
805行parse_tags定義如下(arch/arm/kernel/setup.c)
static void __init parse_tags(const struct tag *t)
{
for (; t->hdr.size; t = tag_next(t))
if (!parse_tag(t)) //針對每個tag 呼叫parse_tag 函式
printk(KERN_WARNING
"Ignoring unrecognised tag 0x%08x\n",
t->hdr.tag);
}
這個函式解析核心引數列表,然後呼叫核心引數列表的處理函式對這些引數進行處理。比如,如果列表為命令列,則最終會用parse_tag_cmdlin函式進行解析,這個函式用_tagtable編譯連線到了核心裡,其中如果U-boot傳入ATAG_CMDLINE,則使用U-boot傳入的 bootargs覆蓋default_command_line
static int __init parse_tag(const struct tag *tag)
{
extern struct tagtable __tagtable_begin, __tagtable_end;
struct tagtable *t;
for (t = &__tagtable_begin; t < &__tagtable_end; t++) //遍歷tagtable列表,並呼叫處理函式,
if (tag->hdr.tag == t->tag) {
t->parse(tag);
break;
}
return t < &__tagtable_end;
}
arch/arm/include/asm/setup.h
struct tagtable {
__u32 tag;
int (*parse)(const struct tag *);
};
#define tag_next(t) ((struct tag *)((__u32 *)(t) + (t)->hdr.size))
#define tag_size(type) ((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
#define for_each_tag(t,base) \
for (t = base; t->hdr.size; t = tag_next(t))
#ifdef __KERNEL__
#define __tag __used __attribute__((__section__(".taglist.init")))
#define __tagtable(tag, fn) \
static struct tagtable __tagtable_##fn __tag = { tag, fn }
這個tagtable 列表 是怎麼形成的?
如arch/arm/kernel/setup.c
556 static int __init parse_tag_mem32(const struct tag *tag)
557 {
558 return arm_add_memory(tag->u.mem.start, tag->u.mem.size);
559 }
560
561 __tagtable(ATAG_MEM, parse_tag_mem32);
607 __tagtable(ATAG_SERIAL, parse_tag_serialnr);
608
609 static int __init parse_tag_revision(const struct tag *tag)
610 {
611 system_rev = tag->u.revision.rev;
612 return 0;
613 }
614
615 __tagtable(ATAG_REVISION, parse_tag_revision);
618 static int __init parse_tag_cmdline(const struct tag *tag)
619 {
620 strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
621 return 0;
622 }
623
624 __tagtable(ATAG_CMDLINE, parse_tag_cmdline);
根據前面相關巨集定義,__tagtable(ATAG_CMDLINE, parse_tag_cmdline)展開後為
static struct tagtable __tagtable_parse_tag_cmdline __used __attribute__((__section__(".taglist.init"))) = { ATAG_CMDLINE, parse_tag_cmdline }
再參看arch/arm/kernel/vmlinux.lds.S檔案
34 __proc_info_begin = .;
35 *(.proc.info.init)
36 __proc_info_end = .;
37 __arch_info_begin = .;
38 *(.arch.info.init)
39 __arch_info_end = .;
40 __tagtable_begin = .;
41 *(.taglist.init)
42 __tagtable_end = .;
tagtable 列表編譯連線後被存放在.taglist.init中。
從808行到811行是init_mm的初始化,記錄核心程式碼的起始,結束虛擬地址
808 init_mm.start_code = (unsigned long) _text; 核心程式碼段開始
809 init_mm.end_code = (unsigned long) _etext; 核心程式碼段結束
810 init_mm.end_data = (unsigned long) _edata; 核心資料段開始
811 init_mm.brk = (unsigned long) _end; 核心資料段結束
_text,_etext,_edata,_end參見arch/arm/kernel/vmlinux.lds.S 連結指令碼,init_mm定義在mm/init-mm.c
12 #ifndef INIT_MM_CONTEXT
13 #define INIT_MM_CONTEXT(name)
14 #endif
15
16 struct mm_struct init_mm = {
17 .mm_rb = RB_ROOT,
18 .pgd = swapper_pg_dir,
19 .mm_users = ATOMIC_INIT(2),
20 .mm_count = ATOMIC_INIT(1),
21 .mmap_sem = __RWSEM_INITIALIZER(init_mm.mmap_sem),
22 .page_table_lock = __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock),
23 .mmlist = LIST_HEAD_INIT(init_mm.mmlist),
24 .cpu_vm_mask = CPU_MASK_ALL,
25 INIT_MM_CONTEXT(init_mm)
26 };
每一個任務都有一個mm_struct結構管理任務記憶體空間,init_mm是核心的mm_struct,設定* pgd=swapper_pg_dir,swapper_pg_dir是核心的頁目錄,在arm體系結構有16k,所以init_mm定義了整個 kernel的記憶體空間,下面我們會碰到核心執行緒,所有的核心執行緒都使用核心空間,擁有和核心同樣的訪問許可權。對於記憶體管理後面再做詳細分析。
mm_struct結構定義在include/linux/mm_types.h中,
struct mm_struct {
struct vm_area_struct * mmap; /* list of VMAs */
struct rb_root mm_rb;
struct vm_area_struct * mmap_cache; /* last find_vma result */
#ifdef CONFIG_MMU
unsigned long (*get_unmapped_area) (struct file *filp,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags);
void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
#endif
unsigned long mmap_base; /* base of mmap area */
unsigned long task_size; /* size of task vm space */
unsigned long cached_hole_size; /* if non-zero, the largest hole below free_area_cache */
unsigned long free_area_cache; /* first hole of size cached_hole_size or larger */
pgd_t * pgd;
atomic_t mm_users; /* How many users with user space? */
atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */
int map_count; /* number of VMAs */
struct rw_semaphore mmap_sem;
spinlock_t page_table_lock; /* Protects page tables and some counters */
struct list_head mmlist; /* List of maybe swapped mm's. These are globally strung
* together off init_mm.mmlist, and are protected
* by mmlist_lock
*/
unsigned long hiwater_rss; /* High-watermark of RSS usage */
unsigned long hiwater_vm; /* High-water virtual memory usage */
unsigned long total_vm, locked_vm, shared_vm, exec_vm;
unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack;
unsigned long arg_start, arg_end, env_start, env_end;
unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
/*
* Special counters, in some configurations protected by the
* page_table_lock, in other configurations by being atomic.
*/
struct mm_rss_stat rss_stat;
struct linux_binfmt *binfmt;
cpumask_t cpu_vm_mask;
/* Architecture-specific MM context */
mm_context_t context;
/* Swap token stuff */
/*
* Last value of global fault stamp as seen by this process.
* In other words, this value gives an indication of how long
* it has been since this task got the token.
* Look at mm/thrash.c
*/
unsigned int faultstamp;
unsigned int token_priority;
unsigned int last_interval;
unsigned long flags; /* Must use atomic bitops to access the bits */
struct core_state *core_state; /* coredumping support */
#ifdef CONFIG_AIO
spinlock_t ioctx_lock;
struct hlist_head ioctx_list;
#endif
#ifdef CONFIG_MM_OWNER
/*
* "owner" points to a task that is regarded as the canonical
* user/owner of this mm. All of the following must be true in
* order for it to be changed:
*
* current == mm->owner
* current->mm != mm
* new_owner->mm == mm
* new_owner->alloc_lock is held
*/
struct task_struct *owner;
#endif
#ifdef CONFIG_PROC_FS
/* store ref to file /proc//exe symlink points to */
struct file *exe_file;
unsigned long num_exe_file_vmas;
#endif
#ifdef CONFIG_MMU_NOTIFIER
struct mmu_notifier_mm *mmu_notifier_mm;
#endif
};
第814行,下面是對命令列的處理,剛才在引數列表處理parse_tag_cmdline函式已把命令列拷貝到了from空間(from前面定義成了default_command_line),這裡814行是把命令列拷貝到boot_command_line,boot_command_line在後面的parse_early_param為用到,boot_command_line在init/main.c中定義
char __initdata boot_command_line[COMMAND_LINE_SIZE];指定變數存放在__initdata區
第817行,填充cmd_line,以備以後使用,維護boot_command_line,cmd_line在127行定義
static char __initdata cmd_line[COMMAND_LINE_SIZE];指定變數存放在__initdata區
__initdata在前面的分析中出現了好幾次,在這裡我們來對它詳細分析
Linux在arch/arm/kernel/vmlinux.lds.S中定義了.init段。__init和__initdata屬性的資料都在這個段中,當核心啟動完畢後,這個段中的記憶體會被釋放掉供其他使用。
__init和__initdata巨集定義如下(include/linux/init.h):
#define __init __section(.init.text) __cold notrace
#define __initdata __section(.init.data)
vmlinux.lds.S有如下內容:
/* arch/arm/kernel/vmlinux.lds.S*/
20 SECTIONS
21 {
22 #ifdef CONFIG_XIP_KERNEL
23 . = XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);
24 #else
25 . = PAGE_OFFSET + TEXT_OFFSET;
26 #endif
27
28 .init : { /* Init code and data */
29 _stext = .;
30 _sinittext = .;
31 HEAD_TEXT
32 INIT_TEXT
33 _einittext = .;
34 __proc_info_begin = .;
35 *(.proc.info.init)
36 __proc_info_end = .;
37 __arch_info_begin = .;
38 *(.arch.info.init)
39 __arch_info_end = .;
40 __tagtable_begin = .;
41 *(.taglist.init)
42 __tagtable_end = .;
43
44 INIT_SETUP(16)
45
46 INIT_CALLS
47 CON_INITCALL
48 SECURITY_INITCALL
49 INIT_RAM_FS
50
51 #ifndef CONFIG_XIP_KERNEL
52 __init_begin = _stext;
53 INIT_DATA
54 #endif
55 }
第32的INIT_TEXT和第53行INIT_DATA在include/asm-generic/vmlinux.lds.h定義如下
457 /* init and exit section handling */
458 #define INIT_DATA \
459 *(.init.data) \
460 DEV_DISCARD(init.data) \
461 CPU_DISCARD(init.data) \
462 MEM_DISCARD(init.data) \
463 KERNEL_CTORS() \
464 *(.init.rodata) \
465 MCOUNT_REC() \
466 DEV_DISCARD(init.rodata) \
467 CPU_DISCARD(init.rodata) \
468 MEM_DISCARD(init.rodata)
469