Arm嵌入式linux启动过程

2019-07-12 19:56发布

一位大师级的人物写的,不看要后悔的哟!!

如果以为到了c代码可以松一口气的话,就大错特措了,linux的c也不比汇编好懂多少,相反到掩盖了汇编的一些和机器相关的部分,有时候更难懂。其实作为编写操作系统的c代码,只不过是汇编的另一种写法,和机器代码的联系是很紧密的。 

start_kernel在 /linux/init/main.c中定义: 

asmlinkage void __init start_kernel(void) 

char * command_line; 
unsigned long mempages; 
extern char saved_command_line[]; 
lock_kernel(); 
printk(linux_banner); 
setup_arch(&command_line); //arm/kernel/setup.c 
printk("Kernel command line: %s ", saved_command_line); 
parse_options(command_line); 


trap_init(); // arm/kernle/traps.c install 
。。。。。。。。。 


start_kernel中的函数个个都是重量级的,首先用printk(linux_banner);打出 系统版本号,这里面就大有文章,系统才刚开张,你让他打印到哪里去呢? 
先给大家交个底,以后到console的部分自然清楚,printk和printf不同,他首先输出到系统的一个缓冲区内,大约4k,如果登记了console,则调用console->wirte函数输出,否则就一直在buffer里呆着。所以,用printk输出的信息,如果超出了4k,会冲掉前面的。在系统引导起来后,用dmesg看的也就是这个buffer中的东东。 

下面就是一个重量级的函数: setup_arch(&command_line); //arm/kernel/setup.c 完成内存映像的初始化,其中command_line是从bootloader中传下来的。 

void __init setup_arch(char **cmdline_p) 

struct param_struct *params = NULL; 
struct machine_desc *mdesc; //arch structure, for your ads, defined in include/arm-asm/mach/arch.h very long 
struct meminfo meminfo; 
char *from = default_command_line; 

memset(&meminfo, 0, sizeof(meminfo)); 

首先把meminfo清零,有个背景介绍一下,从linux 2.4的内核开始,支持内存的节点(node),也就是可支持不连续的物理内存区域。这一点在嵌入式系统中很有用,例如对于SDRAM和FALSH,性质不同,可作为不同的内存节点。 



meminfo结构定义如下: 


/******************************************************/ 
#define NR_BANKS 4 
//define the systen mem region, not consistent 
struct meminfo { 
int nr_banks; 
unsigned long end; 
struct { 
unsigned long start; 
unsigned long size; 
int node; 
} bank[NR_BANKS]; 
}; 
/******************************************************/ 

下面是:ROOT_DEV = MKDEV(0, 255); 

ROOT_DEV是宏,指明启动的设备,嵌入式系统中通常是flash disk. 
这里面有一个有趣的悖论:linux的设备都是在/dev/下,访问这些设备文件需要设备驱动程序支持,而访问设备文件才能取得设备号,才能加载驱动程序,那么第一个设备驱动程序是怎么加载呢?就是ROOT_DEV, 不需要访问设备文件,直接指定设备号。 

下面我们准备初始化真正的内核页表,而不再是临时的了。 
首先还是取得当前系统的内存映像: 

mdesc = setup_architecture(machine_arch_type); 
//find the machine type in mach-integrator/arch.c 
//the ads name, mem map, io map 


返回如下结构: 
mach-integrator/arch.c 


MACHINE_START(INTEGRATOR, "Motorola MX1ADS") 
MAINTAINER("ARM Ltd/Deep Blue Solutions Ltd") 
BOOT_MEM(0x08000000, 0x00200000, 0xf0200000) 
FIXUP(integrator_fixup) 
MAPIO(integrator_map_io) 
INITIRQ(integrator_init_irq) 
MACHINE_END 


我们在前面介绍过这个结构,不过这次用它可是玩真的了。 

书接上回, 
下面是init_mm的初始化,init_mm定义在/arch/arm/kernel/init_task.c: 
struct mm_struct init_mm = INIT_MM(init_mm); 

从本回开始的相当一部分内容是和内存管理相关的,凭心而论,操作系统的 
内存管理是很复杂的,牵扯到处理器的硬件细节和软件算法, 
限于篇幅所限制,请大家先仔细读一读arm mmu的部分, 
中文参考资料:linux内核源代码情景对话, 
linux2.4.18原代码分析。 

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; 
内核数据段结束 

每一个任务都有一个mm_struct结构管理任务内存空间,init_mm 
是内核的mm_struct,其中设置成员变量* mmap指向自己, 
意味着内核只有一个内存管理结构,设置* pgd=swapper_pg_dir, 
swapper_pg_dir是内核的页目录,在arm体系结构有16k, 
所以init_mm定义了整个kernel的内存空间,下面我们会碰到内核 
线程,所有的内核线程都使用内核空间,拥有和内核同样的访问 
权限。 

memcpy(saved_command_line, from, COMMAND_LINE_SIZE); 
//clear command array 

saved_command_line[COMMAND_LINE_SIZE-1] = '