設(shè)置
  • 日夜間
    隨系統(tǒng)
    淺色
    深色
  • 主題色

Linux 0.11 第五回 | 進(jìn)入保護(hù)模式前的最后一次折騰內(nèi)存

低并發(fā)編程 2022/10/4 16:31:40 責(zé)編:子非

本文來自微信公眾號:低并發(fā)編程 (ID:dibingfa),作者:閃客

本系列會以一個(gè)讀小說的心態(tài),從開機(jī)啟動后的代碼執(zhí)行順序,帶著大家閱讀和賞析 Linux 0.11 全部核心代碼,了解操作系統(tǒng)的技術(shù)細(xì)節(jié)和設(shè)計(jì)思想。

你會跟著我一起,看著一個(gè)操作系統(tǒng)從啥都沒有開始,一步一步最終實(shí)現(xiàn)它復(fù)雜又精巧的設(shè)計(jì),讀完這個(gè)系列后希望你能發(fā)出感嘆,原來操作系統(tǒng)源碼就是這破玩意。

以下是已發(fā)布文章的列表,詳細(xì)了解本系列可以先從開篇詞看起。

開篇詞

第一回 | 最開始的兩行代碼

第二回 | 自己給自己挪個(gè)地兒

第三回 | 做好最最基礎(chǔ)的準(zhǔn)備工作

第四回 | 把自己在硬盤里的其他部分也放到內(nèi)存來

本系列的 GitHub 地址如下:

https://github.com/sunym1993/flash-linux0.11-talk

------- 正文開始 -------

書接上回,上回書咱們說到,操作系統(tǒng)已經(jīng)完成了各種從硬盤到內(nèi)存的加載,以及內(nèi)存到內(nèi)存的復(fù)制。

至此,整個(gè) bootsect.s 的使命就完成了,也是我們品讀完的第一個(gè)操作系統(tǒng)源碼文件。之后便跳轉(zhuǎn)到了 0x90200 這個(gè)位置開始執(zhí)行,這個(gè)位置處的代碼就是位于 setup.s 的開頭,我們接著來看。

start:
    mov ax,#0x9000  ; this is done in bootsect already, but
    mov ds,ax
    mov ah,#0x03    ; read cursor pos
    xor bh,bh
            ; save it in known place, con_init fetches
    mov [0],dx      ; it from 0x90000.

又有個(gè) int 指令。

前面的文章好好看過的話,一下就能猜出它要干嘛。還記不記得之前有個(gè) int 0x13 表示觸發(fā) BIOS 提供的讀磁盤中斷程序?這個(gè) int 0x10 也是一樣的,它也是觸發(fā) BIOS 提供的顯示服務(wù)中斷處理程序,而 ah 寄存器被賦值為 0x03 表示顯示服務(wù)里具體的讀取光標(biāo)位置功能

具體 BIOS 提供了哪些中斷服務(wù),如何去調(diào)用和獲取返回值,請大家自行尋找資料,這里只說結(jié)果。

這個(gè) int 0x10 中斷程序執(zhí)行完畢并返回時(shí),dx 寄存器里的值表示光標(biāo)的位置,具體說來其高八位 dh 存儲了行號,低八位 dl 存儲了列號。

這里說明一下:計(jì)算機(jī)在加電自檢后會自動初始化到文字模式,在這種模式下,一屏幕可以顯示 25 行,每行 80 個(gè)字符,也就是 80 列。

那下一步 mov [0],dx 就是把這個(gè)光標(biāo)位置存儲在 [0] 這個(gè)內(nèi)存地址處。注意,前面我們說過,這個(gè)內(nèi)存地址僅僅是偏移地址,還需要加上 ds 這個(gè)寄存器里存儲的段基址,最終的內(nèi)存地址是在 0x90000 處,這里存放著光標(biāo)的位置,以便之后在初始化控制臺的時(shí)候用到。

所以從這里也可以看出,這和我們平時(shí)調(diào)用一個(gè)方法沒什么區(qū)別,只不過這里的寄存器的用法相當(dāng)于入?yún)⒑头祷刂?/strong>,這里的 0x10 中斷號相當(dāng)于方法名。

這里又應(yīng)了之前說的一句話,操作系統(tǒng)內(nèi)核的最開始也處處都是 BIOS 的調(diào)包俠,有現(xiàn)成的就用唄。

再接下來的幾行代碼,都是和剛剛一樣的邏輯,調(diào)用一個(gè) BIOS 中斷獲取點(diǎn)什么信息,然后存儲在內(nèi)存中某個(gè)位置,我們迅速瀏覽一下就好咯。

比如獲取內(nèi)存信息。
 Get memory size (extended mem, kB)
    mov ah,#0x88
    int 0x15
    mov [2],ax
獲取顯卡顯示模式。
 Get video-card data:
    mov ah,#0x0f
    int 0x10
    mov [4],bx      ; bh = display page
    mov [6],ax      ; al = video mode, ah = window width
檢查顯示方式并取參數(shù)
; check for EGA/VGA and some config parameters
    mov ah,#0x12
    mov bl,#0x10
    int 0x10
    mov [8],ax
    mov [10],bx
    mov [12],cx
獲取第一塊硬盤的信息。
 Get hd0 data
    mov ax,#0x0000
    mov ds,ax
    lds si,[4*0x41]
    mov ax,#INITSEG
    mov es,ax
    mov di,#0x0080
    mov cx,#0x10
    rep
    movsb
