start_kernel()之setup_arch()函数详解

该函数主要是根据处理器硬件平台具体的机器型号来设置系统。解析Linux系统的命令行,设置0号jin cheng 的内存描述结构init_mm,系统内存管理初始化,统计并注册系统各种资源,以及其他杂项的初始化功能。注意这个函数在具体的硬件平台上是有所差别的,我们这里以ARM平台为例开始讲解。

下面是代码:

void __init setup_arch(char **cmdline_p)
{
__struct tag *tags = (struct tag *)&init_tags;
__struct machine_desc *mdesc;
__char *from = default_command_line;

__setup_processor();
__mdesc = setup_machine(machine_arch_type);
__machine_name = mdesc->name;

__if (mdesc->soft_reboot)
____reboot_setup("s");

__if (mdesc->param_offset)
____tags = phys_to_virt(mdesc->param_offset);

__/*
__ * If we have the old style parameters, convert them to
__ * a tag list.
__ */
__if (tags->hdr.tag != ATAG_CORE)
____convert_to_tag_list(tags);
__if (tags->hdr.tag != ATAG_CORE)
____tags = (struct tag *)&init_tags;

__if (mdesc->fixup)
____mdesc->fixup(mdesc, tags, &from, &meminfo);

__if (tags->hdr.tag == ATAG_CORE) {
____if (meminfo.nr_banks != 0)
______squash_mem_tags(tags);
____parse_tags(tags);
__}
  init_mm.start_code = (unsigned long) &_text;
__init_mm.end_code   = (unsigned long) &_etext;
__init_mm.end_data   = (unsigned long) &_edata;
__init_mm.brk_   = (unsigned long) &_end;

__memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
__saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
__parse_cmdline(cmdline_p, from);
__paging_init(&meminfo, mdesc);
__request_standard_resources(&meminfo, mdesc);

__/*
__ * Set up various architecture-specific pointers
__ */
__init_arch_irq = mdesc->init_irq;
__system_timer = mdesc->timer;
__init_machine = mdesc->init_machine;

#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
__conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
__conswitchp = &dummy_con;
#endif
#endif
}
init_mm.start_code = (unsigned long) &_text;
__init_mm.end_code   = (unsigned long) &_etext;
__init_mm.end_data   = (unsigned long) &_edata;
__init_mm.brk_   = (unsigned long) &_end;

__memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
__saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
__parse_cmdline(cmdline_p, from);
__paging_init(&meminfo, mdesc);
__request_standard_resources(&meminfo, mdesc);

__/*
__ * Set up various architecture-specific pointers
__ */
__init_arch_irq = mdesc->init_irq;
__system_timer = mdesc->timer;
__init_machine = mdesc->init_machine;

#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
__conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
__conswitchp = &dummy_con;
#endif
#endif
}
init_mm.start_code = (unsigned long) &_text;
__init_mm.end_code   = (unsigned long) &_etext;
__init_mm.end_data   = (unsigned long) &_edata;
__init_mm.brk_   = (unsigned long) &_end;

__memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
__saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
__parse_cmdline(cmdline_p, from);
__paging_init(&meminfo, mdesc);
__request_standard_resources(&meminfo, mdesc);

__/*
__ * Set up various architecture-specific pointers
__ */
__init_arch_irq = mdesc->init_irq;
__system_timer = mdesc->timer;
__init_machine = mdesc->init_machine;

#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
__conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
__conswitchp = &dummy_con;
#endif
#endif
}

函数代码如上面所示,现在我们从这个函数的入参开始,函数入参为二级指针,指向一个char型变量的二级指针,那么在调用这个函数的时候,传递的参数是什么呢?我们回过头来看看。

char * command_line;
setup_arch(&command_line);

如上面所示,在start_kernel中,定义了一个char 型的指针变量command_line,然后将这个指针变量再取地址作为函数入参传递给函数setup_arch。那么这个函数入参在函数内部有什么作用呢?在代码中能够看到函数入参又作为函数parse_cmdline函数的第一个入参使用了,其实在这个函数内,才是给这个字符串指针赋值。后面到这个函数的时候我们在做详细的介绍。

下面我们从函数的第一行开始,struct tag *tags = (struct tag *)&init_tags;其中struct tag的结构定义如下:

typedef struct
{
____le16____tagIdent;
____le16____descVersion;
__uint8_t___tagChecksum;
__uint8_t___reserved;
____le16____tagSerialNum;
____le16____descCRC;
____le16____descCRCLength;
____le32____tagLocation;
} __attribute__ ((packed)) tag;

/*
 * Below are truly Linux-specific types that should never collide with
 * any application/library that wants linux/types.h.
 */

#ifdef __CHECKER__
#define __bitwise __attribute__((bitwise))
#else
#define __bitwise
#endif

typedef __u16 __bitwise __le16;
typedef __u16 __bitwise __be16;
typedef __u32 __bitwise __le32;
typedef __u32 __bitwise __be32;
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
typedef __u64 __bitwise __le64;
typedef __u64 __bitwise __be64;
#endif

如上面的代码,struct tag的结构成员有那么多,每个成员的类型下面都有定义。但是具体这里面的成员有什么作用,现在还不清楚,后面慢慢再了解,其中__attribute__((packed))修饰的作用就是让这个结构体在内存中的位置保持单字节边界对齐。

现在还是回到代码中,struct machine_desc *mdesc;定义了一个struct machine_desc类型的结构指针变量,现在来看看这个结构又包含哪些变量的内容呢?

struct machine_desc {
__/*
__ * Note! The first four elements are used
__ * by assembler code in head-armv.S
__ */
__unsigned int____nr;___/* architecture number__*/
__unsigned int____phys_ram;_/* start of physical ram */
__unsigned int____phys_io;__/* start of physical io_*/
__unsigned int____io_pg_offst;__/* byte offset for io 
____________ * page tabe entry__*/

__const char____*name;____/* architecture name__*/
__unsigned int____param_offset;_/* parameter page_*/

__unsigned int____video_start;__/* start of video RAM_*/
__unsigned int____video_end;__/* end of video RAM_*/

__unsigned int____reserve_lp0 :1;_/* never has lp0__*/
__unsigned int____reserve_lp1 :1;_/* never has lp1__*/
__unsigned int____reserve_lp2 :1;_/* never has lp2__*/
__unsigned int____soft_reboot :1;_/* soft reboot____*/
__void______(*fixup)(struct machine_desc *,
__________ struct tag *, char **,
__________ struct meminfo *);
__void______(*map_io)(void);/* IO mapping function__*/
__void______(*init_irq)(void);
__struct sys_timer__*timer;___/* system tick timer__*/
__void______(*init_machine)(void);
};

这里面每一个成员有什么作用,现在不做详细的介绍,后面再使用的时候我们再说明。

代码的第三行,char *from = default_command_line;定义一个char型的指针,指向了default_command_line,这个变量是一个全局变量,其定义如下:

static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;

#define COMMAND_LINE_SIZE 1024

代码的第四行调用函数setup_processor();这个函数的定义如下:

static void __init setup_processor(void)
{
__extern struct proc_info_list __proc_info_begin, __proc_info_end;
__struct proc_info_list *list;

__/*
__ * locate processor in the list of supported processor
__ * types.  The linker builds this table for us from the
__ * entries in arch/arm/mm/proc-*.S
__ */
__for (list = &__proc_info_begin; list < &__proc_info_end ; list++)
____if ((processor_id & list->cpu_mask) == list->cpu_val)
______break;

__/*
__ * If processor type is unrecognised, then we
__ * can do nothing...
__ */
__if (list >= &__proc_info_end) {
____printk("CPU configuration botched (ID %08x), unable "
____       "to continue.\n", processor_id);
____while (1);
__}

__cpu_name = list->cpu_name;

#ifdef MULTI_CPU
__processor = *list->proc;
#endif
#ifdef MULTI_TLB
__cpu_tlb = *list->tlb;
#endif
#ifdef MULTI_USER
__cpu_user = *list->user;
#endif
#ifdef MULTI_CACHE
__cpu_cache = *list->cache;
#endif
__printk("CPU: %s [%08x] revision %d (ARMv%s)\n",
__       cpu_name, processor_id, (int)processor_id & 15,
__       proc_arch[cpu_architecture()]);

__dump_cpu_info();

__sprintf(system_utsname.machine, "%s%c", list->arch_name, ENDIANNESS);
__sprintf(elf_platform, "%s%c", list->elf_name, ENDIANNESS);
__elf_hwcap = list->elf_hwcap;

__cpu_proc_init();
}

