linux内核segment.h,[Linux Kernel]Linux内核启动过程(2.6.23)

[Linux Kernel]Linux内核启动过程(2.6.23)

(2012-08-22 10:34:56)

标签:

linux

kernel

boot

it

内核版本号:2.6.23

当PC按下电源,CPU加电后,自动从0xFFFF0处开始执行代码,这个地址位于BIOS中。接着

BIOS开始一系列系统检测,并在内存物理地址0处初

始化中断向量,供Linux内核启动过程中进行调用。此后,它将启动设备的第一个扇区(磁盘引导扇区,512Bytes)读入内存绝对地址0x7C00

处,并跳到这个地方开始执行(arch/i386/boot/header.s)。

注意,现在程序现在运行在16位实模式下,使用 64k的段。 segment(段地址) : offset(偏移)构成了

逻辑地址,段地址乘以 16 再加上 偏移 ,就得到 linear

address(线性地址)。header.s(arch/i386/boot/header.s)中包含了大量的.h(头文件)。

18 #include

19 #include

20 #include

21 #include

22 #include

23 #include

24 #include "boot.h"

这些头文件包含了许多重要的宏和声明,在Linux 内核的其他程序中,都会用到。

.section ".header", "a"

.globl hdr

hdr:

上面开始了header.s的最重要的部分,hdr数据段存储了Linux kernel启动过程所需的信息数据,它的结构struct

boot_params定义在 include/asm-i386/bootparam.h中。struct

boot_params非常重要,它包含了其他的一些struct,并不是很复杂。下面开始了header.s程序,开始了header.s的真正的执

行。

.section ".inittext", "ax"

start_of_setup:

它首先初始化 Disk Controller(磁盘控制器),是通过 int 0x13进行的。然后设置寄存器,Zero the

bss(初始化数据段),接着 call main(调用

main)跳转到:arch/i386/boot/main.c的main()函数开始执行。(终于见到我们的可亲可爱的C了,不过别高兴太早喔:~)

下面我们看看main()都做了什么(注:linux kernel

中有太多main函数,程序并不是总是从main开始执行)

copy_boot_params(); 复制 boot header 到 "zeropage"

validate_cpu(); 确保支持当前运行的CPU

set_bios_mode(); 告诉BIOS什么CPU我们将要去运行

detect_memory(); 检测Memory

keyboard_set_repeat(); 设置键盘 repeat rate (Why ?)

set_video(); 设置 Video mode

query_mca(); 获得 MCA 信息

query_voyager(); Voyager ?

query_ist(); 获得 Query Intel SpeedStep (IST) 信息

query_apm_bios(); 获得APM 信息

query_edd(); 获得EDD信息

go_to_protected_mode(); 最后一件事,也是最重要的一件事,进入保护模式

上面只是函数调用的大致顺序,并不是真正的程序,通过这些函数我们可以一目了然地看到内核的执行过程(还是C好啊,UGG boots,

不像汇编,得一行一行地看),当然你要了解更多的细节,可以追踪到每一个函数中去。它通过上方面的检测,不断地填充struct

boot_params结构,记住,struct boot_params 是很重要的喔

。其他函数我们就不研究了,单看最后一个,go_to_protected_mode(),究竟在哪儿?

arch/i386/boot/pm.c

void go_to_protected_mode(void)

依次调用了如下函数:

realmode_switch_hook(); Hook before leaving realmode

move_kernel_around(); 把 Kernel/setup 移到 它们最终的地方

enable_a20() 开启 a20 门

reset_coprocessor(); 重置 coprocessor(协处理器)

mask_all_interrupts(); Mask(屏蔽所有中断)

setup_idt(); 开始转入 保护模式……

setup_gdt();

protected_mode_jump(boot_params.hdr.code32_start,

(u32)&boot_params + (ds()

<< 4));

protected_mode_jump,传入了boot_params->hdr

->code32_start作为第一个参数, 该参数在header.s

中(arch/i386/boot/header.s) 设置如下:

code32_start: # here loaders can put a different

# start address for 32-bit code.

#ifndef __BIG_KERNEL__

.long 0x1000 # 0x1000 = default for zImage

#else

.long 0x100000 # 0x100000 = default for big kernel

#endif

显然是跳转到 0x1000或0x100000处继续执行。

第二个参数,就是 boot_params的线性地址,注意,现在仍是实地址模式,线性地址为段地址乘16加上偏移。

(啊,天哪,又到 汇编里去了,AT&T汇编),

在arch/i386/boot/pmjump.S中,

.globl protected_mode_jump

.type protected_mode_jump, @function

.code16

终于找到了,arch/i386/boot/pmjump.S并不大,只有54行。我们看看它做了什么:

xorl �x, �x # Flag to indicate this is a boot

movl �x, %esi # 传递过来的boot_params地址,转移到 ESI寄存器中

movl �x, 2f # Patch ljmpl instruction 要跳转的地址 放到 2f 处

........

1:

movw $__BOOT_DS, %cx

movl %cr0, �x

orb $1, %dl # Protected mode (PE) bit 设置CR0的PE位,进入保护模式!!

movl �x, %cr0

很简单嘛 :~)

# Jump to the 32-bit entrypoint 进入32位程序段

.byte 0x66, 0xea # ljmpl opcode 跳转指令码 (为什么不用汇编呢?不太清楚)

