Chinaunix首页 | 论坛 | 博客
  • 博客访问: 325651
  • 博文数量: 163
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2014-11-23 17:54
个人简介

做一个“好”人... 思想上会思考; 生活上有追求; 技术上不停步; 工作上有担当;

文章分类

全部博文(163)

文章存档

2016年(1)

2015年(143)

2014年(19)

我的朋友

分类: LINUX

2015-11-01 22:59:16

执行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
470 #define INIT_TEXT                                                       \
471         *(.init.text)                                                   \
472         DEV_DISCARD(init.text)                                          \
473         CPU_DISCARD(init.text)                                          \
474         MEM_DISCARD(init.text)
475
可以发现__init对应的section(.init.text)和__initdata对应的section(.init.data)都在.init 段中。同样,这里定义的其他一些section也都会在使用完后被释放,如.init.setup,.initcall1.init等。
释放memory的大小会在系统启动过程中打印出来:
eth0: link up
IP-Config: Complete:
device=eth0, addr=192.168.167.15, mask=255.255.255.0, gw=192.168.1.1,
host=192.168.167.15, domain=, nis-domain=(none),
bootserver=192.168.1.8, rootserver=192.168.1.54, rootpath=
Looking up port of RPC 100003/2 on 192.168.167.170
Looking up port of RPC 100005/1 on 192.168.167.170
VFS: Mounted root (nfs filesystem).
Freeing init memory: 128K

