1. 程式人生 > >Linux核心啟動第二階段之setup_arch函式分析

Linux核心啟動第二階段之setup_arch函式分析

轉自: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