2: .long 0 # offset 偏移地址

.word __BOOT_CS # segment 段 类似于 ljmp segment : offset;

.size protected_mode_jump, .-protected_mode_jump

现在我们到哪儿了呢,想必大家都迷住了吧。我找了半天,终于找到了:

arch/i386/boot/compressed/head.s

********** head.S contains the 32-bit startup code.

NOTE!!! Startup happens at absolute address 0x00001000, which is

also where

* the page directory will exist. The startup code will be

overwritten by

* the page directory.

必定是跳转到这里了,Startup 被加载到绝对地址

0x00001000处。head.s的作用想必大家已经猜到了吧。既然在compressed文件夹中,肯定是和压缩有关的啦。对!

.text

#include

#include

#include

#include

.section ".text.head","ax",@progbits

.globl startup_32 定义了startup_32函数,从这儿开始执行

startup_32: 程序开始……

接下来,startup_32要检测是不是被加载到预定的位置了呢,怎么检查呢,这里用了一个技巧:

leal (0x1e4+4)(%esi), %esp 还记得 ESI中存的是boot_params的地址吗?这条指令就是把

栈指针指向boot_params的一个成员,而这个成员 u32 scratch就是专为程序检查是否被加载到预定位置设置的作

为栈使用的

call 1f 当 call时,一条指令的指针要入栈,就是保存到 scratch中了

1: popl �p 把指令指针放到 EBP中,EBP存的是当前的位置

subl $1b, �p 1b指的是指令在程序段中的偏移,两者之差,就是程序被加载的address了

* �p contains the address we are loaded at by the boot loader and

�x

* contains the address where we should move the kernel image

temporarily

* for safe in-place decompression.

* 现在 EBP中含有我们被加载的地址,而 EBX 中含有我们应该暂时把内核 image移动到的安全位置的地址

接着,就开始复制并移动内核,接着跳到移动后的地址开始执行,并为解压过程设置栈(stack)

* Do the decompression, and jump to the new kernel..

*开始解压缩,然后跳到新的内核开始执行

movl output_len(�x), �x #将参数依次入栈

pushl �x

pushl �p # output address

movl input_len(�x), �x

pushl �x # input_len

leal input_data(�x), �x

pushl �x # input_data

leal _end(�x), �x

pushl �x # end of the image as third argument

pushl %esi # real mode pointer as second arg

call decompress_kernel

addl $20, %esp 恢复栈

popl �x

如果需要,还要再移动内核,然后:

* Jump to the decompressed kernel.

xorl �x,�x

jmp *�p EBP 中有解压后新内核的地址

先说下内核的的解压缩。

内核解压缩的 decompress_kernel

在arch/i386/boot/compressed/misc.c中定义,

asmlinkage void decompress_kernel(void *rmode, unsigned long

end,

uch *input_data, unsigned long input_len, uch *output)

{

……

……

putstr("Uncompressing Linux... "); 很高兴啊,Uncompressing

Linux...

gunzip(); 是专门解压内核的gzip,它涉及到同目录下的relocs.c文件,

还有 lib/inflate.c(主要的解压例程)

putstr("Ok, booting the kernel.\n"); 很熟悉吧,Ok,booting the

kernel.

return;

}

好了,继续下去,现在Linux应该是跳转到新的解压缩后的位置开始执行了吧(跳来跳去真辛苦啊)

到哪儿了呢? 还是汇编~:( arch/i386/kernel/head.s

head.s文件,578L(行代码,不小啊,不过这算是最后一个啦,坚持哈)

head.s 虽然大,但很有条理:

重新设置段

把BSS段设置为0

把 boot_params的参数重新COPY到安全的地方

Initialize page tables 初始化页表

为SMP设置页表(如果有的话)

* Enable paging

movl $swapper_pg_dir-__PAGE_OFFSET,�x

movl �x,%cr3 # set the page table pointer..设置页指针

movl %cr0,�x

orl $0x80000000,�x

movl �x,%cr0 # ..and set paging (PG) bit 启动分页

ljmp $__BOOT_CS,$1f # Clear prefetch and normalize %eip

设置EIP

1:

lss stack_start,%esp #Set up the stack pointer 设置栈 指针

pushl $0 #Initialize eflags. 把EFLAGES设置为0

popfl

检测 CPU 类型

setup_idt 设置 IDT表

include arch/i386/xen/xen-head.S

…………

360(Line) jmp start_kernel 跳转到 init/main.c

现在,终于熬过了 AT&T汇编 ,是不是很有成就感呢,下面的start_kernel要是也讲习

懂了,Linux内核的其他部分,就不在话下了,加油啊,我现在水平还不够,只能列出start_kernel源码,大家一同分析

513 asmlinkage void __init start_kernel(void)

........

至此,内核正常启动起来了。太高兴了,现在还没有研究到 start_kernel 内部,再接再厉!!

在此,记录一篇比较详细的文章:

linux 启动过程综述:

startup/index.html

分享:

a4c26d1e5885305701be709a3d33442f.png喜欢

0

a4c26d1e5885305701be709a3d33442f.png赠金笔

加载中,请稍候......

评论加载中,请稍候...

发评论

登录名: 密码: 找回密码 注册记住登录状态

昵   称:

评论并转载此博文

a4c26d1e5885305701be709a3d33442f.png

发评论

以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值