哈工大李治军操作系统--操作系统基础(操作系统启动)

2023-11-22 20:10

本文主要是介绍哈工大李治军操作系统--操作系统基础(操作系统启动),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

打开电源,计算机执行的第一句指令是什么?

(1) x86PC刚开机时CPU处于实模式;
(2) 开机时, CS=0xFFFF, IP=0x0000;
(3) 寻址0xFFFF0(ROM BIOS映射区);
(4) 检查RAM, 键盘, 显示器, 软硬磁盘;
(5) 将软盘0面0道1扇区(主引导扇区, 对应操作系统第一段代码,即主引导程序.Linux 0.11源码对应文件为boot/bootsect.s)读入0x7c00处;
(6) 设置cs=0x07c0, ip=0x0000.

在这里插入图片描述

bootsect.s

bootsect.s (1个扇区)把自身从0x7c000处搬到0x90000处,再把setup.s(4个扇区)搬到0x90200处,然后调用0x13号中断在屏幕上打印“Loading system…”,再把system模块加载到0x10000处。之后确定根文件系统的设备号,若没指定,则根据所保存的引导盘的每磁道扇区数判定盘的类型(是1.44M A盘吗?)并保存其设备号于root_dev。最后139行跳转到setup.s执行。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
140行~227行代码用来加载system模块,由于采用比较原始的CHS模式读取软盘比较复杂,不再细看,暂且当成一个黑箱吧,日后若研究细节再做分析。

在这里插入图片描述
在这里插入图片描述

setup.s

代码105行之前,利用 ROM BIOS 中断例程获取系统参数(如光标位置、内存大小等),并写入 0x900000-x901FF 处(覆盖原来的 bootsect.s 区域)。然后把 system 模块从 0x10000-0x8ffff (当时认为 system 模块不会超过512K)搬到 0x0000 处(0x00000-0x003ff 的256个中断号的中断向量表被覆盖,之后再用int就不再是查0x00000处的中断向量表了,而是查IDT)。然后开启 A20 地址线并设置 8259A 中断控制芯片,再设置 CR0 的 PE 位,进入 32 位保护模式,转到 system 模块的第一个代码 head.s 执行。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
执行191行之后,跳到0x00000000。那么该处对应哪个代码文件呢?

Makefile

要让操作系统正常运行,必须通过Makefile控制各个代码文件加载顺序、如何组合等工作。Makefile是make工具的配置文件,相当于批处理文件,在含有Makefile的当前目录里执行make命令,就会根据Makefile中的信息对源代码进行编译、链接等工作。在这里插入图片描述
根据Makefile的配置要求,0x00000000处就是system模块的head.s,下面要进入head.s执行。
在这里插入图片描述

head.s

本程序重新设置GDT和IDT,并做一些校验工作(如A20地址线是否启动等),然后设置并开启分页(分页相关的内容在内存管理再说),然后跳入main函数执行。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

源代码(个人适当增加注释)

bootsect.s

!
! SYS_SIZE is the number of clicks (16 bytes) to be loaded.
! 0x3000 is 0x30000 bytes = 196kB, more than enough for current
! versions of linux
!
SYSSIZE = 0x3000
!
!	bootsect.s		(C) 1991 Linus Torvalds
!
! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
! iself out of the way to address 0x90000, and jumps there.
!
! It then loads 'setup' directly after itself (0x90200), and the system
! at 0x10000, using BIOS interrupts. 
!
! NOTE! currently system is at most 8*65536 bytes long. This should be no
! problem, even in the future. I want to keep it simple. This 512 kB
! kernel size should be enough, especially as this doesn't contain the
! buffer cache as in minix
!
! The loader has been made as simple as possible, and continuos
! read errors will result in a unbreakable loop. Reboot by hand. It
! loads pretty fast by getting whole sectors at a time whenever possible..globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.textSETUPLEN = 4				! nr of setup-sectors
BOOTSEG  = 0x07c0			! original address of boot-sector
INITSEG  = 0x9000			! we move boot here - out of the way
SETUPSEG = 0x9020			! setup starts here
SYSSEG   = 0x1000			! system loaded at 0x10000 (65536).
ENDSEG   = SYSSEG + SYSSIZE		! where to stop loading! ROOT_DEV:	0x000 - same type of floppy as boot.
!		0x301 - first partition on first drive etc
ROOT_DEV = 0x306 ! 设备号=主设备*256+次设备号,指定根文件系统是第2个硬盘的第1个分区! (主设备号:1-内存/2-软盘/3-硬盘/4-ttyx/5-tty/6-并行口/7-非命名管道)
entry _start
_start:mov	ax,#BOOTSEGmov	ds,axmov	ax,#INITSEGmov	es,axmov	cx,#256sub	si,sisub	di,direpmovwjmpi	go,INITSEG
go:	mov	ax,csmov	ds,axmov	es,ax
! put stack at 0x9ff00.mov	ss,axmov	sp,#0xFF00		! arbitrary value >>512,从0x90000开始的5个扇区是bootsect.s和setup.s,! sp > 5*0x200 + 堆栈大小
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.load_setup:mov	dx,#0x0000		! drive 0, head 0mov	cx,#0x0002		! sector 2, track 0mov	bx,#0x0200		! address = 512, in INITSEGmov	ax,#0x0200+SETUPLEN	! service 2, nr of sectorsint	0x13			! read it,参考BIOS中断手册,查看0x13号详情对比阅读jnc	ok_load_setup		! ok - continuemov	dx,#0x0000		mov	ax,#0x0000		! reset the disketteint	0x13			j	load_setup		! 加载不成功,重新加载ok_load_setup:! Get disk drive parameters, specifically nr of sectors/trackmov	dl,#0x00mov	ax,#0x0800		! AH=8 is get drive parametersint	0x13mov	ch,#0x00seg cs				! 87行只影响下一行即88行,87和88行等价于mov cs:[sectors],cxmov	sectors,cx		! 每道的扇区数mov	ax,#INITSEGmov	es,ax! Print some inane messagemov	ah,#0x03		! read cursor posxor	bh,bhint	0x10mov	cx,#24			! 打印到屏幕的字节数mov	bx,#0x0007		! page 0, attribute 7 (normal)mov	bp,#msg1		! 加载完setup.s,就打印msg1处的信息到屏幕mov	ax,#0x1301		! write string, move cursorint	0x10! ok, we've written the message, now
! we want to load the system (at 0x10000)mov	ax,#SYSSEGmov	es,ax		! segment of 0x010000call	read_itcall	kill_motor	! 关闭驱动马达,这样就可以获取驱动器的状态信息了! 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.! 软驱主设备号是2,次设备号是type*4+nr,nr为0-3对应软驱A/B/C/Dseg cs				! type是软驱类型,2-1.2M,7-1.44M,7*4+0=28(/dev/PS0,设备号0x021c)mov	ax,root_dev		! 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,ax! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:jmpi	0,SETUPSEG! This routine loads the system at address 0x10000, making sure
! no 64kB boundaries are crossed. We try to load it as fast as
! possible, loading whole tracks whenever we can.
!
! in:	es - starting address segment (normally 0x1000)
!
sread:	.word 1+SETUPLEN	! 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!/*
! * This procedure turns off the floppy drive motor, so
! * that we enter the kernel in a known state, and
! * don't have to worry about it later.
! */
kill_motor:push dxmov dx,#0x3f2mov al,#0outbpop dxretsectors:.word 0msg1:.byte 13,10.ascii "Loading system ...".byte 13,10,13,10.org 508
root_dev:.word ROOT_DEV
boot_flag:.word 0xAA55.text
endtext:
.data
enddata:
.bss
endbss:

setup.s

!
!	setup.s		(C) 1991 Linus Torvalds
!
! setup.s is responsible for getting the system data from the BIOS,
! and putting them into the appropriate places in system memory.
! both setup.s and system has been loaded by the bootblock.
!
! This code asks the bios for memory/disk/other parameters, and
! puts them in a "safe" place: 0x90000-0x901FF, ie where the
! boot-block used to be. It is then up to the protected mode
! system to read them from there before the area is overwritten
! for buffer-blocks.
!! NOTE! These had better be the same as in bootsect.s!INITSEG  = 0x9000	! we move boot here - out of the way
SYSSEG   = 0x1000	! system loaded at 0x10000 (65536).
SETUPSEG = 0x9020	! this is the current segment.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.textentry start
start:! ok, the read went well so we get current cursor position and save it for
! posterity.mov	ax,#INITSEG	! this is done in bootsect already, but...mov	ds,axmov	ah,#0x03	! read cursor posxor	bh,bhint	0x10		! save it in known place, con_init fetchesmov	[0],dx		! it from 0x90000.
! Get memory size (extended mem, kB)mov	ah,#0x88int	0x15mov	[2],ax! Get video-card data:mov	ah,#0x0fint	0x10mov	[4],bx		! bh = display pagemov	[6],ax		! al = video mode, ah = window width! check for EGA/VGA and some config parametersmov	ah,#0x12mov	bl,#0x10int	0x10mov	[8],axmov	[10],bxmov	[12],cx! Get hd0 datamov	ax,#0x0000mov	ds,axlds	si,[4*0x41]		! lds reg,操作数,操作数高16位给ds,低16位给regmov	ax,#INITSEGmov	es,axmov	di,#0x0080mov	cx,#0x10repmovsb! Get hd1 datamov	ax,#0x0000mov	ds,axlds	si,[4*0x46]mov	ax,#INITSEGmov	es,axmov	di,#0x0090mov	cx,#0x10repmovsb! Check that there IS a hd1 :-)mov	ax,#0x01500mov	dl,#0x81int	0x13jc	no_disk1cmp	ah,#3je	is_disk1
no_disk1:mov	ax,#INITSEGmov	es,axmov	di,#0x0090mov	cx,#0x10mov	ax,#0x00repstosb
is_disk1:! now we want to move to protected mode ...cli			! no interrupts allowed !! first we move the system to it's rightful placemov	ax,#0x0000cld			! 'direction'=0, movs moves forward
do_move:mov	es,ax		! destination segmentadd	ax,#0x1000cmp	ax,#0x9000jz	end_movemov	ds,ax		! source segmentsub	di,disub	si,simov 	cx,#0x8000repmovswjmp	do_move! then we load the segment descriptorsend_move:mov	ax,#SETUPSEG	! right, forgot this at first. didn't work :-)mov	ds,axlidt	idt_48		! load idt with 0,0lgdt	gdt_48		! load gdt with whatever appropriate! that was painless, now we enable A20call	empty_8042mov	al,#0xD1		! command writeout	#0x64,alcall	empty_8042mov	al,#0xDF		! A20 onout	#0x60,alcall	empty_8042! well, that went ok, I hope. Now we have to reprogram the interrupts :-(
! we put them right after the intel-reserved hardware interrupts, at
! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
! messed this up with the original PC, and they haven't been able to
! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
! which is used for the internal hardware interrupts as well. We just
! have to reprogram the 8259's, and it isn't fun.mov	al,#0x11		! initialization sequenceout	#0x20,al		! send it to 8259A-1.word	0x00eb,0x00eb		! jmp $+2, jmp $+2out	#0xA0,al		! and to 8259A-2.word	0x00eb,0x00ebmov	al,#0x20		! start of hardware int's (0x20)out	#0x21,al.word	0x00eb,0x00ebmov	al,#0x28		! start of hardware int's 2 (0x28)out	#0xA1,al.word	0x00eb,0x00ebmov	al,#0x04		! 8259-1 is masterout	#0x21,al.word	0x00eb,0x00ebmov	al,#0x02		! 8259-2 is slaveout	#0xA1,al.word	0x00eb,0x00ebmov	al,#0x01		! 8086 mode for bothout	#0x21,al.word	0x00eb,0x00ebout	#0xA1,al.word	0x00eb,0x00ebmov	al,#0xFF		! mask off all interrupts for nowout	#0x21,al.word	0x00eb,0x00ebout	#0xA1,al! well, that certainly wasn't fun :-(. Hopefully it works, and we don't
! need no steenking BIOS anyway (except for the initial loading :-).
! The BIOS-routine wants lots of unnecessary data, and it's less
! "interesting" anyway. This is how REAL programmers do it.
!
! Well, now's the time to actually move into protected mode. To make
! things as simple as possible, we do no register set-up or anything,
! we let the gnu-compiled 32-bit programs do that. We just jump to
! absolute address 0x00000, in 32-bit protected mode.mov	ax,#0x0001	! protected mode (PE) bitlmsw	ax		! This is it! 加载机器状态字jmpi	0,8		! jmp offset 0 of segment 8 (cs)! This routine checks that the keyboard command queue is empty
! No timeout is used - if this hangs there is something wrong with
! the machine, and we probably couldn't proceed anyway.
empty_8042:.word	0x00eb,0x00ebin	al,#0x64	! 8042 status porttest	al,#2		! is input buffer full?jnz	empty_8042	! yes - loopretgdt:.word	0,0,0,0		! dummy.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb).word	0x0000		! base address=0.word	0x9A00		! code read/exec.word	0x00C0		! granularity=4096, 386.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb).word	0x0000		! base address=0.word	0x9200		! data read/write.word	0x00C0		! granularity=4096, 386idt_48:.word	0			! idt limit=0.word	0,0			! idt base=0Lgdt_48:.word	0x800		! gdt limit=2048, 256 GDT entries.word	512+gdt,0x9	! gdt base = 0X9xxxx 32位线性基地址高16位是0x0009,! 低16位是512+gdt,对应绝对地址就是0x0009xxxx,更
.text					! 准确的就是0x0009020+gdt,正好就是本代码标号gdt处
endtext:
.data
enddata:
.bss
endbss:

参考资料

[1] 中国大学MOOC《操作系统》李治军 哈尔滨工业大学
[2] 《Linux内核完全注释》赵炯
[3] BIOS中断大全
[4] 汇编语言最全指令表
[5] 《x86汇编语言:从实模式到保护模式》李忠 著
[6] https://blog.csdn.net/ccnuacmhdu/article/details/104946213

这篇关于哈工大李治军操作系统--操作系统基础(操作系统启动)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/412615

相关文章

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

springboot3打包成war包,用tomcat8启动

1、在pom中,将打包类型改为war <packaging>war</packaging> 2、pom中排除SpringBoot内置的Tomcat容器并添加Tomcat依赖,用于编译和测试,         *依赖时一定设置 scope 为 provided (相当于 tomcat 依赖只在本地运行和测试的时候有效,         打包的时候会排除这个依赖)<scope>provided

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

用命令行的方式启动.netcore webapi

用命令行的方式启动.netcore web项目 进入指定的项目文件夹,比如我发布后的代码放在下面文件夹中 在此地址栏中输入“cmd”,打开命令提示符,进入到发布代码目录 命令行启动.netcore项目的命令为:  dotnet 项目启动文件.dll --urls="http://*:对外端口" --ip="本机ip" --port=项目内部端口 例: dotnet Imagine.M

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

Linux服务器Java启动脚本

Linux服务器Java启动脚本 1、初版2、优化版本3、常用脚本仓库 本文章介绍了如何在Linux服务器上执行Java并启动jar包, 通常我们会使用nohup直接启动,但是还是需要手动停止然后再次启动, 那如何更优雅的在服务器上启动jar包呢,让我们一起探讨一下吧。 1、初版 第一个版本是常用的做法,直接使用nohup后台启动jar包, 并将日志输出到当前文件夹n

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

SpringBoot项目是如何启动

启动步骤 概念 运行main方法,初始化SpringApplication 从spring.factories读取listener ApplicationContentInitializer运行run方法读取环境变量,配置信息创建SpringApplication上下文预初始化上下文,将启动类作为配置类进行读取调用 refresh 加载 IOC容器,加载所有的自动配置类,创建容器在这个过程