电脑的启动流程详见这篇博文电脑开机过程, 简述一下就是按下电源键后, cs:ip置位到0xffff0的位置, 而这个位置是固化的, 上面都刻录好了BIOS程序, BIOS执行基本的硬件自检以及建立中断向量表, 初始化BIOS中断等, 接下来会把第一块磁盘的第一块扇区(512Byte)加载到内存为0x7c00的位置, 接下来就有kernel接管了, 不同版本的kernel的实现也是不一样的.
/arch这个目录是与架构有关的代码,不同架构的机器的特性不一样实现也不一样, 所以arch目录下有不同的机器的目录, 如下图, 而开机的代码因为刚刚启动时还在实模式下, 要由汇编去实现, 与机器的架构有关.
接下来, 我们看看常见的x86架构的目录结构
本来看这个目录, 我想着应该是bios加载bootsect, bootsect加载setup, 但我看完bootsect我发现不是的, 这个bootsect被弃用了!!!我查了下, 说是2.4版本之前是这样BIOS执行玩自己的代码后会将bootsect.S加载到0x7c00的位置, bootsect执行期间, 它会将自己移动到内存绝对地址的0x90000开始处并继续执行. bootsect的主要作用吧处于磁盘第二个扇区开始的4个扇区的setup模块(由setup.S编译而成)加载到内存紧接着bootsect后面位置处(0x902000), 接下来会把磁盘上setup模块后面的system模块加载到内存0x10000开始的地方.
2.4之后我在研究之中…下面是bootsect的注释别看了, 没用
#ifndef _LINUX_BOOT_H
#define _LINUX_BOOT_H
/* Don't touch these, unless you really know what you're doing. */
#define DEF_INITSEG 0x9000 //bootsect会将自己移动的内存段地址
#define DEF_SYSSEG 0x1000 //system模块加载的段地址
#define DEF_SETUPSEG 0x9020 //setup模块加载的段地址
#define DEF_SYSSIZE 0x7F00 //默认的系统模块长度
/* Internal svga startup constants */
#define NORMAL_VGA 0xffff /* 80x25 mode */
#define EXTENDED_VGA 0xfffe /* 80x50 mode */
#define ASK_VGA 0xfffd /* ask for it at bootup */
#endif
/*
* bootsect.S Copyright (C) 1991, 1992 Linus Torvalds
*
* modified by Drew Eckhardt
* modified by Bruce Evans (bde)
* modified by Chris Noe (May 1999) (as86 -> gas)
* gutted by H. Peter Anvin (Jan 2003)
*
* BIG FAT NOTE: We're in real mode using 64k segments. Therefore segment
* addresses must be multiplied by 16 to obtain their respective linear
* addresses. To avoid confusion, linear addresses are written using leading
* hex while segment addresses are written as segment:offset.
*
*/
#include <asm/boot.h>
SETUPSECTS = 4 /* default nr of setup-sectors */
/* setup在磁盘的从第二个扇区开始的4个扇区 */
BOOTSEG = 0x07C0 /* original address of boot-sector */
/* bootsect会被BIOS加载到0x7c00的位置 */
INITSEG = DEF_INITSEG /* we move boot here - out of the way */
/* 将bootsect位置移到位置0x90000-避开系统模块占用处 */
SETUPSEG = DEF_SETUPSEG /* setup starts here */
/* setup程序从0x90200处开始 */
SYSSEG = DEF_SYSSEG /* system loaded at 0x10000 (65536) */
/* system模块加载到0x10000(64KB)处 */
SYSSIZE = DEF_SYSSIZE /* system size: # of 16-byte clicks */
/* system模块的长度 */
/* to be loaded */
ROOT_DEV = 0 /* ROOT_DEV is now written by "build" */
SWAP_DEV = 0 /* SWAP_DEV is now written by "build" */
#ifndef SVGA_MODE
#define SVGA_MODE ASK_VGA
#endif
#ifndef RAMDISK
#define RAMDISK 0
#endif
#ifndef ROOT_RDONLY
#define ROOT_RDONLY 1
#endif
.code16
.text
.global _start
_start:
# Normalize the start address
jmpl $BOOTSEG, $start2
start2:
/* 对段寄存器ds, es, ss进行初始化, 使之等于cs*/
movw %cs, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movw $0x7c00, %sp /* 初始化栈指针 */
sti /* 设置中断 */
cld /* 清除方向标志 */
movw $bugger_off_msg, %si /* 寄存器si指向要显示的字符 */
/* 逐字符显示bugger_off_msg的内容 */
msg_loop:
lodsb /* lodsb的功能是将ds:[si]中的内容取到al中, 然后si = si + 1 */
andb %al, %al
jz die /* jz是jmp if zero, 如果al相与的值为0, 跳到die处 */
/* 在屏幕上显示一个字符,前进光标和滚动, 根据需要显示屏幕 */
movb $0xe, %ah /* BIOS中断0x10功能号ah = 0x13 */
movw $7, %bx /* [bh,bl]bh = 显示页面号, bl = 字符属性 */
int $0x10
jmp msg_loop
die:
# Allow the user to press a key, then reboot
xorw %ax, %ax
int $0x16 /* 从键盘读一字符, al = 字符码, ah = 扫描码 */
int $0x19 /* 寻找并加载MBR到0x7c00, 并重启了 */
# int 0x19 should never return. In case it does anyway,
# invoke the BIOS reset code...
ljmp $0xf000,$0xfff0 /* 为了防止上面返回, 跳转0xffff0的位置, 会重新执行BIOS程序重启 */
bugger_off_msg:
.ascii "Direct booting from floppy is no longer supported.\r\n"
.ascii "Please use a boot loader program instead.\r\n"
.ascii "\n"
.ascii "Remove disk and press any key to reboot . . .\r\n"
.byte 0
# Kernel attributes; used by setup
.org 497
setup_sects: .byte SETUPSECTS
root_flags: .word ROOT_RDONLY
syssize: .word SYSSIZE
swap_dev: .word SWAP_DEV
ram_size: .word RAMDISK
vid_mode: .word SVGA_MODE
root_dev: .word ROOT_DEV
boot_flag: .word 0xAA55
# 我哭了!!!!!!!!!我研究了半天从2.6开始系统启动bootsect的功能已经不支持, 如果从这个文件中的bootsect启动, 则直接提示按任意键重启电脑..........平平无奇小天才, 我emo了