下面看一下这个函数的主要实现,首先定义变量proc_info_list类型的指针变量list,这个变量依次遍历__proc_info_begin和__proc_info_end之间的所有成员。那么__proc_info_begin, __proc_info_end是如何定义的呢?

/*
 * Look in  for information about the __proc_info structure.
 */
	.align	2
	.type	__lookup_processor_type_data, %object
__lookup_processor_type_data:
	.long	.                 @ “.”表示当前这行代码编译连接后的虚拟地址
	.long	__proc_info_begin @ proc_info_list结构的开始地址,这是链接地址,也是虚拟地址
	.long	__proc_info_end   @ @proc_info_list结构的结束地址,这是链接地址,也是虚拟地址
	.size	__lookup_processor_type_data, . - __lookup_processor_type_data


#define PROC_INFO							\
	. = ALIGN(4);							\
	VMLINUX_SYMBOL(__proc_info_begin) = .;				\
	*(.proc.info.init)						\
	VMLINUX_SYMBOL(__proc_info_end) = .;

从上面的定义我们可以看出,其实这两个变量就是代表编译号的内核中的一个虚拟地址,这个地址段存放的就是product info的信息,从这里面依次获取信息。

if ((processor_id & list->cpu_mask) == list->cpu_val)break;这个判断如果为真,也就是当前CPU的ID号和list中某一个成员的ID号相同,那么就认为当前的型号就是那个型号,所有的信息都在那段地址中,直接break就好。代码向下运行。然后依次为cpu_name,processor,cpu_tlb,cpu_user,cpu_cache填值。这些变量都是全局变量,整个内核代码中唯一,其定义和类型的定义如下:

#ifdef MULTI_CPU
struct processor processor;
#endif
#ifdef MULTI_TLB
struct cpu_tlb_fns cpu_tlb;
#endif
#ifdef MULTI_USER
struct cpu_user_fns cpu_user;
#endif
#ifdef MULTI_CACHE
struct cpu_cache_fns cpu_cache;
#endif

/*
 * Don't change this structure - ASM code
 * relies on it.
 */
extern struct processor {
__/* check for any bugs */
__void (*_check_bugs)(void);
__/* Set up any processor specifics */
__void (*_proc_init)(void);
__/* Disable any processor specifics */
__void (*_proc_fin)(void);
__/* set the MEMC hardware mappings */
__void (*_set_pgd)(pgd_t *pgd);
__/* XCHG */
__unsigned long (*_xchg_1)(unsigned long x, volatile void *ptr);
__unsigned long (*_xchg_4)(unsigned long x, volatile void *ptr);
} processor;

struct cpu_tlb_fns {
__void (*flush_user_range)(unsigned long, unsigned long, struct vm_area_struct *);
__void (*flush_kern_range)(unsigned long, unsigned long);
__unsigned long tlb_flags;
};

struct cpu_user_fns {
__void (*cpu_clear_user_page)(void *p, unsigned long user);
__void (*cpu_copy_user_page)(void *to, const void *from,
________   unsigned long user);
};

struct cpu_cache_fns {
__void (*flush_kern_all)(void);
__void (*flush_user_all)(void);
__void (*flush_user_range)(unsigned long, unsigned long, unsigned int);

__void (*coherent_kern_range)(unsigned long, unsigned long);
__void (*coherent_user_range)(unsigned long, unsigned long);
__void (*flush_kern_dcache_page)(void *);

__void (*dma_inv_range)(unsigned long, unsigned long);
__void (*dma_clean_range)(unsigned long, unsigned long);
__void (*dma_flush_range)(unsigned long, unsigned long);
};

这样全局变量的信息就都得到了。然后调用dump_cpu_info,将信息打印出来。然后调用cpu_proc_init();函数,下面看看这个函数的定义是如何定义的?

#define cpu_proc_init()   processor._proc_init()

看定义现在知道这个函数是一个函数指针,这个指针在哪里赋值的就要看processor这个变量在哪里被初始化的。回头看上面的代码我们知道processor就是在上面初始化的,processor= *list->proc这个语句,所以想要知道_proc_init()函数到底是哪个函数就要看list中的proc信息。然后返回到函数setup_arch函数中,mdesc = setup_machine(machine_arch_type);这段代码就是获取机器描述结构mdesc的信息。详细代码如下:

static struct machine_desc * __init setup_machine(unsigned int nr)
{
__extern struct machine_desc __arch_info_begin, __arch_info_end;
__struct machine_desc *list;

__/*
__ * locate architecture in the list of supported architectures.
__ */
__for (list = &__arch_info_begin; list < &__arch_info_end; list++)
____if (list->nr == nr)
______break;

__/*
__ * If the architecture type is not recognised, then we
__ * can co nothing...
__ */
__if (list >= &__arch_info_end) {
____printk("Architecture configuration botched (nr %d), unable "
____       "to continue.\n", nr);
____while (1);
__}

__printk("Machine: %s\n", list->name);

__return list;
}

从代码可知,大体结构和上面的函数类似,就是找到介于_arch_info_begin和_arch_info_end之间的那么一个list,然后返回这个list的地址。这里面关键的就是函数入参和_arch_info_begin和_arch_info_end。函数入参为machine_arch_type,这个就是一个宏定义的数值。_arch_info_begin和_arch_info_end也是在汇编中定义号地址的全局变量。其定义的地址如下:

.long  __arch_info_begin

.long  __arch_info_end

在编译的时候,就会将arch_info的信息的结构编译到这两个地址之间。这样找到对应的描述结构之后就返回了,mdesc这个指针变量就指向了这个结构的地址。然后将硬件平台的名字字符串保存到全局变量machine_name中。如果mdesc的软件重启标志soft_reboot不为0,那么将全局字符串reboot_mode设置为"s"当这个变量被初始化为"h"的时候,表示硬件重启。系统默认的情况就是采用的硬件重启。如果mdesc的parm_offset不等于0,那么说明在系统的物理内存基地址加parm_offset处保存了bootloader中设置并传进来的系统参数。保存在这里的参数可以是struct parm_struct格式(老版本),或拓展的struct init_tags格式(新版)。在linux2.6.10版本中,如果传进来的参数是struct parm_struct格式,那么将这个格式转化为struct init_tags格式,然后来解析数据。如果parm_offset为0,那么就不从bootloader中读取系统参数。那么系统将使用保存在struct init_tags格式的全局变量init_tags中的数据作为系统参数。把系统参数(_va(parm_offset))处的数据或者init_tags指针传给struct tag型局部结构指针tags。如果mdesc的fixup()成员函数不空,那么调用fixup()成员函数做一些前期补充初始化系统参数。注意,这个fixup()成员函数很少在处理器中有定义,因此这里很有可能没有被执行。再查看系统参数结构tags中是否设置了核心参数,参数标签字为ATAG_CORE=0x54410001,其中设置的参数包括根文件系统是否只读,内存页的大小,根文件系统设备号。如果设置了核心参数,再检查保存在struct meminfo型全局结构变量meminfo(这个变量的成员都被初始化为0)中的系统内存信息。如果meminfo中的nr_banks不等于0,说明已经被前面的fixup()函数更新过,那么将tags中设置内存参数的结构(标签字为ATG_MEM=0x54410002)清楚掉,即将结构的标签字设置为ATG_NONE=0x00000000,tags中的其他参数不变,然后分析连续排放在struct tag结构变量的地址tags处所有的有效参数,设置标签,从.taglist区根据参数标签找出每个有效标签对应的struct tagtable结构变量,执行struct tagtable结构变量中的标签解析函数解析相应的参数设置标签的内容。在linux2.6.10中用_tagable()宏定义了11个struct tagable全局变量,也就是说linux2.6.10支持11中系统参数通过设置标签到struct tagable全局结构变量中区查找对一个的参数来设置函数来设置参数。

设置struct mem_struct型全局变量init_mm,其定义如下:

struct mm_struct init_mm = INIT_MM(init_mm);

#define INIT_MM(name) \
{_____ _________\
__.mm_rb____= RB_ROOT,________\
__.pgd____= swapper_pg_dir, ______\
__.mm_users_= ATOMIC_INIT(2), ______\
__.mm_count_= ATOMIC_INIT(1), ______\
__.mmap_sem_= __RWSEM_INITIALIZER(name.mmap_sem),_\
__.page_table_lock =  SPIN_LOCK_UNLOCKED, ____\
__.mmlist___= LIST_HEAD_INIT(name.mmlist),____\
__.cpu_vm_mask__= CPU_MASK_ALL,_______\
__.default_kioctx = INIT_KIOCTX(name.default_kioctx, name),_\
}

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 */
__unsigned long (*get_unmapped_area) (struct file *filp,
________unsigned long addr, unsigned long len,
________unsigned long pgoff, unsigned long flags);
__void (*unmap_area) (struct vm_area_struct *area);
__unsigned long mmap_base;____/* base of mmap area */
__unsigned long free_area_cache;____/* first hole */
__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, mm->rss, mm->anon_rss */

