0%

Mr. Process的一生-Linux内核的社会视角 (2)启动(转)

其实这才应该是这一系列文章的第一节,因为这篇文章讲的是盘古开天地的事。话说Mr. Process是一个现代人,但是,只要是人,总该有个祖先。人们总想知道自己从哪来,然后才可以估摸算一下自己将去向何方。所以咱也要了解一下Linux的世界里人类的起源。

图1:从上电到BIOS

按下电源开关的那个真实的人就是Linux世界里的上帝,他创造了Linux世界的一切。当他按下机箱上的电源开关时,主板开始供电,CPU上的Reset Pin被拉高,这会引起CPU的一系列动作,这些动作是芯片设计时就决定的。CPU中的一些寄存器被置为固定的值,因为这些寄存器可能在启动的过程中要使用,比如CS(代码段寄存器)和EIP(指针指令寄存器)。这一步完成之后,CPU就可开执行地址为0xfffffff0里的ROM中的程序。这段程序就是BIOS(Basic Input Output System)。

BIOS完成下面的功能:

1.POST(Power-On Self Test):顾名思名,就是查查有什么设备,这些设备是不是正常。要是CPU有Advanced Configuration and Power Interface(ACPI )的支持,也在这个时候进行。ACPI是用来对电源进行管理的,用来节电之类的。

2.初始化设备:确保没有IRQ和IO冲突。

3.寻找OS/Bootloader。这一步后面点再细说

我们在BIOS的设置菜单里能够设置从何处启动,比如软盘,硬盘,光驱…BIOS会按我们设定的顺序搜索OS。

4.把Bootloader复制到RAM里(地址为0x00007c00),然后那个地址开始执行。

什么是Bootloader?\

现在,我们只要关心的是:bootloader会找到OS,把OS内核COPY到RAM中。

图2:boot loader的加载

如上图所示,在硬盘的第一个sector,有一个分区表(记录了哪些分区上有操作系统)和一个小版的Bootloader。当这个BIOS被设置为从这里启动时,这个小版的bootloader被复制到RAM的0x00007c00。然后它会把自己又移动到0x00096a00。在这之后,它再把另一段Bootloader从硬盘上复制到0x00096c00,然后从那里开始执行。分作两段的原因是因为现在的bootloader太大了,在MBR上存不完那么多。

Bootloader会把OS的内核映像复制到RAM中。

Bootloader的工作

\1. 调用BIOS以显示“Loading Image”的消息。

\2. 调用BIOS,把内核映像的前512字节复制到0×00090000,setup()函数在0×00090200。

\3. 调用BIOS,把剩下的内核映像加载到0×00010000(小内核zImage)或0×00100000(大内核bzImage)

\4. 跳到setup()开始执行。

Setup()的工作

setup()用来初始化设备。虽然BIOS已经做了一些初始化的工作,但是Linux关不依赖于他。setup()会初始化键盘,FPU等设备,并设置一些寄存器。在Setup()的最后,会调用startup_32()。

startup_32()

Linux里有两个startup_32()。

首先会执行的是arch/i386/boot/compressed/head.S里的那个。这个startup_32()的作用主要是解压内核。

第二个startup_32()是在arch/i386/kernel/head.S的。这个startup_32()的工作就是为Linux的第一个进程(就是Mr. Process的祖先)设置生存环境。最后跳到start_kernel()中去。

在Understanding the Linux Kernel 3rd 中的描述如下

  1. Initializes the segmentation registers with their final values.
  2. Fills the bss segment of the kernel (see the section “Program Segments and Process Memory Regions” in Chapter 20) with zeros.
  3. Initializes the provisional kernel Page Tables contained in swapper_pg_dir and pg0 to identically map the linear addresses to the same physical addresses, as explained in the section “Kernel Page Tables” in Chapter 2.
  4. Stores the address of the Page Global Directory in the cr3 register, and enables paging by setting the PG bit in the cr0 register.
  5. Sets up the Kernel Mode stack for process 0 (see the section “Kernel Threads” in Chapter 3).
  6. Once again, the function clears all bits in the eflags register.
  7. Invokes setup_idt( ) to fill the IDT with null interrupt handlers (see the section “Preliminary Initialization of the IDT” in Chapter 4).
  8. Puts the system parameters obtained from the BIOS and the parameters passed to the operating system into the first page frame (see the section “Physical Memory Layout” in Chapter 2).
  9. Identifies the model of the processor.

\10. Loads the gdtr and idtr registers with the addresses of the GDT and IDT tables.

\11. Jumps to the start_kernel( ) function.

start_kernel()的工作

完成所有组件的初始化工作。

Understanding the Linux Kernel对这一段工作的描述如下:

¨ The scheduler is initialized by invoking the sched_init( ) function (see Chapter 7).

¨ The memory zones are initialized by invoking the build_all_zonelists( ) function (see the section “Memory Zones” in Chapter 8).

¨ The Buddy system allocators are initialized by invoking the page_alloc_init( ) and mem_init( ) functions (see the section “The Buddy System Algorithm” in Chapter 8).

¨ The final initialization of the IDT is performed by invoking trap_init( ) (see the section “Exception Handling” in Chapter 4) and init_IRQ( ) (see the section “IRQ data structures” in Chapter 4).

¨ The TASKLET_SOFTIRQ and HI_SOFTIRQ are initialized by invoking the softirq_init( ) function (see the section “Softirqs” in Chapter 4).

¨ The system date and time are initialized by the time_init( ) function (see the section “The Linux Timekeeping Architecture” in Chapter 6).

¨ The slab allocator is initialized by the kmem_cache_init( ) function (see the section “General and Specific Caches” in Chapter 8).

¨ The speed of the CPU clock is determined by invoking the calibrate_delay( ) function (see the section “Delay Functions” in Chapter 6).

¨ The kernel thread for process 1 is created by invoking the kernel_thread( ) function. In turn, this kernel thread creates the other kernel threads and executes the /sbin/init program, as described in the section “Kernel Threads” in Chapter 3.

以上几个函数的执行过程如下图:

图3:启动函数的执行过程

第二个startup_32()和start_kernel()揭示了Linux一生的真谛。从这里面咱看到了Mr. Process(一个普通的进程)所拥有的一切是怎么得到的。弄清楚了这些,也就弄清楚了Linux。Really清楚。

BTW,我做的图怎么那么好看~美感天生在那里,你挡都挡不住,怎么搞怎么好看加专业!