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

前言

今天原本的任務看書和把以前寫的FragileOS整理一下,可是到如今還在摸魚,書也只看一點。後來整理了一下寫這個系列的思路,本來的目的是對操做系統原理性的學習和對以前寫的一個玩具型操做系統的回顧,就是想對操做系統的知識的輪廓能有一個瞭解,如今想來想減小對以前寫的系統的回顧,畢竟也只有2000多行,可是仍是要有對整個思路的展示。而後增長對Linux 0.12源碼的一些學習。因此離標題可能比較遠了一點,可是就這樣吧git

什麼是操做系統

本來這一節是寫計算機系統和操做系統概述的,可是寫到一半以爲太水就刪了。就總結幾句,後面用到什麼就補什麼。計算機系統的概述應該屬於計算機組成原理的內容,這倆部分也是《操做系統:精髓和設計原理》的第一二章。可是以爲若是對於想學習操做系統內部的代碼的話,換成彙編的內容會更好。github

進入正題,操做系統是什麼windows

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

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

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

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

操做系統的啓動

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

  • 開機時的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則是進入以後的設置。可是由於這兩部分包含了一些其它重要概念,因此打算再下一篇寫。

相關文章
相關標籤/搜索