__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 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 rss, anon_rss, total_vm, locked_vm, shared_vm;
__unsigned long exec_vm, stack_vm, reserved_vm, def_flags, nr_ptes;

__unsigned long saved_auxv[42]; /* for /proc/PID/auxv */
__unsigned dumpable:1;
__cpumask_t cpu_vm_mask;

__/* Architecture-specific MM context */
__mm_context_t context;

__/* Token based thrashing protection. */
__unsigned long swap_token_time;
__char recent_pagein;

__/* coredumping support */
__int core_waiters;
__struct completion *core_startup_done, core_done;

__/* aio bits */
__rwlock_t____ioctx_list_lock;
__struct kioctx___*ioctx_list;

__struct kioctx___default_kioctx;
};

注意这个变量在声明的时候就已经做了一部分的初始化工作,代码上面都有。将linux内核编译出来的代码段开始和结束的地址,数据段结束地址和整个内核结束地址分别保存到init_mm的start_code,end_code,end_data和brk成员变量中。代码中的_text,_etext,_data,_end就是内核编译时候代码段对应的地址,这些变量都是在*ld.s中定义的位置,这里不再详细的介绍了。

将1024字节的系统默认命令行字符串全局变量default_command_line复制到系统备份命令行字符串全局变量save_command_line中,然后解析default_command_line中的每一个命令。系统认识的系统参数设置命令有一个对应的struct early_parms全局变量,变量中定义了命令参数名以及对应的命令解析函数,所有这些struct early_parms全局结构变量都是通过_early_parm()宏定义的,他们被连续放置在_early_param区中,一条命令行可以设置多个参数命令,两个命令之间只能用空格隔开,参数命令内部不能用空格隔开,同一参数的不同属性用逗号隔开。

下面是详细的解析命令的代码,以及设计到的结构:

/*
 * Initial parsing of the command line.
 */
static void __init parse_cmdline(char **cmdline_p, char *from)
{
__char c = ' ', *to = command_line;
__int len = 0;

__for (;;) {
____if (c == ' ') {
______extern struct early_params __early_begin, __early_end;
______struct early_params *p;

______for (p = &__early_begin; p < &__early_end; p++) {
________int len = strlen(p->arg);

________if (memcmp(from, p->arg, len) == 0) {
__________if (to != command_line)
____________to -= 1;
__________from += len;
__________p->fn(&from);

__________while (*from != ' ' && *from != '\0')
____________from++;
__________break;
________}
______}
____}
____c = *from++;
____if (!c)
______break;
____if (COMMAND_LINE_SIZE <= ++len)
______break;
____*to++ = c;
__}
__*to = '\0';
__*cmdline_p = command_line;
}

/*
 * Early command line parameters.
 */
struct early_params {
__const char *arg;
__void (*fn)(char **p);
};

代码中的early_begin,early_end也是在编译链接脚本中定义的位置。

然后调用paging_init()函数,这个函数的代码如下:

/*
 * paging_init() sets up the page tables, initialises the zone memory
 * maps, and sets up the zero page, bad page and bad page tables.
 */
