- 從零開始寫Linux內核:一書學透核心原理與實現
- 海納
- 1590字
- 2025-04-09 18:37:35
1.3 第一個內核程序
學習一門編程語言往往會從打印Hello World開始,開發操作系統也同樣如此。讀者在本節將會看到一個最簡單的操作系統雛形,它的唯一作用就是在屏幕上顯示“Hello World!”。
1.3.1 打印Hello World
首先創建文件bootsect.S,這是一個基于匯編語言的源文件。系統的引導就是從這個文件開始的,使用匯編語言與硬件打交道是最方便的。本書選擇的匯編編譯器是GNU的as,采用的是AT & T語法。編輯文件內容,如代碼清單1-1所示。
代碼清單1-1 bootsect.S源代碼
1 BOOTSEG=0x7c0
2
3 .code16
4 .text
5
6 .global_start
7 _start:
8 jmpl$BOOTSEG,$start2
9
10 start2:
11 movw $BOOTSEG,%ax
12 movw %ax,%ds
13 movw %ax,%es
14 movw %ax,%fs
15 movw %ax,%gs
16
17 movw $msg,%ax
18 movw %ax,%bp
19 movw $0x01301,%ax
20 movw $0x0c,%bx #文字為紅色
21 movw $12,%cx #字符串長度
22 movb $0,%dl
23 int $0x010 #通知顯卡刷新內容
24
25 loop:
26 jmp loop
27
28 msg:
29 .ascii"Hello World!"
30
31 .org 510
32 boot_flag:
33 .word 0xaa55
在控制臺編譯bootsect.S,命令如下:
1 #as-o bootsect.o bootsect.S
2 #ld-m elf_x86_64-Ttext 0x0-s--oformat binary-o linux.img bootsect.o
如果一切順利,則在當前目錄下可以看到linux.img文件已創建。將這個文件復制到Bochs的linux011目錄下,并在這個目錄下執行run.bat文件,則會看到Bochs虛擬機運行起來以后在屏幕上打印了紅色的“Hello World!”,如圖1-1所示。

圖1-1 在屏幕上打印“Hello World!”
如果使用QEMU運行,則需要將linux.img復制到QEMU所在的目錄中,然后在QEMU文件路徑中打開PowerShell或者cmd,并執行如下命令:
.\qemu-system-i386.exe-boot a-fda linux.img
則同樣可以看到,QEMU虛擬機運行起來后在屏幕中打印了紅色的“Hello World!”,如圖1-2所示。

圖1-2 在QEMU中顯示“Hello World!”
代碼清單1-1第11~15行是設置寄存器的值:將cs寄存器中的值設置到ds和es寄存器。第17~18行是將要打印的字符串的首地址放到bp寄存器。第23行是一條中斷觸發指令,中斷號是0x10,表示和顯示器相關的服務。中斷功能號保存在ah中。在第19行,ax被賦值為0x1301,那么對應的ah值為0x13。0x13表示在teletype模式下顯示字符串。同時al的值為0x01,表示顯示輸出方式為字符串中含顯示字符和顯示屬性,并且顯示后光標位置不變。第20行將0x0c放入bx,在中斷號為0x10、功能號為0x13的情況下,bh寄存器存放的是頁碼,bl寄存器用于設置文字顏色,其中0xc代表紅色。第21行將字符串長度送入cx寄存器。第22行表示輸出光標的位置,dh表示行號、dl表示列號,這里都為0,表示光標在屏幕的左上角。
1.3.2 開機引導程序
BIOS(Basic Input/Output System,基本輸入/輸出系統)是計算機接通電源后執行的第一個程序。BIOS首先會做硬件檢查,判斷是否滿足計算機運行的條件,例如,內存條如果沒插好,BIOS會提示錯誤信息,某些情況下主板會發出蜂鳴聲警告等。
做完硬件檢查之后要確定啟動順序,當選中某塊磁盤之后,控制權限就會交給這塊磁盤上的MBR(Master Boot Record,主引導記錄)。MBR位于磁盤的第一個扇區,一個扇區只有512字節,其中最后兩個字節是0x55和0xAA,表明這個設備可以啟動。
回顧1.3.1節的bootsect.S的ld選項,-Ttext 0x0的含義正是將目標文件bootsect.o的代碼段放到linux.img的開頭,也就是在第一個扇區。同時我們可以用如下命令查看第一個扇區的最后兩個字節為0x55AA,可以用來啟動。
xxd-s 510 linux.img
1981年8月,IBM公司最早的個人電腦IBM PC 5150上市,使用的是Intel的第一代個人電腦芯片8088。8088芯片本身需要占用0x0000~0x03FF的地址空間,用來保存各種中斷處理程序(引導程序本身就是中斷信號INT 19h的處理程序)。所以,內存只剩下0x0400至0x7FFF的地址空間可以使用。為了把盡量多的連續內存留給操作系統,引導程序就被放到了內存地址的尾部。因為一個扇區是512(十六進制為0x200)字節,引導程序本身也需要一段內存保存數據,系統就另外給它留出512字節。所以,引導扇區的預留位置就變成了0x7FFF?512?512+1=0x7FFF?0x200?0x200+0x1=0x7C00。
在1.3.1節的例子中,bootsect.S中定義了BOOTSEG=0x7c0,匯編代碼將被加載到內存地址0x7C00執行。需要注意的是,早期的8086處理器的寄存器都是16位的,地址線是20位,這就意味著CPU的尋址能力是1MB(2的20次方),但是只采用一個寄存器只能尋址64KB(2的16次方),所以它采用了基地址加偏移的方式尋址,也就是使用兩個寄存器的值拼接一個真實的物理地址。它的計算方式是物理地址等于基地址左移4位加上偏移值,例如下面的代碼:
mov %ds:(%ax),%bx
上述代碼表示以ds寄存器為基地址,以ax寄存器為偏移值計算一個地址,然后取這個內存地址處的值,送入bx寄存器。其中,真實物理地址的值是ds的值左移4位再加上ax寄存器的值。