网站建设原,组织建设方面存在的问题,汕头企业做网站,自己做的网站怎么在百度可以查到从电源到启动BIOS
从我们按下启动电源到BIOS#xff0c;按下电源–主板会向电源组发出信号– 接受到信号后#xff0c;当主板收到电源正常启动信号后#xff0c;主板会启动CPU(CPU重置所有寄存器数据#xff0c;并且初始化数据)#xff0c;比如32位系统#xff…从电源到启动BIOS
从我们按下启动电源到BIOS按下电源–主板会向电源组发出信号– 接受到信号后当主板收到电源正常启动信号后主板会启动CPU(CPU重置所有寄存器数据并且初始化数据)比如32位系统实模式采用内存段来管理0-0xFFFFF这1MB内存空间。 实模式内存访问是真实的内存地址软件可以不受限制操作实际内存所有地址空间和IO设备) 保护模式全部使用虚拟内存页等机制对内存进行保护比起实模式更为安全和可靠同时也增加了扩展性和灵活性 计算机的运行是离不开程序的但是CPU被设计的智能读取内存中的程序和数据不能直接从硬盘加载程序和数据必须先将程序和数据加载到内存那我们怎么启动程序呢秘诀是 0xFFFF0 既然软件的方式无法执行BIOS那我们就采取硬件的方式从硬件的角度看8086系列CPU可以分别在16位实模式和32位保护模式运行为了兼容也为了解决最初的启动问题Intel将所有的x86系列CPU包括最新的CPU硬件都设计为加电即进入16位实模式运行当然关键的是将CPU的硬件逻辑设计为加电瞬间强行将CS寄存器的值设置为 0xF000 IP寄存器的值设置为 0xFFF0 这样CS:IP就指向了 0xFFFF0这个地址位置 CS是代码段寄存器IP是指令指针寄存器两者组合就是即将要执行的指令的内存地址实模式为绝对地址指令指针为16位保护模式下为线性地址指令指针为32位即EIP 实模式下两者组合出线性地址的方式为 CS 4 IP 注意这是一个纯硬件操作如果 0xFFFF0 处没有任何可执行代码那么CPU就此死机但是BIOS程序的第一条指令就设计在这个位置
BIOS程序的代码量并不大但是却非诚精深需要对计算机硬件非常熟悉才能看明白这显然已经超出了 Linux 的范围所以只做简单讲解BIOS程序被固化在计算机主板上一块非常小的ROM内通常不同主板采用的BIOS也不同但是其基本原理大致相同当屏幕开始显示时这意味着BIOS已经启动了有一项对启动操作系统至关重要的工作就是BIOS在内存中建立中断向量表和中断服务程序。 ROM 只读存储器断电之后仍能保存信息但是某些情况下因为BIOS是写在闪存上的所以也可以更改毕竟BIOS也是可以升级的。 BIOS程序在内存最开始的位置用1KB构建中断向量表用256字节构建BIOS数据区在大约57KB之后的位置加载了8KB左右的与中断向量表相应的若干中断服务程序。此时内存的分布
内存位置内容0x00000 - 0x003FF中断向量表0x00400 - 0x004FFBIOS数据区0x00500 - 0x0E05A空0x0E05B - 0x0FFFE中断服务程序0x0FFFF - 0xFFFEF空0xFE000 - 0xFFFFFBIOS启动块
我们可以发现其实0xFFFF0并不是BIOS启动块的头地址只是BIOS的第一行程序的地址。 中断INT打断正在执行的程序转而执行处理这个时间的特定程序处理结束后回到被打断的程序继续执行可以理解为一种技术手段 加载操作系统内核
现在开始我们要加载操作系统内核了但是我们发现一个问题没有我们操作系统内核还是在硬盘或者软盘上没有在内存里啊这个其实也是硬件完成的BIOS启动之后计算机完成自检我们把我们的软盘设置成了启动设备所以计算机硬件和BIOS程序会联手操作让CPU收到一个 int 0x19 中断CPU收到这个中断后会立即在中断向量表中找到这个中断向量然后中断向量吧CPU指向 0x0E6F2这个位置这个位置就是对应的中断服务程序的入口地址这个中断是设计好的代码是固定的与我们的操作系统无关这段代码唯一的作用就是找到软盘加载第一扇区其余的它什么都不知道也不必知道。 中断向量表interrupt Vector Table实模式中断机制的重要组成部分记录所有的中断号对应的中断服务程序内存地址 中断服务程序interrupt Service Programme通过中断向量表的索引对中断进行响应服务是一些具有特殊功能的程序 按照这个规则int 0x19 中断将软盘的0号磁头对应的盘面的0磁道1扇区的内容复制到 0x07C00 的位置这个数据块有多大呢512B这个扇区内装载的就是Linux 0.11的引导程序也就是我们要讲的 bootsect.s 程序这是一个汇编程序。此时我们就有了我们操作系统自己的代码了虽然只是启动代码。此程序的源文件地址位于 /boot/bootsect.s
bootsect 的功能很简单就是把第二批和第三批程序陆续加载到内存为了能加载到适当位置bootsect 首先做的就是规划内存。要知道实模式下的内存寻址最大范围是1MB内存是非常宝贵的。
这里我们就涉及到Linux的内核源码了接着看
SYSSIZE 0x3000 ! 给出system模块的大小 0x3000字节 192KBSETUPLEN 4 ! setup程序的扇区数
BOOTSEG 0x07c0 ! bootsect的起始地址
INITSEG 0x9000 ! bootsect的目标地址
SETUPSEG 0x9020 ! setup的起始地址
SYSSEG 0x1000 ! system的起始加载地址
ENDSEG SYSSEG SYSSIZE ! system的终止地址! ROOT_DEV: 0x000 - same type of floppy as boot.
! 0x301 - first partition on first drive etc 指定根文件系统设备时第2个硬盘的第一个分区
ROOT_DEV 0x306
这里定义了一些数据目的是规划内存
entry _start
_start:mov ax,#BOOTSEGmov ds,axmov ax,#INITSEGmov es,axmov cx,#256sub si,sisub di,direpmovwjmpi go,INITSEG这里进入第一个函数 _start 这个函数的目的就是想 0x07C00处的引导程序搬到 0x90000 处在这次复制过程中DS和SI联合使用构成了原地址 0x07C00 ES和DI联合使用构成了目标地址 0x90000 而给CX赋值 256 的原因是 movw这个指令复制的是字一个字为两个字节256个字刚好为512字节rep反复执行movw直到CX为0执行完毕后执行跳转指令 ENTRY 是程序入口伪指令。在一个完整的汇编程序中至少有一个 ENTRY编译程序在编译连接时依据程序入口进行连接 go: mov ax,csmov ds,ax ! 数据段寄存器mov es,ax ! 附加段寄存器
! put stack at 0x9ff00. mov ss,ax ! 栈寄存器 栈操作是从高地址到低地址mov sp,#0xFF00 ! arbitrary value 512 这里使用CS初始化段寄存器和栈空间就比较简单了此时CS段寄存器的地址是 0x9000所以DSESSS都被赋值为 0x9000 这其实意味着我们可以使用栈了也意味着我们可以使用13号中断来从磁盘搬运程序了接着往下走
load_setup:mov dx,#0x0000 ! drive 0, head 0mov cx,#0x0002 ! sector 2, track 0mov bx,#0x0200 ! address 512, in INITSEGmov ax,#0x0200SETUPLEN ! service 2, nr of sectorsint 0x13 ! read it jnc ok_load_setup ! ok - continuemov dx,#0x0000 ! mov ax,#0x0000 ! reset the disketteint 0x13 j load_setup0x13 中断是一个服务用来对磁盘进行操作我们介绍一下各寄存器的功能如果要使用这个功能需要 AH必须设置为 0x02 AL扇区数目 CH柱面 CL扇区 DH 磁头 DL 驱动器00H - 7FH 为软盘80H - 0FFH 为硬盘 ES:BX 缓冲区地址 既然是读取操作必然要制导读取结果如果 CF0 则表示成功此时 AH状态码 AL传输的扇区数
此时ES 9000H而读取时BX 0x0200所以读取后被放到0x90200这个地址。如果读取成功就会跳转到ok_load_setup这个标签失败重置磁盘状态AH 0调用重试直到成功。执行完int 0x13指令后我们看看下面的程序
ok_load_setup:! Get disk drive parameters, specifically nr of sectors/trackmov dl,#0x00 ! DL 为驱动器 如果成功CF 0 BL会获得1-4的数据为磁盘大小mov ax,#0x0800 ! AH8 is get drive parameters AH 8 时是获取磁盘信息int 0x13 ! mov ch,#0x00 seg csmov sectors,cxmov ax,#INITSEGmov es,ax当AH 0x08时调用int 0x13指令是获取磁盘大小信息其中DL为驱动器如果成功CF 0BL会获得1-4的数值含义如下 BL1 360KB BL2 2.2MB BL3 720KB BL4 1.44MB 与此同时一些其他寄存器也被赋予了含义 CH代表柱面数的低八位 CL的高两位代表柱面数的高两位低六位代表扇区数 DH代表柱头数 DL代表驱动器数 ES:DI指向的是磁盘驱动器参数表地址 seg cs 只影响到mov sectors,cx而不影响mov ax,#INITSEG 如果以Masm语法写seg cs和mov sectors,cx两句合起来等价于mov cs:[sectors],cx这里使用了间接寻址方式。seg cs只是表明紧跟它的下一条语句将使用段超越因为在编译后的代码中可以清楚的看出段超越本质上就是加了一个字节的指令前缀。cx的高八位已经被置为0所以这里是将区块的扇区数目放到了CS:sectors这个地址中。
! Print some inane messagemov ah,#0x03 ! read cursor posxor bh,bhint 0x10 mov cx,#24mov bx,#0x0007 ! page 0, attribute 7 (normal)mov bp,#msg1mov ax,#0x1301 ! write string, move cursorint 0x10这段代码中的 int 0x10 中断用于向屏幕中输出字符串这一段代码将 msg1 输出到了屏幕上
msg1: ! \nLoading system ...\n.byte 13,10.ascii Loading system ....byte 13,10,13,10msg1 写的是 Loading system ...回车键的ASCII是13换行键的ASCII是10如果组合起来就是回车换行就是C/C的\n
我们继续下面的代码
! ok, weve written the message, now
! we want to load the system (at 0x10000)mov ax,#SYSSEGmov es,ax ! segment of 0x010000call read_itcall kill_motor这部分就是加载system模块了system模块就是内核模块包含库模块lib、内存管理模块mm、内核模块kernel、main.c和head.s程序后面将会详细介绍。read_it就是读取函数将模块读取到0x010000这个地址。kill_motor函数是关闭驱动器马达以知道驱动器状态。
从底层技术上看这与前面的setup程序加载没什么本质区别比较突出的特点是这次加载的扇区数是240个足足是之前加载4个扇区的60倍所需时间也是几十倍的增加为了防止用于以为是机器故障所以才有了之前的往屏幕上输出一个字符串的操作。
我们先看read_it这个函数的功能是把软盘第六扇区看事的约240个扇区的system模块加载到内存的SYSSEG(0x10000)处由于长时间的操作软盘所以对软盘设备需要更多地监控对读盘结果需要不断地检测因此read_it后续的调用步骤多一点
sread: .word 1SETUPLEN ! sectors read of current track
head: .word 0 ! current head
track: .word 0 ! current trackread_it:mov ax,estest ax,#0x0fff
die: jne die ! es must be at 64kB boundaryxor bx,bx ! bx is starting address within segment
rp_read:mov ax,escmp ax,#ENDSEG ! have we loaded all yet?jb ok1_readret
ok1_read:seg csmov ax,sectorssub ax,sreadmov cx,axshl cx,#9add cx,bxjnc ok2_readje ok2_readxor ax,axsub ax,bxshr ax,#9
ok2_read:call read_trackmov cx,axadd ax,sreadseg cscmp ax,sectorsjne ok3_readmov ax,#1sub ax,headjne ok4_readinc track
ok4_read:mov head,axxor ax,ax
ok3_read:mov sread,axshl cx,#9add bx,cxjnc rp_readmov ax,esadd ax,#0x1000mov es,axxor bx,bxjmp rp_readread_track:push axpush bxpush cxpush dxmov dx,trackmov cx,sreadinc cxmov ch,dlmov dx,headmov dh,dlmov dl,#0and dx,#0x0100mov ah,#2int 0x13jc bad_rtpop dxpop cxpop bxpop axret
bad_rt: mov ax,#0mov dx,#0int 0x13pop dxpop cxpop bxpop axjmp read_track嗯是不是乱的一批其实没有太多的必要去理解这个代码这个和硬件的关系太紧密了我们只需要知道这里搬运了一部分代码就可以了。
此时只剩下了最后一块bootsect.s程序
! After that we check which root-device to use. If the device is
! defined (! 0), nothing is done and the given device is used.
! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
! on the number of sectors that the BIOS reports currently.seg csmov ax,root_devcmp ax,#0jne root_definedseg csmov bx,sectorsmov ax,#0x0208 ! /dev/ps0 - 1.2Mbcmp bx,#15je root_definedmov ax,#0x021c ! /dev/PS0 - 1.44Mbcmp bx,#18je root_defined
undef_root:jmp undef_root
root_defined:seg csmov root_dev,axroot_dev是一个变量它指向一个字大小的宏ROOT_DEV它的值为0x306。
root_dev:.word ROOT_DEV为什么是这个值呢如果该值为0,根文件系统设备与引导使用同样的软驱设备如果是0x301则为第一个硬盘的第一个分区上这个被称为设备号。设备号 主设备号 * 256 次设备号
设备号设备文件对应的设备0x300/dev/hd0系统中第一个硬盘0x301/dev/hd1系统中第一个硬盘的第一分区0x302/dev/hd2系统中第一个硬盘的第二分区0x303/dev/hd3系统中第一个硬盘的第三分区0x304/dev/hd4系统中第一个硬盘的第四分区0x305/dev/hd5系统中第二个硬盘0x306/dev/hd6系统中第二个硬盘的第一分区0x307/dev/hd7系统中第二个硬盘的第二分区0x308/dev/hd8系统中第二个硬盘的第三分区0x309/dev/hd9系统中第二个硬盘的第四分区
这一部分就是再次确认一下根设备号 Linux 0.11使用的文件系统管理方式要求系统必须存在一个根文件系统其他文件系统挂接在骑上而不是同等地为Linux 0.11 并没有提供在设备上建立文件系统的工具所以必须在一个正在运行的系统上利用工具作出一个文件系统并加载至本机所以Linux 0.11 的启动需要分为两部分内核镜像和根文件系统。 于是该内核使用的是第二个硬盘的第一个分区作为根文件系统设备。接下来两个cmp可能看不懂咱们给个解释sectors是我们之前保存的每磁道扇区数目如果是 15 ,那么就是 1.2 MB 的驱动器如果是 18 ,那么就是 1.44 MB 的也就是引导驱动器的设备号。如果正常找到将会执行jmpi 0,SETUPSEG该部分程序结束否则直接死循环。
跳转到setup
跳转到setup就简单多了随便的一行
! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:jmpi 0,SETUPSEG总结
本文主要讲了从按下电源按钮的那一刻到将操作系统加载到内存中主要发生了什么这也是这一个系列的第一个部分的第一章。