void __init paging_init(struct meminfo *mi, struct machine_desc *mdesc)
{
__void *zero_page;
__int node;

__bootmem_init(mi);

__memcpy(&meminfo, mi, sizeof(meminfo));

__/*
__ * allocate the zero page.  Note that we count on this going ok.
__ */
__zero_page = alloc_bootmem_low_pages(PAGE_SIZE);

__/*
__ * initialise the page tables.
__ */
__memtable_init(mi);
__if (mdesc->map_io)
____mdesc->map_io();
__flush_tlb_all();

__/*
__ * initialise the zones within each node
__ */
__for (node = 0; node < numnodes; node++) {
____unsigned long zone_size[MAX_NR_ZONES];
____unsigned long zhole_size[MAX_NR_ZONES];
____struct bootmem_data *bdata;
____pg_data_t *pgdat;
____int i;
/*
____ * Initialise the zone size information.
____ */
____for (i = 0; i < MAX_NR_ZONES; i++) {
______zone_size[i]  = 0;
______zhole_size[i] = 0;
____}

____pgdat = NODE_DATA(node);
____bdata = pgdat->bdata;

____/*
____ * The size of this node has already been determined.
____ * If we need to do anything fancy with the allocation
____ * of this memory to the zones, now is the time to do
____ * it.
____ */
____zone_size[0] = bdata->node_low_pfn -
________(bdata->node_boot_start >> PAGE_SHIFT);

____/*
____ * If this zone has zero size, skip it.
____ */
____if (!zone_size[0])
______continue;

____/*
____ * For each bank in this node, calculate the size of the
____ * holes.  holes = node_size - sum(bank_sizes_in_node)
____ */
____zhole_size[0] = zone_size[0];
____for (i = 0; i < mi->nr_banks; i++) {
______if (mi->bank[i].node != node)
________continue;

______zhole_size[0] -= mi->bank[i].size >> PAGE_SHIFT;
____}
/*
____ * Adjust the sizes according to any special
____ * requirements for this machine type.
____ */
____arch_adjust_zones(node, zone_size, zhole_size);

____free_area_init_node(node, pgdat, zone_size,
________bdata->node_boot_start >> PAGE_SHIFT, zhole_size);
__}

这段代码比较复杂,首先根据全局结构变量meminfo中的系统内存信息,将系统中属于同一内存node中的bank组成一个node,汇总处系统中的内存node数以及每一个node所占的内存空间(包括内存孔洞)大小,物理开始地址,该内存node空间(包括内存孔洞)的页帧位码占的内存页数,统计系统中所有内存node的页帧位码所占的总内存页数,统计系统中的地段内存最大可能页数max_low_pfn(包括内存孔洞)和系统中物理内存最大可能页数max_pfn(包括内存孔洞),把系统中的高端内存开始的地址high_memory设置为系统中低端内存结束的地址对应的虚拟地址。查询meminfo中属于0号内存node的所有bank,看是否有一个足够大的bank,这个bank要能装下编译出来的整个内核空间以及系统所有内存node的页帧位码所占的内存空间,如果没有系统崩溃,查询meminfo中所有bank,找到包含bootloader村放initrd空间的内存bank所属node号,如果没有bank能包含initrd,那么将phy_initrd_start和phys_initrd_size清0,删掉initrd。在配置了CONFIG_DISCONTIGMEM选项即系统中有多个内存node的情况下,按从最高到0号内存node顺序,将系统中每一个node对应的pg_data_t结构变量disconting_node_data[n]连接到开环链表pgdat_list中。(最后pgdat_list指向disconting_node_data[0]),在没有配置CONFIG_DISCONtOGMEM选项即系统中只有一个内存node的情况下,系统中只有一个内存node,pgdat_list始终指向conting_page_data,将该node空间对应的页帧位码全部设置位1,即使这个node中的内存全部设置位不能使用状态,再将这个内存node中的每个内存bank对应的页帧位码清0,即使事实存在的物理内存空间设置的可以使用,由此看出,那些内存node内各bank之间的内存孔洞对应的页帧位码被设置为1,不能使用状态,这样系统使用内存时就不会踩空,对于0号节点,再把内核物理开始地址_pa(_stext)到内核结束地址_pa(_end)前面的整个内核空间,从_pa(swapper_pg_dir)到内核物理开始地址_pa之间的16K一级页表空间,从内核物理结束地址_pa(_end)开始的系统所有的内存nodes的页帧位码所占的内存空间根据具体处理器型号不同从系统物理内存开始地址到_pa(swapper_pg_dir)之间的一段内存空间对应的页帧位码设置为1,不允许内核和用户使用0号内存node中的这4段内存空间,再将存放对phys_initrd_start处大小为phys_initrd_size的initrd空间对应的页帧设置为1,保护initrd系统在被压缩,挂接前不被系统破坏。把initrd_start设置成_phys_to_vir(phy_initrd_start),把initrd_end设置成initrd_start+phys_initrd_size。更新meminfo,调用alloc_bootmem_low_pages()函数从低端内存空间分配第1页空闲内存,将该页的页帧位码设置位1,并将这页内存空间全部清0。根据编译前在配置菜单中对cache的设置情况以及当前处理器的版本号修正前面设置的系统的cache政策编号全局变量cachepolicy,如果处理器的版本编号小于ARM5的编号,再将全局变量ecc_mask设置为0,因为ARMv5以前处理器的一级描述符中没有定义第九位作为保护标志位。

你可能感兴趣的:(linux)