在分析820行的parse_early_param函数前,先说一下系统启动时对bootloader传递参数的初始化,即linux启动参数的实现,启动参数的实现,我们知道boot传递给内核的参数都是 "name_varibale=value"这种形式的,如下:
CONFIG_CMDLINE="root=/dev/mtdblock3 init=/linuxrc console=ttySAC0,115200 mem=64M"
那么内核如何知道传递进来的参数该怎么去处理呢?
内核是通过__setup宏或者early_param宏来将参数与参数所处理的函数相关联起来的。我们接下来就来看这个宏以及相关的结构的定义:
include/linux/init.h
230 #define __setup_param(str, unique_id, fn, early)                        \
231         static const char __setup_str_##unique_id[] __initconst \
232                 __aligned(1) = str; \
233         static struct obs_kernel_param __setup_##unique_id      \
234                 __used __section(.init.setup)                   \
235                 __attribute__((aligned((sizeof(long)))))        \
236                 = { __setup_str_##unique_id, fn, early }
237
238 #define __setup(str, fn)                                        \
239         __setup_param(str, fn, fn, 0)
240
241 /* NOTE: fn is as per module_param, not __setup!  Emits warning if fn
242  * returns non-zero. */
243 #define early_param(str, fn)                                    \
244         __setup_param(str, fn, fn, 1)
看起来很复杂。 首先setup宏第一个参数是一个key。比如"netdev="这样的,而第二个参数是这个key对应的处理函数。这里要注意相同的handler能联系到不同的key。__setup与early_param不同的是,early_param宏注册的内核选项必须要在其他内核选项之前被处理。在函数start_kernel中,parse_early_param处理early_param定义的参数,parse_args处理__setup定义的参数。
early_param和setup唯一不同的就是传递给__setup_param的最后一个参数,这个参数下面会说明,而接下来_setup_param定义了一个struct obs_kernel_param类型的结构,然后通过_section宏,使这个变量在链接的时候能够放置在段.init.setup(后面会详细介绍内核中的这些初始化段).

接下来来看struct obs_kernel_param结构:
include/linux/init.h
218 struct obs_kernel_param {
219         const char *str;
220         int (*setup_func)(char *);
221         int early;
222 };

前两个参数很简单,一个是key,一个是handler。最后一个参数其实也就是类似于优先级的一个flag,因为传递给内核的参数中,有一些需要比另外的一些更早的解析。(这也就是为什么early_param和setup传递的最后一个参数的不同的原因了。
启动参数的实现
1,所有的系统启动参数都是由形如
static int __init init_setup(char *str)
的函数来支持的
init/main.c
static int __init init_setup(char *str)
{
    unsigned int i;

    execute_command = str;
    for (i = 1; i < MAX_INIT_ARGS; i++)
        argv_init[i] = NULL;
    return 1;
}
__setup("init=", init_setup);
注:(include/linux/init.h):
#define __init          __section(.init.text) __cold notrace申明所有的启动参数支持函数都放入.init.text段
2.1,用__setup宏来导出参数的支持函数
__setup("init=", init_setup);
展开后就是如下的形式

static const char __setup_str_init_setup[] __initdata = "init=";  
static struct obs_kernel_param __setup_init_setup              
        __used __section__(".init.setup")
        __attribute__((aligned((sizeof(long)))))  
        = { __setup_str_init_setup, init_setup, 0 };//"init=",init_setup,0
也就是说,启动参数(函数指针)被封装到obs_kernel_param结构中,
所有的内核启动参数形成内核映像.init.setup段中的一个
obs_kernel_param数组

2.2用early_param宏来申明需要'早期'处理的启动参数,例如在
arch/arm/kernel/setup.c就有如下的申明:
468 early_param("mem", early_mem);
展开后和__setup是一样的只是early参数不一样,因此会在do_early_param
中被处理
443 static int __init early_mem(char *p)
444 {
445         static int usermem __initdata = 0;
446         unsigned long size, start;
447         char *endp;
448
449         /*
453          */
454         if (usermem == 0) {
455                 usermem = 1;
456                 meminfo.nr_banks = 0;
457         }
458
459         start = PHYS_OFFSET;
460         size  = memparse(p, &endp);
461         if (*endp == '@')
462                 start = memparse(endp + 1, NULL);
463
464         arm_add_memory(start, size);
465
466         return 0;
467 }

3,内核对启动参数的解析:下面函数历遍obs_kernel_param数组,调用
支持函数
static int __init do_early_param(char *param, char *val)
这个函数在parse_early_param中被调用,而parse_early_param在start_kernel
中被调用,parse_early_param之后的parse_args会调用下面函数
static int __init obsolete_checksetup(char *line)
这两个函数后面会讲到。
init/main.c中启动参数申明列表:
early_param("nosmp", nosmp);
early_param("nr_cpus", nrcpus);
early_param("maxcpus", maxcpus);
__setup("reset_devices", set_reset_devices);
early_param("debug", debug_kernel);
early_param("quiet", quiet_kernel);
early_param("loglevel", loglevel);
__setup("init=", init_setup);
__setup("rdinit=", rdinit_setup);
arch/arm/kernel/setup.c中启动参数申明列表:
__setup("fpe=", fpe_setup);
early_param("mem", early_mem);
early_param("elfcorehdr", setup_elfcorehdr);

接下来我们来看内核解析bootloader传递给内核的参数的步骤:
先看下面的图:

可以看到内核首先通过parse_early_param来解析优先级更高的,也就是需要被更早解析的命令行参数,然后通过parse_ares来解析一般的命令行参数.
现在再来看看start_arch函数中第820行的parse_early_param函数
init/main.c
void __init parse_early_options(char *cmdline)
{
    parse_args("early options", cmdline, NULL, 0, do_early_param);
}

/* Arch code calls this early on, or if not, just before other parsing. */
void __init parse_early_param(void)
{
    static __initdata int done = 0;
    static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];

    if (done)
        return;

    /* All fall through to do_early_param. */
    strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
    parse_early_options(tmp_cmdline);
    done = 1;
}
在上面我们可以看到最终调用的是 parse_args("early options", cmdline, NULL, 0, do_early_param);它调用do_early_param函数处理early_param定义的参数函数
这个函数还另一种调用形式
parse_args("Booting kernel", static_command_line, __start___param,_stop___param - __start___param,&unknown_bootoption); 它调用unknown_bootoption函数处理__setup定义的参数,unknown_bootoption函数在init/main.c中定义如下:
static int __init unknown_bootoption(char *param, char *val)
{
    /* Change NUL term back to "=", to make "param" the whole string. */
    if (val) {
        /* param=val or param="val"? */
        if (val == param+strlen(param)+1)
            val[-1] = '=';
        else if (val == param+strlen(param)+2) {
            val[-2] = '=';
            memmove(val-1, val, strlen(val)+1);
            val--;
        } else
            BUG();
    }

    /* Handle obsolete-style parameters */
    if (obsolete_checksetup(param))
        return 0;

    /* Unused module parameter. */
    if (strchr(param, '.') && (!val || strchr(param, '.') < val))
        return 0;

    if (panic_later)
        return 0;

    if (val) {
        /* Environment option */
        unsigned int i;
        for (i = 0; envp_init[i]; i++) {
            if (i == MAX_INIT_ENVS) {
                panic_later = "Too many boot env vars at `%s'";
                panic_param = param;
            }
            if (!strncmp(param, envp_init[i], val - param))
                break;
        }
        envp_init[i] = param;
    } else {
        /* Command line option */
        unsigned int i;
        for (i = 0; argv_init[i]; i++) {
            if (i == MAX_INIT_ARGS) {
                panic_later = "Too many boot init vars at `%s'";
                panic_param = param;
            }
        }
        argv_init[i] = param;
    }
    return 0;
}
在这个函数中它调用了obsolete_checksetup函数,该函数也定义在init/main.c中
static int __init obsolete_checksetup(char *line)
{
    const struct obs_kernel_param *p;
    int had_early_param = 0;

    p = __setup_start;
    do {
        int n = strlen(p->str);
        if (!strncmp(line, p->str, n)) {
            if (p->early) {
                /* Already done in parse_early_param?
                 * (Needs exact match on param part).
                 * Keep iterating, as we can have early
                 * params and __setups of same names 8( */
                if (line[n] == '\0' || line[n] == '=')
                    had_early_param = 1;
            } else if (!p->setup_func) {
                printk(KERN_WARNING "Parameter %s is obsolete,"
                       " ignored\n", p->str);
                return 1;
            } else if (p->setup_func(line + n)) //调用支持函数
                return 1;
        }
        p++;
    } while (p < __setup_end);

    return had_early_param;
}
我们再来看一下do_early_param函数,它在init/main.c中
static int __init do_early_param(char *param, char *val)
{
    const struct obs_kernel_param *p;
    这里的__setup_start和_-setup_end分别是.init.setup段的起始和结束的地址
    for (p = __setup_start; p < __setup_end; p++) { 如果没有early标志则跳过
        if ((p->early && strcmp(param, p->str) == 0) ||
            (strcmp(param, "console") == 0 &&
             strcmp(p->str, "earlycon") == 0)
        ) {  
            if (p->setup_func(val) != 0)  调用处理函数
                printk(KERN_WARNING
                       "Malformed early option '%s'\n", param);
        }
    }
    /* We accept everything at this stage. */
    return 0;
}
parse_args在kernel/params.c中定义,注意它与前面讲到的parse_args区别。
/* Args looks like "foo=bar,bar2 baz=fuz wiz". */
int parse_args(const char *name,
           char *args,
           const struct kernel_param *params,
           unsigned num,
           int (*unknown)(char *param, char *val))
{
    char *param, *val;

    DEBUGP("Parsing ARGS: %s\n", args);

    /* Chew leading spaces */
    args = skip_spaces(args);

    while (*args) {
        int ret;
        int irq_was_disabled;

        args = next_arg(args, ¶m, &val);
        irq_was_disabled = irqs_disabled();
        ret = parse_one(param, val, params, num, unknown);
        if (irq_was_disabled && !irqs_disabled()) {
            printk(KERN_WARNING "parse_args(): option '%s' enabled "
                    "irq's!\n", param);
        }
        switch (ret) {
        case -ENOENT:
            printk(KERN_ERR "%s: Unknown parameter `%s'\n",
                   name, param);
            return ret;
        case -ENOSPC:
            printk(KERN_ERR
                   "%s: `%s' too large for parameter `%s'\n",
                   name, val ?: "", param);
            return ret;
        case 0:
            break;
        default:
            printk(KERN_ERR
                   "%s: `%s' invalid for parameter `%s'\n",
                   name, val ?: "", param);
            return ret;
        }
    }

    /* All parsed OK. */
    return 0;
}

接下来看下,一些内置模块的参数处理的问题。传递给模块的参数都是通过module_param来实现的:
include/linux/moduleparam.h
 99 #define module_param(name, type, perm)                          \
100         module_param_named(name, name, type, perm)

113 #define module_param_named(name, value, type, perm)                        \
114         param_check_##type(name, &(value));                                \
115         module_param_cb(name, ¶m_ops_##type, &value, perm);            \
116         __MODULE_PARM_TYPE(name, #type)

126 #define module_param_cb(name, ops, arg, perm)                                     \
127         __module_param_call(MODULE_PARAM_PREFIX,                                  \
128                             name, ops, arg, __same_type((arg), bool *), perm    )

142 #define __module_param_call(prefix, name, ops, arg, isbool, perm)       \
143         /* Default value instead of permissions? */                     \
144         static int __param_perm_check_##name __attribute__((unused)) =  \
145         BUILD_BUG_ON_ZERO((perm) < 0 || (perm) > 0777 || ((perm) & 2))  \
146         + BUILD_BUG_ON_ZERO(sizeof(""prefix) > MAX_PARAM_PREFIX_LEN);   \
147         static const char __param_str_##name[] = prefix #name;          \
148         static struct kernel_param __moduleparam_const __param_##name   \
149         __used                                                          \
150     __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *))))     \
151         = { __param_str_##name, ops, perm, isbool ? KPARAM_ISBOOL : 0,  \
152             { arg } }
这里也就是填充了 struct kernel_param的结构体,并将这个变量标记为__param段,以便于链接器将此变量装载到指定的段
接下来我们来看struct kernel_param这个结构:
include/linux/moduleparam.h
struct kernel_param;

