linux-3.4.2内核源码分析——(一)操作系统的引导和启动程序

Linux操作系统的引导

BIOS/Bootloader:
由PC机的BIOS (OXFFFFO是BIOS存储的总线地址)把bootsect从某个固定的地址拿到了内存中的某个固定地址(0x90000),并且进行了一系列的硬件初始化和参数设置

bootsect.s
磁盘引导块程序,在磁盘的第一个扇区中的程序(0磁道0磁头1扇区)作用:首先将后续的setup.s代码从磁盘中加载到紧接着bootsect.s的地方。在显示屏上显示loading system再将system(操作系统)模块加载到0x10000的地方最后跳转到setup.s中去运行
setup .s
解析BIOS/BOOTLOADER传递来的参数
设置系统内核运行的LDT(局部描述符)IDT(中断描述符寄存器)全局描述符(设置全局描述符寄存器)

设置中断控制芯片,进入保护模式运行(svc32保护模式,设置寄存器中的值)
跳转到system模块的最前面的代码运行(head.s)
head.s
加载内核运行时的各数据段寄存器,重新设置中断描述符表开启内核正常运行时的协处理器等资源
设置内存管理的分页机制
跳转到main.c开始运行

BOOTLOADER的启动内核代码创建

uboot中有如下代码:

定义一个指针
void (*theKernel) (int zero,int arch,uint params) ;

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

/*执行linux并传入参数,bd->bi_arch_number称为process id即CPU的架构号
bd->bi_bootparams 为bootlader给linux传递的参数地址 */
theKernel (0,bd->bi_arch_number,bd->bi_bootparams);

带着上面的参数就会执行下面linux内核中head.S中的代码

//比对当前板子是否支持lInux系统,如果不支持则不启动直接跳转到error_p。正确则执行下面代码。
    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			//如果不正确则跳转


 //设置物理内存页面
#ifndef CONFIG_XIP_KERNEL
	adr	r3, 2f
	ldmia	r3, {r4, r8}
	sub	r4, r3, r4			@ (PHYS_OFFSET - PAGE_OFFSET)
	add	r8, r8, r4			@ PHYS_OFFSET


//验证参数是否完整
	bl	__vet_atags  


 //创建虚拟内存的页表
    bl	__create_page_tables

/* 把函数__mmap_switched压入栈中,r13中存放sp*/
	ldr	r13, =__mmap_switched		@ address to jump to after

需要注意的一点是在ARM的user模式下,ARM CPU有16个数据寄存器,被命名为R0~R15(这个要比X86多一些),它们均为32位寄存器,其中的R13~R15有特殊用途。寄存器R13保存堆栈指针SP,寄存器R14用作子程序链接寄存器,也称为LR,用以保存返回地址R15(PC)用作程序计数器。

在__mmap_switched中干的事情就是会将旧的地址转化为虚拟地址,即代码重定义,

  会有如下一行代码

b	start_kernel

该代码会跳转到 init/main.c(linux-3.4.2版本)下的start_kernel函数,该函数能对进程初始化c函数进行调用。如下进行各类初始化。

asmlinkage void __init start_kernel(void)
{
 
      。。。。。。。。

	tick_init(); /*时钟初始化,初始化jiffies*/
	boot_cpu_init();
	page_address_init();
	printk(KERN_NOTICE "%s", linux_banner);
	setup_arch(&command_line);
	mm_init_owner(&init_mm, &init_task);
	mm_init_cpumask(&init_mm);
	setup_command_line(command_line);
	setup_nr_cpu_ids();
	setup_per_cpu_areas();
	smp_prepare_boot_cpu();	/* arch-specific boot-cpu hooks */

	build_all_zonelists(NULL);
	page_alloc_init();

 。。。。。。。。。。。各种init

/* Do the rest non-__init'ed, we're now alive */
	rest_init();

}

再rest_init()函数中有如下代码,会创建一个线程调用kernel_init函数

kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

以上这句话和linux-0.11版本main.c中的

	if (!fork()) {	/*创建一个0号进程,进程若创建成功会返回一个0*/
		init();
	}

是一个意思。init()函数和kernel_init()中干的事也有很多相同。比如打开各种控制tai

进入linux-3.4.2版本kernel_init函数

static int __init kernel_init(void * unused)
{

            。。。 。。。。。。。。。。。。

/* Open the /dev/console on the rootfs, this should never fail */
	if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)  /*打开标准输入控制台*/
		printk(KERN_WARNING "Warning: unable to open an initial console.\n");

	(void) sys_dup(0); 	/*拷贝标准输输入控制台*/
	(void) sys_dup(0);	/*拷贝标准错误控制台*/



        。。。。。。。。。。。。。。。。。。。。

    init_post();
	return 0;
}

init_post()函数中有如下代码,最后会运行根文件系统。

    run_init_process("/sbin/init");
	run_init_process("/etc/init");
	run_init_process("/bin/init");
	run_init_process("/bin/sh");   /*运行跟文件系统*/

这与linux-0.11版本相似。

	if (!(pid=fork())) {  /*创建1号进程返回0则创建成功*/
		close(0);		/*关闭0号句柄,关闭了0号进程创建的输入输出*/
		if (open("/etc/rc",O_RDONLY,0))  /*打开/etc/rc文件*/
			_exit(1);
		execve("/bin/sh",argv_rc,envp_rc);	/*执行shell程序*/
		_exit(2);
	}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值