獲取第二塊硬盤的信息。
 Get hd1 data
    mov ax,#0x0000
    mov ds,ax
    lds si,[4*0x46]
    mov ax,#INITSEG
    mov es,ax
    mov di,#0x0090
    mov cx,#0x10
    rep
    movsb

以上原理都是一樣的。

我們就沒必要細(xì)琢磨了,對操作系統(tǒng)的理解作用不大,只需要知道最終存儲在內(nèi)存中的信息是什么,在什么位置,就好了,之后會用到他們的。

內(nèi)存地址長度 (字節(jié)) 名稱 0x900002 光標(biāo)位置 0x900022

擴(kuò)展內(nèi)存數(shù) 0x900042 顯示頁面 0x900061

顯示模式 0x900071 字符列數(shù) 0x900082 未知 0x9000A1

顯示內(nèi)存 0x9000B1

顯示狀態(tài) 0x9000C2 顯卡特性參數(shù) 0x9000E1

屏幕行數(shù) 0x9000F1 屏幕列數(shù) 0x9008016

硬盤 1 參數(shù)表 0x9009016 硬盤 2 參數(shù)表 0x901FC2

根設(shè)備號

由于之后很快就會用 c 語言進(jìn)行編程,雖然匯編和 c 語言也可以用變量的形式進(jìn)行傳遞數(shù)據(jù),但這需要編譯器在鏈接時(shí)做一些額外的工作,所以這么多數(shù)據(jù)更方便的還是雙方共同約定一個(gè)內(nèi)存地址,我往這里存,你從這里取,就完事了。這恐怕是最最原始和直觀的變量傳遞的方式了。

把這些信息存儲好之后,操作系統(tǒng)又要做什么呢?我們繼續(xù)往下看。

cli         ; no interrupts allowed ;

就一行 cli,表示關(guān)閉中斷的意思。

因?yàn)楹竺嫖覀円言臼?BIOS 寫好的中斷向量表給覆蓋掉,也就是給破壞掉了,寫上我們自己的中斷向量表,所以這個(gè)時(shí)候是不允許中斷進(jìn)來的。

繼續(xù)看。

 first we move the system to it's rightful place
    mov ax,#0x0000
    cld          'direction'=0, movs moves forward
do_move:
    mov es,ax        destination segment
    add ax,#0x1000
    cmp ax,#0x9000
    jz  end_move
    mov ds,ax        source segment
    sub di,di
    sub si,si
    mov cx,#0x8000
    
    jmp do_move
; then we load the segment descriptors
end_move:
    ...

看到后面那個(gè) rep movsw 熟不熟悉,一開始我們把操作系統(tǒng)代碼從 0x7c00 移動到 0x90000 的時(shí)候就是用的這個(gè)指令,來圖回憶一下。

同前面的原理一樣,也是做了個(gè)內(nèi)存復(fù)制操作,最終的結(jié)果是,把內(nèi)存地址 0x10000 處開始往后一直到 0x90000 的內(nèi)容,統(tǒng)統(tǒng)復(fù)制到內(nèi)存的最開始的 0 位置,大概就是這么個(gè)效果。

由于之前的各種加載和復(fù)制,導(dǎo)致內(nèi)存看起來很亂,是時(shí)候進(jìn)行一波取舍和整理了,我們重新梳理一下此時(shí)的內(nèi)存布局。

棧頂?shù)刂啡匀皇?0x9FF00 沒有改變。

0x90000 開始往上的位置,原來是 bootsectsetup 程序的代碼,現(xiàn) bootsect 的一部分代碼在已經(jīng)被操作系統(tǒng)為了記錄內(nèi)存、硬盤、顯卡等一些臨時(shí)存放的數(shù)據(jù)給覆蓋了一部分。

內(nèi)存最開始的 00x80000 這 512K 被 system 模塊給占用了,之前講過,這個(gè) system 模塊就是除了 bootsect 和 setup 之外的全部程序鏈接在一起的結(jié)果,可以理解為操作系統(tǒng)的全部。

那么現(xiàn)在的內(nèi)存布局就是這個(gè)樣子。

好了,記住上面的圖就好了,這回是不是又重新清晰起來了?之前的什么 0x7c00,已經(jīng)是過去式了,趕緊忘掉它,向前看!

接下來,就要進(jìn)行有點(diǎn)技術(shù)含量的工作了,那就是模式的轉(zhuǎn)換,需要從現(xiàn)在的 16 位的實(shí)模式轉(zhuǎn)變?yōu)橹?32 位的保護(hù)模式,這是一項(xiàng)大工程!也是我認(rèn)為的這趟操作系統(tǒng)源碼旅程中,第一個(gè)頗為精彩的地方,大家做好準(zhǔn)備!

后面的世界越來越精彩,欲知后事如何,且聽下回分解。

廣告聲明:文內(nèi)含有的對外跳轉(zhuǎn)鏈接(包括不限于超鏈接、二維碼、口令等形式),用于傳遞更多信息,節(jié)省甄選時(shí)間,結(jié)果僅供參考,IT之家所有文章均包含本聲明。

相關(guān)文章

關(guān)鍵詞:Linux,linux

軟媒旗下網(wǎng)站: IT之家 最會買 - 返利返現(xiàn)優(yōu)惠券 iPhone之家 Win7之家 Win10之家 Win11之家

軟媒旗下軟件: 軟媒手機(jī)APP應(yīng)用 魔方 最會買 要知