struct kernel_param_ops {
    /* Returns 0, or -errno.  arg is in kp->arg. */
    int (*set)(const char *val, const struct kernel_param *kp);设置参数的函数
    /* Returns length written or -errno.  Buffer is 4k (ie. be short!) */
    int (*get)(char *buffer, const struct kernel_param *kp);读取参数的函数
    /* Optional function to free kp->arg when module unloaded. */
    void (*free)(void *arg);
};

/* Flag bits for kernel_param.flags */
#define KPARAM_ISBOOL        2

struct kernel_param {
    const char *name;
    const struct kernel_param_ops *ops;
    u16 perm;
    u16 flags;
    union { 传递给上面kernel_param_ops中两个函数的参数
        void *arg;
        const struct kparam_string *str;
        const struct kparam_array *arr;
    };
};

/* Special one for strings we want to copy into */
struct kparam_string {
    unsigned int maxlen;
    char *string;
};

/* Special one for arrays */
struct kparam_array
{
    unsigned int max;
    unsigned int *num;
    const struct kernel_param_ops *ops;
    unsigned int elemsize;
    void *elem;
};

接下来来看parse_one函数,其中early和一般的pase都是通过这个函数来解析:
kernel/params.c
static inline char dash2underscore(char c)
{
    if (c == '-')
        return '_';
    return c;
}

static inline int parameq(const char *input, const char *paramname)
{
    unsigned int i;
    for (i = 0; dash2underscore(input[i]) == paramname[i]; i++)
        if (input[i] == '\0')
            return 1;
    return 0;
}

static int parse_one(char *param,
             char *val,
             const struct kernel_param *params,
             unsigned num_params,
             int (*handle_unknown)(char *param, char *val))
{
    unsigned int i;
    int err;
     如果是early_param则直接跳过这步,而非early的,则要通过这步来设置一些内置模块的参数。
    /* Find parameter */
    for (i = 0; i < num_params; i++) {
        if (parameq(param, params[i].name)) {
            /* Noone handled NULL, so do it here. */
            if (!val && params[i].ops->set != param_set_bool) 调用参数设置函数来设置对应的参数。
                return -EINVAL;
            DEBUGP("They are equal!  Calling %p\n",
                   params[i].ops->set);
            mutex_lock(¶m_lock);
            err = params[i].ops->set(val, ¶ms[i]);
            mutex_unlock(¶m_lock);
            return err;
        }
    }

    if (handle_unknown) {
        DEBUGP("Unknown argument: calling %p\n", handle_unknown);
        return handle_unknown(param, val);
    }

    DEBUGP("Unknown argument `%s'\n", param);
    return -ENOENT;
}
这里之所以把所有的内核初始化参数都放在一个段里,主要是为了初始化成功后,释放这些空间。
我们下来看一下内核编译完毕后的一些段的位置:


右边就是每个段定义的相关的宏。在__init_start和__init_end之间的段严格按照顺序进行初始化,比如 core_initcall宏修饰的函数就要比arch_initcall宏定义的函数优先级高,所以就更早的调用。这个特性就可以使我们将一些优先级比较高的初始化函数放入相应比较高的段。。
这里要注意,每个init宏,基本都会有个对应的exit宏,比如__initial和__exit等等。。
内核中修饰的宏很多,我们下面只介绍一些常用的:
__devinit 用来标记一个设备的初始化函数,比如pci设备的probe函数,就是用这个宏来修饰。
__initcall这个已被废弃,现在实现为__devinit的别名。
剩下的宏需要去看内核的相关文档。。

最后还要注意几点:
1 当模块被静态的加入到内核中时,module_init标记的函数只会被执行一次,因此当初始化成功后,内核会释放掉这块的内存。
2 当模块静态假如内核,module_exit在链接时就会被删除掉。
这里的优化还有很多,比如热拔插如果不支持的话,很多设备的初始化函数都会在执行完一遍后,被丢弃掉。
这里可以看到,如果模块被静态的编译进内核时,内核所做的内存优化会更多,虽然损失了更多的灵活性 


回到start_arch中第822行,来看arm_memblock_init(&meminfo, mdesc);函数
arch/arm/mm/init.c
270 void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc)
271 {
272         int i;
273
274         memblock_init();
275         for (i = 0; i < mi->nr_banks; i++)
276                 memblock_add(mi->bank[i].start, mi->bank[i].size);
277
278         /* Register the kernel text, kernel data and initrd with memblock. */
279 #ifdef CONFIG_XIP_KERNEL
280         memblock_reserve(__pa(_data), _end - _data);
281 #else
282         memblock_reserve(__pa(_stext), _end - _stext);
283 #endif
284 #ifdef CONFIG_BLK_DEV_INITRD
285         if (phys_initrd_size) {
286                 memblock_reserve(phys_initrd_start, phys_initrd_size);
287
288                 /* Now convert initrd to virtual addresses */
289                 initrd_start = __phys_to_virt(phys_initrd_start);
290                 initrd_end = initrd_start + phys_initrd_size;
291         }
292 #endif
293
294         arm_mm_memblock_reserve();
295
296         /* reserve any platform specific memblock areas */
297         if (mdesc->reserve)
298                 mdesc->reserve();
299
300         memblock_analyze();
301         memblock_dump_all();
302 }
这个函数保留了内存, 包括linux内核占用的代码数据段空间, initrd占用的空间 以及一些平台相关的内存
第274行,memblock_init()在mm/memblock.c里面被定义。
void __init memblock_init(void)
{
    /* Create a dummy zero size MEMBLOCK which will get coalesced away later.
     * This simplifies the memblock_add() code below...
     */
    memblock.memory.region[0].base = 0;
    memblock.memory.region[0].size = 0;
    memblock.memory.cnt = 1;

    /* Ditto. */
    memblock.reserved.region[0].base = 0;
    memblock.reserved.region[0].size = 0;
    memblock.reserved.cnt = 1;
}
其作用就是初始化memblock这个结构。memblock包含两个重要的成员,分别是memblock.memory和memblock.reserved.其分别代表系统中可用的内存和已经被保留的内存。
memblock.memory和memblock.reserved被定义为以下结构:include/linux/memblock.h
#define MAX_MEMBLOCK_REGIONS 128

struct memblock_property {
    u64 base;
    u64 size;
};

struct memblock_region {
    unsigned long cnt;
    u64 size;
    struct memblock_property region[MAX_MEMBLOCK_REGIONS+1];
};

struct memblock {
    unsigned long debug;
    u64 rmo_size;
    struct memblock_region memory;
    struct memblock_region reserved;
};
276行,内存原始数据由u-boot传入,并在start_arch中调用parse_tags对它初始化(其详细过程请参看我后面的u-boot与linux内核间的参数传递过程分析),在初始化完memblock_init后,memblock_add调用memblock_add_region加入原始内存数据,
阅读(757) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~