📄 1120.html
字号:
·pgtable_cache_init [arch/i386/mm/init.c]<br>
·fork_init [kernel/fork.c]<br>
·proc_caches_init<br>
·vfs_caches_init [fs/dcache.c]<br>
·buffer_init [fs/buffer.c]<br>
·page_cache_init [mm/filemap.c]<br>
·signals_init [kernel/signal.c]<br>
·proc_root_init [fs/proc/root.c]<br>
·ipc_init [ipc/util.c]<br>
·check_bugs [include/asm/bugs.h]<br>
·smp_init [init/main.c]<br>
·rest_init<br>
·kernel_thread [arch/i386/kernel/process.c]<br>
·unlock_kernel [include/asm/smplock.h]<br>
·cpu_idle [arch/i386/kernel/process.c]<br>
<br>
start_kernel( )程序用于初始化系统内核的各个部分,包括:<br>
<br>
*设置内存边界,调用paging_init( )初始化内存页面。<br>
*初始化陷阱,中断通道和调度。<br>
*对命令行进行语法分析。<br>
*初始化设备驱动程序和磁盘缓冲区。<br>
*校对延迟循环。<br>
<br>
最后的function'rest_init' 作了以下工作:<br>
<br>
·开辟内核线程'init'<br>
·调用unlock_kernel<br>
·建立内核运行的cpu_idle环, 如果没有调度,就一直死循环<br>
<br>
实际上start_kernel永远不能终止.它会无穷地循环执行cpu_idle.<br>
<br>
最后,系统核心转向move_to_user_mode( ),以便创建初始化进程(init)。此后,进程0开始进入无限循环。<br>
<br>
初始化进程开始执行/etc/init、/bin/init 或/sbin /init中的一个之后,系统内核就不再对程序进行直接控制了。之后系统内核的作用主要是给进程提供系统调用,以及提供异步中断事件的处理。多任务机制已经建立起来,并开始处理多个用户的登录和fork( )创建的进程。<br>
<br>
[init]<br>
init是第一个进程,或者说内核线程<br>
<br>
|init<br>
|lock_kernel<br>
|do_basic_setup<br>
|mtrr_init<br>
|sysctl_init<br>
|pci_init<br>
|sock_init<br>
|start_context_thread<br>
|do_init_calls<br>
|(*call())-> kswapd_init<br>
|prepare_namespace<br>
|free_initmem<br>
|unlock_kernel<br>
|execve<br>
<br>
[目录]<br>
<br>
--------------------------------------------------------------------------------<br>
<br>
<br>
启动步骤<br>
<br>
系统引导:<br>
涉及的文件<br>
./arch/$ARCH/boot/bootsect.s<br>
./arch/$ARCH/boot/setup.s<br>
<br>
bootsect.S<br>
这个程序是linux kernel的第一个程序,包括了linux自己的bootstrap程序,<br>
但是在说明这个程序前,必须先说明一般IBM PC开机时的动作(此处的开机是指<br>
"打开PC的电源"):<br>
<br>
一般PC在电源一开时,是由内存中地址FFFF:0000开始执行(这个地址一定<br>
在ROM BIOS中,ROM BIOS一般是在FEOOOh到FFFFFh中),而此处的内容则是一个<br>
jump指令,jump到另一个位於ROM BIOS中的位置,开始执行一系列的动作,包<br>
括了检查RAM,keyboard,显示器,软硬磁盘等等,这些动作是由系统测试代码<br>
(system test code)来执行的,随着制作BIOS厂商的不同而会有些许差异,但都<br>
是大同小异,读者可自行观察自家机器开机时,萤幕上所显示的检查讯息。<br>
<br>
紧接着系统测试码之后,控制权会转移给ROM中的启动程序<br>
(ROM bootstrap routine),这个程序会将磁盘上的第零轨第零扇区读入<br>
内存中(这就是一般所谓的boot sector,如果你曾接触过电脑病<br>
毒,就大概听过它的大名),至於被读到内存的哪里呢? --绝对<br>
位置07C0:0000(即07C00h处),这是IBM系列PC的特性。而位在linux开机<br>
磁盘的boot sector上的正是linux的bootsect程序,也就是说,bootsect是<br>
第一个被读入内存中并执行的程序。现在,我们可以开始来<br>
看看到底bootsect做了什么。<br>
<br>
第一步<br>
首先,bootsect将它"自己"从被ROM BIOS载入的绝对地址0x7C00处搬到<br>
0x90000处,然后利用一个jmpi(jump indirectly)的指令,跳到新位置的<br>
jmpi的下一行去执行,<br>
<br>
第二步<br>
接着,将其他segment registers包括DS,ES,SS都指向0x9000这个位置,<br>
与CS看齐。另外将SP及DX指向一任意位移地址( offset ),这个地址等一下<br>
会用来存放磁盘参数表(disk para- meter table )<br>
<br>
第三步<br>
接着利用BIOS中断服务int 13h的第0号功能,重置磁盘控制器,使得刚才<br>
的设定发挥功能。<br>
<br>
第四步<br>
完成重置磁盘控制器之后,bootsect就从磁盘上读入紧邻着bootsect的setup<br>
程序,也就是setup.S,此读入动作是利用BIOS中断服务int 13h的第2号功能。<br>
setup的image将会读入至程序所指定的内存绝对地址0x90200处,也就是在内存<br>
中紧邻着bootsect 所在的位置。待setup的image读入内存后,利用BIOS中断服<br>
务int 13h的第8号功能读取目前磁盘的参数。<br>
<br>
第五步<br>
再来,就要读入真正linux的kernel了,也就是你可以在linux的根目录下看<br>
到的"vmlinuz" 。在读入前,将会先呼叫BIOS中断服务int 10h 的第3号功能,<br>
读取游标位置,之后再呼叫BIOS 中断服务int 10h的第13h号功能,在萤幕上输<br>
出字串"Loading",这个字串在boot linux时都会首先被看到,相信大家应该觉<br>
得很眼熟吧。<br>
<br>
第六步<br>
接下来做的事是检查root device,之后就仿照一开始的方法,利用indirect<br>
jump 跳至刚刚已读入的setup部份<br>
<br>
第七步<br>
setup.S完成在实模式下版本检查,并将硬盘,鼠标,内存参数写入到 INITSEG<br>
中,并负责进入保护模式。<br>
<br>
第八步<br>
操作系统的初始化。<br>
<br>
<br>
<br>
<br>
<br>
[目录]<br>
<br>
--------------------------------------------------------------------------------<br>
<br>
<br>
bootsect.S<br>
<br>
1.将自己移动到0x9000:0x0000处,为内核调入留出地址空间;<br>
2.建立运行环境(ss=ds=es=cs=0x9000, sp=0x4000-12),保证起动程序运行;<br>
3.BIOS初始化0x1E号中断为软盘参数表,将它取来保存备用;<br>
4.将setup读到0x9000:0x0200处;<br>
5.测试软盘参数一个磁道有多少个扇区(也没有什么好办法,只能试试36, 18, 15, 9对不对了);<br>
6.打印“Loading”;<br>
7.读入内核到0x1000:0000(如果是bzImage, 则将每个64K移动到0x100000处,在实模式下,只能调用0x15号中断了,这段代码无法放在bootsect中所以只能放在setup中,幸好此时setup已经读入了);<br>
8.到setup去吧<br>
发发信人: seis (矛), 信区: linux<br>
标 题: linux操作系统内核引导程序详细剖析<br>
发信站: BBS 水木清华站 (Fri Feb 2 14:12:43 2001)<br>
<br>
! bootsect.s (c) 1991, 1992 Linus Torvalds 版权所有<br>
! Drew Eckhardt修改过<br>
! Bruce Evans (bde)修改过<br>
!<br>
! bootsect.s 被bios-启动子程序加载至0x7c00 (31k)处,并将自己<br>
! 移到了地址0x90000 (576k)处,并跳转至那里。<br>
!<br>
! bde - 不能盲目地跳转,有些系统可能只有512k的低<br>
! 内存。使用中断0x12来获得(系统的)最高内存、等。<br>
!<br>
! 它然后使用BIOS中断将setup直接加载到自己的后面(0x90200)(576.5k),<br>
! 并将系统加载到地址0x10000处。<br>
!<br>
! 注意! 目前的内核系统最大长度限制为(8*65536-4096)(508k)字节长,即使是在<br>
! 将来这也是没有问题的。我想让它保持简单明了。这样508k的最大内核长度应该<br>
! 是足够了,尤其是这里没有象minix中一样包含缓冲区高速缓冲(而且尤其是现在<br>
! 内核是压缩的 icon_smile.gif<br>
!<br>
! 加载程序已经做的尽量地简单了,所以持续的读出错将导致死循环。只能手工重启。<br>
! 只要可能,通过一次取得整个磁道,加载过程可以做的很快的。<br>
<br>
#include /* 为取得CONFIG_ROOT_RDONLY参数 */<br>
!! config.h中(即autoconf.h中)没有CONFIG_ROOT_RDONLY定义!!!?<br>
<br>
#include<br>
<br>
.text<br>
<br>
SETUPSECS = 4 ! 默认的setup程序扇区数(setup-sectors)的默认值;<br>
<br>
BOOTSEG = 0x7C0 ! bootsect的原始地址;<br>
<br>
INITSEG = DEF_INITSEG ! 将bootsect程序移到这个段处(0x9000) - 避开;<br>
SETUPSEG = DEF_SETUPSEG ! 设置程序(setup)从这里开始(0x9020);<br>
SYSSEG = DEF_SYSSEG ! 系统加载至0x1000(65536)(64k)段处;<br>
SYSSIZE = DEF_SYSSIZE ! 系统的大小(0x7F00): 要加载的16字节为一节的数;<br>
!! 以上4个DEF_参数定义在boot.h中:<br>
!! DEF_INITSEG 0x9000<br>
!! DEF_SYSSEG 0x1000<br>
!! DEF_SETUPSEG 0x9020<br>
!! DEF_SYSSIZE 0x7F00 (=32512=31.75k)*16=508k<br>
<br>
! ROOT_DEV & SWAP_DEV 现在是由"build"中编制的;<br>
ROOT_DEV = 0<br>
SWAP_DEV = 0<br>
#ifndef SVGA_MODE<br>
#define SVGA_MODE ASK_VGA<br>
#endif<br>
#ifndef RAMDISK<br>
#define RAMDISK 0<br>
#endif<br>
#ifndef CONFIG_ROOT_RDONLY<br>
#define CONFIG_ROOT_RDONLY 1<br>
#endif<br>
<br>
! ld86 需要一个入口标识符,这和通常的一样;<br>
.globl _main<br>
_main:<br>
#if 0 /* 调试程序的异常分支,除非BIOS古怪(比如老的HP机)否则是无害的 */<br>
int 3<br>
#endif<br>
mov ax,#BOOTSEG !! 将ds段寄存器置为0x7C0;<br>
mov ds,ax<br>
mov ax,#INITSEG !! 将es段寄存器置为0x9000;<br>
mov es,ax<br>
mov cx,#256 !! 将cx计数器置为256(要移动256个字, 512字节);<br>
sub si,si !! 源地址 ds:si=0x07C0:0x0000;<br>
sub di,di !! 目的地址es:di=0x9000:0x0000;<br>
cld !! 清方向标志;<br>
rep !! 将这段程序从0x7C0:0(31k)移至0x9000:0(576k)处;<br>
movsw !! 共256个字(512字节)(0x200长);<br>
jmpi go,INITSEG !! 间接跳转至移动后的本程序go处;<br>
<br>
! ax和es现在已经含有INITSEG的值(0x9000);<br>
<br>
go: mov di,#0x4000-12 ! 0x4000(16k)是>=bootsect + setup 的长度 +<br>
! + 堆栈的长度 的任意的值;<br>
! 12 是磁盘参数块的大小 es:di=0x94000-12=592k-12;<br>
<br>
! bde - 将0xff00改成了0x4000以从0x6400处使用调试程序(bde)。如果<br>
! 我们检测过最高内存的话就不用担心这事了,还有,我的BIOS可以被配置为将wini驱动<br>
表<br>
! 放在内存高端而不是放在向量表中。老式的堆栈区可能会搞乱驱动表;<br>
<br>
mov ds,ax ! 置ds数据段为0x9000;<br>
mov ss,ax ! 置堆栈段为0x9000;<br>
mov sp,di ! 置堆栈指针INITSEG:0x4000-12处;<br>
/*<br>
* 许多BIOS的默认磁盘参数表将不能<br>
* 进行扇区数大于在表中指定<br>
* 的最大扇区数( - 在某些情况下<br>
* 这意味着是7个扇区)后面的多扇区的读操作。<br>
*<br>
* 由于单个扇区的读操作是很慢的而且当然是没问题的,<br>
* 我们必须在RAM中(为第一个磁盘)创建新的参数表。<br>
* 我们将把最大扇区数设置为36 - 我们在一个ED 2.88驱动器上所能<br>
* 遇到的最大值。<br>
*<br>
* 此值太高是没有任何害处的,但是低的话就会有问题了。<br>
*<br>
* 段寄存器是这样的: ds=es=ss=cs - INITSEG,(=0X9000)<br>
* fs = 0, gs没有用到。<br>
*/<br>
<br>
! 上面执行重复操作(rep)以后,cx为0;<br>
<br>
mov fs,cx !! 置fs段寄存器=0;<br>
mov bx,#0x78 ! fs:bx是磁盘参数表的地址;<br>
push ds<br>
seg fs<br>
lds si,(bx) ! ds:si是源地址;<br>
!! 将fs:bx地址所指的指针值放入ds:si中;<br>
mov cl,#6 ! 拷贝12个字节到0x9000:0x4000-12开始处;<br>
cld<br>
push di !! 指针0x9000:0x4000-12处;<br>
<br>
rep<br>
movsw<br>
<br>
pop di !! di仍指向0x9000:0x4000-12处(参数表开始处);<br>
pop si !! ds => si=INITSEG(=0X9000);<br>
<br>
movb 4(di),*36 ! 修正扇区计数值;<br>
<br>
seg fs<br>
mov (bx),di !! 修改fs:bx(0000:0x0078)处磁盘参数表的地址为0x9000:0x4000-12;<br>
seg fs<br>
mov 2(bx),es<br>
<br>
! 将setup程序所在的扇区(setup-sectors)直接加载到boot块的后面。!! 0x90200开始处<br>
;<br>
! 注意,es已经设置好了。<br>
! 同样经过rep循环后cx为0<br>
<br>
load_setup:<br>
xor ah,ah ! 复位软驱(FDC);<br>
xor dl,dl<br>
int 0x13<br>
<br>
xor dx,dx ! 驱动器0, 磁头0;<br>
mov cl,#0x02 ! 从扇区2开始,磁道0;<br>
mov bx,#0x0200 ! 置数据缓冲区地址=es:bx=0x9000:0x200;<br>
! 在INITSEG段中,即0x90200处;<br>
mov ah,#0x02 ! 要调用功能号2(读操作);<br>
mov al,setup_sects ! 要读入的扇区数SETUPSECS=4;<br>
! (假释所有数据都在磁头0、磁道0);<br>
int 0x13 ! 读操作;<br>
jnc ok_load_setup ! ok则继续;<br>
<br>
push ax ! 否则显示出错信息。保存ah的值(功能号2);<br>
call print_nl !! 打印换行;<br>
mov bp,sp !! bp将作为调用print_hex的参数;<br>
call print_hex !! 打印bp所指的数据;<br>
pop ax<br>
<br>
jmp load_setup !! 重试!<br>
<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -