我是如何學習寫一個操作系統(二):操作系統的啟動之Bootloader


前言

今天本來的任務看書和把之前寫的FragileOS整理一下,但是到現在還在摸魚,書也只看一點。后來整理了一下寫這個系列的思路,原本的目的是對操作系統原理性的學習和對之前寫的一個玩具型操作系統的回顧,就是想對操作系統的知識的輪廓能有一個了解,現在想來想減少對之前寫的系統的回顧,畢竟也只有2000多行,但是還是要有對整個思路的展現。然后增加對Linux 0.12源碼的一些學習。所以離標題可能比較遠了一點,但是就這樣吧

什么是操作系統

原本這一節是寫計算機系統和操作系統概述的,但是寫到一半覺得太水就刪了。就總結幾句,后面用到什么就補什么。計算機系統的概述應該屬於計算機組成原理的內容,這倆部分也是《操作系統:精髓和設計原理》的第一二章。但是覺得如果對於想學習操作系統內部的代碼的話,換成匯編的內容會更好。

進入正題,操作系統是什么

對於計算機來說最根本的運行方式,就是取指執行

對於在屏幕上輸出Hello,World!的過程,首先CPU拿到內存中的指令,這些指令是通知CPU把存在某個內存中的'H''E''L'等移動到顯存位置,這樣在屏幕上就可以看到這些字符了。這就是計算機最原始的運行方式

而操作系統就是對硬件層面的抽象,讓我們不用在直面硬件,如果想要再次在屏幕輸出字符,只要直接調用操作系統的write(windows下的好像是這個名),C語言中的printf下就是一個系統調用

當然操作系統絕對是比想象中的龐大的多,操作系統還對內存、終端、磁盤、網絡和文件等等進行管理,光windows 2000應該就有3000多萬行的代碼了。當然有簡陋的內存、進程管理和文件系統的玩具型內核,只要幾千行代碼就可以完成了。

操作系統的啟動

對於X86架構的計算機,開機時一共做這幾件事

  • 開機時的CS = 0xFFFF, IP = 0x0000

    這時候的CPU處理實模式,也就是尋址的方式是CS:IP (實模式和保護模式屬於CPU的工作模式,其中比較大的區別就是尋址的方式)

  • 尋址0xFFFF0

  • 檢查硬件設備,像鍵盤顯示器之類的

  • 將磁盤0磁道0扇區讀入0x7c00處

    會從這里讀入512字節,也就是傳說中的引導程序,這里放着計算機執行的第一段代碼

  • 設置cs = 0x7c00 ip = 0x0000

FragileOS/boot

這個是我之前寫的FragileOS的boot,采用的是Intel匯編格式

主要的邏輯就是:

  • 先加載到0x7c00位置
  • 進行初始化操作
  • 調用CPU提供的中斷來讀取數據
  • 讀取完畢后直接跳到內核的起始位置,也就是引導結束了

(部分代碼)

org  0x7c00;                                ;加載到內存0x7c00處

LoadAddr EQU  08000h                        ;內核的內存地址
BufferAddr EQU  7E0h                        ;讀取扇區的時候進行的緩存

BaseOfStack EQU 07c00h

entry:              
    mov  ax, 0                              ;進行寄存器的初始化操作
    mov  ss, ax
    mov  ds, ax

    mov  ax, BufferAddr
    mov  es, ax                             ;ES:BX 數據存儲緩沖區,指示扇區加載后放置的地址
    
    mov  ax, 0
    mov  ss, ax
    mov  sp, BaseOfStack
    mov  di, ax
    mov  si, ax
    

    mov          BX, 0                      ;ES:BX 數據存儲緩沖區
    mov          CH, 1                      ;CH 用來存儲柱面號
    mov          DH, 0                      ;DH 用來存儲磁頭號
    mov          CL, 0                      ;CL 用來存儲扇區號
    
read_floppy:                                ;每次都把扇區寫入緩存地址07E00處
    cmp          byte [load_count], 0       ;比較load_count地址處的值,如果=0就跳轉到begin_load
    je           begin_load
    
    mov          bx, 0
    inc          CL
    mov          AH, 0x02                   ;AH = 02 表示要做的是讀盤操作
    mov          AL, 1                      ;AL 表示要練習讀取幾個扇區
    mov          DL, 0                      ;驅動器編號,一般我們只有一個軟盤驅動器,所以寫死

    int          0x13                       ;調用BIOS中斷實現磁盤讀取功能
    jc           fin

Linux 0.12/boot

Linux 0.12的boot自然比上面的復雜的多,Linux采用的boot的匯編是as86格式的,其余的匯編采用的都是AT&T

Linux 0.12下的boot一共有三個文件:

  • bootsect
  • head
  • setup

(代碼太長不全部貼了,有需要的可以私信我)

bootsect

bootsect的主要作用就是把自己移動到0x90000處執行,然后再加載setup模塊 (也就是setup.s)到bootsect的后面,再把system模塊加載到0x10000處,這個也就是內核的主要部分

bootsect的開頭是一些常量的定義

SETUPLEN = 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
  • _start先設置好目的地址和源地址

    ds:si和es:di

  • 然后執行rep指令

    rep指令是重復的意思,它以cx寄存器的值為判斷,如果cx的值為0就停止

  • movw指令

    開始從[si]處移動cx個字到[di]處,這里也就是一共移動了256個字,512字節

  • 最后跳轉到0x9000開始執行

_start:
	mov	ax,#BOOTSEG
	mov	ds,ax
	mov	ax,#INITSEG
	mov	es,ax
	mov	cx,#256
	sub	si,si
	sub	di,di
	rep
	movw
	jmpi	go,INITSEG
  • 現在的這些代碼都是在0x90000后的
  • 先重新設置段寄存器和棧指針
go:	mov	ax,cs
	mov	ds,ax
	mov	es,ax
! put stack at 0x9ff00.
	mov	ss,ax
	mov	sp,#0xFF00		! arbitrary value >>512
  • 這一部分和我之前的一樣,就是調用中斷來讀取磁盤內容,只是Linux讀取的是在第二扇區的setup模塊

  • 如果失敗就重新設置驅動器然后跳回重新讀取

  • 成功就跳到ok_load_setup

  • ok_load_setup是設置根文件系統設備的,並且讀入SYSTEM模塊 (內核的主要部分)到0x10000處,結尾是跳到SETUP模塊

load_setup:
	mov	dx,#0x0000		! drive 0, head 0
	mov	cx,#0x0002		! sector 2, track 0
	mov	bx,#0x0200		! address = 512, in INITSEG
	mov	ax,#0x0200+SETUPLEN	! service 2, nr of sectors
	int	0x13			! read it
	jnc	ok_load_setup		! ok - continue
	mov	dx,#0x0000
	mov	ax,#0x0000		! reset the diskette
	int	0x13
	j	load_setup

小結

一個簡單的boot引導程序,顧名思義就是把做一些引導工作的,進行一些初始化設置再讀入真正的內核部分,進入OS。

其實Linux 0.12一個完整的boot應該還包括setup.s用來完成OS啟動前最后的設置 (進入保護模式等),head.s則是進入之后的設置。但是因為這兩部分包含了一些其它重要概念,所以打算再下一篇寫。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM