本文來自微信公眾號:低并發(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ì)了解本系列可以先從開篇詞看起。
開篇詞
本系列的 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 開始往上的位置,原來是 bootsect 和 setup 程序的代碼,現(xiàn) bootsect 的一部分代碼在已經(jīng)被操作系統(tǒng)為了記錄內(nèi)存、硬盤、顯卡等一些臨時(shí)存放的數(shù)據(jù)給覆蓋了一部分。
內(nèi)存最開始的 0 到 0x80000 這 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之家所有文章均包含本聲明。