二、Linux操作系统的引导和启动

1.Linux操作系统的引导

Linux是如何从硬盘中读出的?
Linux在启动的时候是如何拿到硬件参数的?
Linux在初始运行中都做了什么?

分析

BIOS/Bootloader做的事情:
由PC机的BIOS(0xFFFF0是BIOS存储的总线地址)把我们的bootsect.s从某一个固定的地址拿到内存的0x7c00,然后bootsect.s自移到固定地址(0x90000),并且进行了一系列的硬件初始化和参数设置

bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves iself out of the way to address 0x90000, and jumps there.

bootsect.s做的事情:
磁盘引导块程序,在磁盘的第一个扇区中的程序(0磁道 0磁头 1扇区)
1.首先将后续的setup.s代码从磁盘中加载到紧接着bootsect.s的地方
2.在显示屏上显示loading system 再将system(操作系统)模块加载到0x10000的地方
3.最后跳转到setup.s中去运行

setup.s做的事情:
1.解析BIOS/Bootloader中传递来的参数
2.设置系统内核运行的LDT(局部描述符) IDT(中断向量表) 全局描述符(全局描述符寄存器)
3.设置中断控制芯片,进入保护模式运行(svc 32保护模式 设置寄存器)
4.跳转到system模块的最前面的代码运行(head.s)

head.s做的事情:
1.加载内核运行时的各数据段寄存器,重新设置中断向量表
2.开启内核正常运行时的协处理器等资源
3.设置内存管理的分页机制
4.跳转到main.c开始运行
图
main.c做的事情:

void main(void)		/* This really IS void, no error here. */
{			/* The startup routine assumes (well, ...) this */
/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
 	ROOT_DEV = ORIG_ROOT_DEV;//设置操作系统的根文件
 	drive_info = DRIVE_INFO;//设置操作系统驱动参数
	//解析setup.s代码后获取系统内存参数
	//设置系统的内存大小,系统本身内存(1MB)+扩展内存大小(参数*KB)
	memory_end = (1<<20) + (EXT_MEM_K<<10);
	//取整4k的内存大小
	memory_end &= 0xfffff000;
	if (memory_end > 16*1024*1024)//控制操作系统的最大内存为16M
		memory_end = 16*1024*1024;
	
	if (memory_end > 12*1024*1024) //大于12M
		buffer_memory_end = 4*1024*1024;//设置高速缓冲区的大小,跟块设备有关,跟设备交互的时候,充当缓冲区,写入到块设备中的数据先放在缓冲区里,只有执行sync时才真正写入;这也是为什么要区分块设备驱动和字符设备驱动;块设备写入需要缓冲区,字符设备不需要是直接写入的
	else if (memory_end > 6*1024*1024)//大于6M小于12M
		buffer_memory_end = 2*1024*1024;//设置2M高速缓冲区
	else
		buffer_memory_end = 1*1024*1024;//小于6M设置1M高速缓冲区
	main_memory_start = buffer_memory_end;//主内存起始地址即高速缓冲区结束地址
#ifdef RAMDISK
	main_memory_start += rd_init(main_memory_start, RAMDISK*1024);
#endif
	//内存控制器初始化
	mem_init(main_memory_start,memory_end);
	//异常函数初始化
	trap_init();
	//块设备驱动初始化
	blk_dev_init();
	//字符型设备驱动初始化
	chr_dev_init();
	//控制台设备初始化
	tty_init();
	//加载定时器驱动
	time_init();
	//进程间调度初始化
	sched_init();
	//缓冲区初始化
	buffer_init(buffer_memory_end);
	//硬盘初始化
	hd_init();
	//软盘初始化
	floppy_init();
	sti();
	//从内核态切换到用户态,上面的初始化都是在内核态运行的
	//内核态无法被抢占,不能在进程间进行切换,运行不会被干扰
	move_to_user_mode();
	if (!fork()) {	//创建0号进程 fork函数就是用来创建进程的函数	/* we count on this going ok */
		//0号进程是所有进程的父进程
		init();
	}
//0号进程永远不会结束,他会在没有其他进程调用的时候调用,只会执行for(;;) pause();
	for(;;) pause();
}

图

高速缓冲区只跟块设备有关,跟设备交互的时候,充当缓冲区,写入到块设备中的数据先放在缓冲区里,只有执行sync时才真正写入;这也是为什么要区分块设备驱动和字符设备驱动;块设备写入需要缓冲区,字符设备是直接写入的

2. Bootloader启动内核代码

创建
void (*theKernel)(int zero,int arch,uint params);

把指针移到ih_ep上去,Linux的启动入口
theKernel = (void(*)(int, int,uint))ntohl(hdr->ih_ep) ;

执行Linux并传入参数
theKernel(0,bd->bi_arch_number, bd->bi_boot _params) ;

bd->bi_arch_number称为process id CPU的架构号
bd->bi_boot_params 成为参数地址

2. 内核代码 head.s

比对当前板子的CPU是否支持Linux,如果不支持怎不启动直接退出,如果支持则继续进行
mrc p15,0,r9,c0,c0 @get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid
movs r10, r5 @invalid processor (r5=0)?
THUMB ( it eq ) @ force fixup-able long branch encoding
beq __error_p @yes,error ‘p’

设置物理内存的页面

验证参数是否完整(tag_list)参数

把函数__mmap_switched的地址装载进链接寄存器

进行初始化c函数start_kernel的调用

3. 内核如何认识不同板子

链接脚本:vmlinux.lds.S

.init.arch.info : {
	arch info begin = .; 
	*(.arch. info.init) //代码段
	arch_info_end = .;
}

ARCH.H宏定义

#define MACHINE_START(_type,_name)
static const struct machine_desc _mach_desc_##_type \
_used 					\
_attribute___((__section_(".arch.info.init")) )={\
	.nr=MACH_TYPE_##_type,\
	.name=_name,
#define MACHINE_END\
};

1
总结 :
machine desc结构体,用于Linux做设备板子的识别结构体
这些结构体被限定在了内存的某一片区域,并且通过UBOOT传过来的参数进行该结构体的配置(通过检索taglist的方式来设置)
移植Linux时就需要自己对结构体进行赋值
在之后的启功中或其他函数中,对结构体的变量进行调用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值