Linux内核——实模式
文章目錄
- 項目目錄
- 1. 匯編程序的運行條件
- 1.1 可執(zhí)行文件
- 1.2 偏移地址
- 1.3 硬盤
- 1.4 代碼段和數(shù)據(jù)段
- 1.5 邏輯地址
- 1.6 物理地址
- 1.7 cpu 取指
- 2. x86 虛擬機(jī) bochs 實驗環(huán)境
- 2.1 安裝 bochs
- 2.2 匯編、鏈接程序
- 2.3 制作硬盤鏡像文件
- 2.4 啟動 bochs
- 2.5 BIOS
- 3. 匯編程序詳解
- 3.1 偽指令
- 3.2 符號
- 3.3 設(shè)置代碼段
- 3.4 設(shè)置數(shù)據(jù)段
- 3.5 中斷處理程序
- 3.6 調(diào)用中斷處理程序
- 3.7 使 cpu 進(jìn)入死循環(huán)
- 3.8 引導(dǎo)扇區(qū)標(biāo)志
項目目錄
root:[.] +--.DS_Store +--bochsrc.bxrc +--print_tree_file.py +--run.sh +--test1| +--bootsect.s| +--run_test1.sh1. 匯編程序的運行條件
cpu 只能識別、運行機(jī)器指令,無法運行匯編程序 bootsect.s 中的匯編指令,所以只有把匯編程序 bootsect.s 匯編、鏈接生成二進(jìn)制可執(zhí)行文件 bootsect.bin ,并將其加載到物理內(nèi)存后,cpu 才能逐條從物理內(nèi)存中取指、解析、執(zhí)行可執(zhí)行文件 bootsect.bin 中的機(jī)器指令。
1.1 可執(zhí)行文件
我們使用 as 匯編器和 ld 鏈接器把匯編程序 bootsect.s 匯編、鏈接成可執(zhí)行文件 bootsect.bin。
可執(zhí)行文件由可以被 cpu 直接運行的機(jī)器指令和機(jī)器指令運行過程中需要訪問的數(shù)據(jù)組成。可執(zhí)行文件 bootsect.bin 如圖1.1所示,機(jī)器指令在前面,每個實線方框表示一條指令;數(shù)據(jù)緊跟在機(jī)器指令的后面,每個虛線方框表示一個(組)數(shù)據(jù)。可執(zhí)行文件 bootsect.bin 由13條機(jī)器指令和3個(組)數(shù)據(jù)組成,其中,機(jī)器指令由匯編文件 bootsect.s 中的匯編指令匯編、鏈接得到,數(shù)據(jù)由匯編文件 bootsect.s 中的偽指令匯編、鏈接得到。
圖1.1.1 可執(zhí)行文件bootsect.bin1.2 偏移地址
可執(zhí)行文件中的每個字節(jié)都有唯一的地址——偏移地址。如圖1.1(1)所示,第一個字節(jié) 0xea 的偏移地址為0,每條機(jī)器指令和每個(組)數(shù)據(jù)都有唯一的偏移地址,是它們的第1個字節(jié)的偏移地址。第一個實線方框中的機(jī)器指令的偏移地址為0,第2個實線方框中的機(jī)器指令的偏移地址為5。
表1.2.1 匯編程序bootsect.s1.3 硬盤
只有把可執(zhí)行文件加載到物理內(nèi)存后,cpu才能逐條從物理內(nèi)存中讀取可執(zhí)行文件的機(jī)器指令,并解析和執(zhí)行機(jī)器指令。但是因為保存在物理內(nèi)存中的數(shù)據(jù)在掉電之后會消失,所以需要將可執(zhí)行文件保存在掉電后數(shù)據(jù)不會消失的硬盤中。在運行可執(zhí)行文件之前,再將其從硬盤加載到物理內(nèi)存。
我們可以把硬盤看做是一個由扇區(qū)組成的一維數(shù)組,每個扇區(qū)大小為512B,其中第一個扇區(qū)叫作引導(dǎo)扇區(qū)(本書中所有實驗中使用的硬盤大小為1MB,共計2048個扇區(qū),引導(dǎo)扇區(qū)的扇區(qū)號為0)。
1.4 代碼段和數(shù)據(jù)段
可執(zhí)行文件加載到物理內(nèi)存后,把占用的一段物理內(nèi)存叫作物理段,每個物理段都有一個屬性:物理段起始地址。將可執(zhí)行文件 bootsect.bin 加載到物理內(nèi)存后,占用的物理段的物理地址空間為 0x7c00~0x7dff(512B),物理段的起始地址為0x7c00。
當(dāng)物理內(nèi)存中的可執(zhí)行文件 bootsect.bin 在 cpu 中運行時,把機(jī)器指令所在的物理段叫作代碼段,把數(shù)據(jù)所在的物理段叫作數(shù)據(jù)段。可執(zhí)行文件 bootsect.bin 中包含了機(jī)器指令和數(shù)據(jù),所以代碼段和數(shù)據(jù)段共用同一個物理段,因此代碼段和數(shù)據(jù)段起始地址都是 0x7c00。
1.5 邏輯地址
當(dāng)可執(zhí)行文件被加載到物理內(nèi)存的物理段之后,代碼段中的機(jī)器指令和數(shù)據(jù)段中的數(shù)據(jù)就擁有了邏輯地址。在實模式下,邏輯地址的格式為:[右移4位后的代碼/數(shù)據(jù)段的物理起始地址:偏移地址]。第一個實線方框中的機(jī)器指令的邏輯地址為:[0x7c0:0],第2個實線方框中的機(jī)器指令的邏輯地址為[0x7c0:5],第一個虛線方框中的數(shù)據(jù)邏輯地址為:[0x7c0:0x20],最后一個虛線方框的數(shù)據(jù)邏輯地址為:[0x7c0:0x1fe]。
1.6 物理地址
物理內(nèi)存可以看做是一個由字節(jié)組成的一維數(shù)組,每個字節(jié)都有唯一的物理地址。物理內(nèi)存中的第1個字節(jié)的物理地址為0。機(jī)器指令/數(shù)據(jù)的物理地址=代碼/數(shù)據(jù)段的物理段起始地址+偏移地址。
1.7 cpu 取指
cpu 中有2個用于 cpu 從物理內(nèi)存中讀取機(jī)器指令的寄存器:
- 代碼段寄存器 cs。
- 機(jī)器指令偏移地址寄存器 ip。
寄存器 [cs:ip] 保存了 cpu 下一條需要從物理內(nèi)存讀取的機(jī)器指令的邏輯地址。
2. x86 虛擬機(jī) bochs 實驗環(huán)境
2.1 安裝 bochs
在 Ubuntu 18.04 中安裝 x86 虛擬機(jī) bochs 的debugger 版本的命令如下:
sudo apt-get install build-essential libx11-dev xorg-dev libgtk2.0-dev wget https://sourceforge.net/projects/bochs/files/bochs/2.6.8/bochs-2.6.8.tar.gz tar zxvf bochs-2.6.8.tar.gz cd bochs-2.6.8/ ./configure --enable-debugger --enable-disasm --enable-debugger-gui make -j4 sudo make install2.2 匯編、鏈接程序
as 是匯編器,-o bootsect.o 表示生成的可執(zhí)行文件的文件名為 bootsect.o。可執(zhí)行文件 bootsect.o 還需要鏈接才能在 cpu 上運行。
ld 是鏈接器,將 bootsect.o 鏈接成 bootsect.bin。
- --oformat binary:表示可執(zhí)行文件 bootsect.bin 中只包含機(jī)器指令和數(shù)據(jù);
- -Ttext=0:表示第一條機(jī)器指令的偏移地址為0。
- -o bootsect.bin:表示生成的可執(zhí)行文件的文件名為 bootsect.bin。
run_test1.sh 程序內(nèi)容:
# 匯編、鏈接bootsect.bin as -o bootsect.o bootsect.s ld --oformat binary -Ttext=0 -o bootsect.bin bootsect.o# 制作硬盤鏡像文件 dd if=/dev/zero of=../c.img bs=512 count=2048 dd if=bootsect.bin of=../c.img bs=512 seek=0 conv=notrunc將匯編和鏈接命令集合到 run_test1.sh 程序中,只需要使用 sh run_test1.sh 命令即可實現(xiàn)匯編和鏈接功能。
2.3 制作硬盤鏡像文件
在 bochs 中,需要使用硬盤鏡像文件虛擬物理硬盤。
給上層 test 目錄創(chuàng)建一個大小為 1MB 的硬盤鏡像文件 c.img 的命令:dd if=/dev/zero of=../c.img bs=512 count=2048。
- dd:文件拷貝命令。
- if=/dev/zero:表示拷貝的源文件的路徑,dev/zero是一個特殊的文件,可以提供 n(bs*count) 個 0。
- of=../c.img:表示拷貝的目標(biāo)文件路徑,若不存在則創(chuàng)建。
- bs=512:塊的大小,單位為B。
- count=2048:表示拷貝的文件的塊的數(shù)量。
硬盤鏡像文件制作好后,將可執(zhí)行文件 bootsect.bin 拷貝到硬盤鏡像文件 c.img 的引導(dǎo)扇區(qū)的命令:dd if=bootsect.bin of=../c.img bs=512 seek=0 conv=notrunc。
- seek=0:表示把可執(zhí)行文件 bootsect.bin 拷貝到硬盤鏡像文件 c.img 的引導(dǎo)扇區(qū)。
- conv=notrunc:表示不改變目標(biāo)文件的大小,若沒有該項,則硬盤鏡像文件 c.img 的大小會由 1MB 變?yōu)榭蓤?zhí)行文件 bootsect.bin 的大小512B。
2.4 啟動 bochs
“硬盤”制作好后,要想啟動 bochs,還需要一個配置文件(bochsrc.bxrc),文件內(nèi)容如下:
romimage: file=/usr/local/share/bochs/BIOS-bochs-latest vgaromimage: file=/usr/local/share/bochs/VGABIOS-lgpl-latest ata0-master: type=disk, path="c.img" megs: 16 cpu: count=1 boot: disk- romimage:指定 bochs 運行過程中使用 ROM-BIOS 的路徑。
- vgaromimage:指定 bochs 運行過程中使用的 VGA 的 ROM-BIOS 的路徑。
- ata0-master:指定硬盤鏡像文件 c.img 的路徑。
- megs:指定物理內(nèi)存的大小,單位為MB,bochs 中只有 16MB 物理內(nèi)存。
- cpu:指定 cpu 個數(shù),僅有1個cpu。
- boot:指定啟動方式,硬盤啟動。
run.sh 保存了啟動 bochs 的命令,文件內(nèi)容如下:
bochs -q -f bochsrc.bxrc- -q:跳過 bochs 啟動后的配置界面。
- -f bochsrc.bxrc:指定配置文件的路徑。
運行sh run.sh 命令后,就相當(dāng)于我們按下了一臺電腦的開機(jī)鍵。bochs 首先進(jìn)行一些自身的初始化工作,最終在終端輸出如下信息,并等待用戶輸入調(diào)試命令。
Next at t=0 (0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b ; ea5be000f0 <bochs:1>-
第1行:t 的值表示,cpu 從上電開始已經(jīng)運行了多少條機(jī)器指令。
-
第2行:cpu 上電后運行的第1條機(jī)器指令的信息(數(shù)字均為16進(jìn)制)
- [0x0000fffffff0]:該機(jī)器指令在物理內(nèi)存中的物理地址,在實模式下,cpu 最多只能訪問 1MB 物理內(nèi)存,即該機(jī)器指令的物理地址為 0xffff0;
- f000:fff0:當(dāng)前寄存器[cs:ip]的值,該機(jī)器指令的邏輯地址。
- jmpf 0xf000:e05b:該機(jī)器指令對應(yīng)的匯編指令;
- ea5be000f0:機(jī)器指令的內(nèi)容。
-
第3行:用戶輸入調(diào)試命令的命令行。
-
查看 cpu 中寄存器的值:r 命令和 sreg 命令。
- <bochs:1> reax: 0x00000000 0ecx: 0x00000000 0edx: 0x00000000 0ebx: 0x00000000 0esp: 0x00000000 0ebp: 0x00000000 0esi: 0x00000000 0edi: 0x00000000 0eip: 0x0000fff0eflags 0x00000002: id vip vif ac vm rf nt IOPL=0 of df if tf sf zf af pf cf<bochs:2> sreges:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessedcs:0xf000, dh=0xff0093ff, dl=0x0000ffff, valid=7Data segment, base=0xffff0000, limit=0x0000ffff, Read/Write, Accessedss:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessedds:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessedfs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessedgs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessedldtr:0x0000, dh=0x00008200, dl=0x0000ffff, valid=1tr:0x0000, dh=0x00008b00, dl=0x0000ffff, valid=1gdtr:base=0x00000000, limit=0xffffidtr:base=0x00000000, limit=0xffff
-
打斷點是重要的調(diào)試手段之一,bochs 提供了多個打斷點的命令,其中 vb 命令使用邏輯地址打斷點,b 命令使用物理地址打斷點,使用 blist 可以查看所有斷點信息。斷點打好后,使用 c 命令運行程序,當(dāng) cpu 下一條即將運行的機(jī)器指令被打了斷點時,cpu 停止在該機(jī)器指令前,等待輸入新的命令。
- <bochs:3> vb 0x0:0x7c00<bochs:4> b 0x7c00<bochs:5> blistNum Type Disp Enb Address1 vbreakpoint keep y 0x0000:00007c002 pbreakpoint keep y 0x000000007c00<bochs:6> c00000004661i[BIOS ] $Revision: 12579 $ $Date: 2014-12-26 11:31:39 +0100 (Fr, 26. Dez 2014) $00000318049i[KBD ] reset-disable command received00000320818i[BIOS ] Starting rombios3200000321256i[BIOS ] Shutdown flag 000000321840i[BIOS ] ram_size=0x0100000000000322261i[BIOS ] ram_end=16MB00000362771i[BIOS ] Found 1 cpu(s)00000376975i[BIOS ] bios_table_addr: 0x000fa498 end=0x000fcc0000000704770i[PCI ] i440FX PMC write to PAM register 59 (TLB Flush)00001032699i[P2ISA ] PCI IRQ routing: PIRQA# set to 0x0b00001032718i[P2ISA ] PCI IRQ routing: PIRQB# set to 0x0900001032737i[P2ISA ] PCI IRQ routing: PIRQC# set to 0x0b00001032756i[P2ISA ] PCI IRQ routing: PIRQD# set to 0x0900001032766i[P2ISA ] write: ELCR2 = 0x0a00001033536i[BIOS ] PIIX3/PIIX4 init: elcr=00 0a00001041217i[BIOS ] PCI: bus=0 devfn=0x00: vendor_id=0x8086 device_id=0x1237 class=0x060000001043496i[BIOS ] PCI: bus=0 devfn=0x08: vendor_id=0x8086 device_id=0x7000 class=0x060100001045614i[BIOS ] PCI: bus=0 devfn=0x09: vendor_id=0x8086 device_id=0x7010 class=0x010100001045839i[PIDE ] new BM-DMA address: 0xc00000001046455i[BIOS ] region 4: 0x0000c00000001048489i[BIOS ] PCI: bus=0 devfn=0x0b: vendor_id=0x8086 device_id=0x7113 class=0x068000001048721i[ACPI ] new irq line = 1100001048733i[ACPI ] new irq line = 900001048758i[ACPI ] new PM base address: 0xb00000001048772i[ACPI ] new SM base address: 0xb10000001048800i[PCI ] setting SMRAM control register to 0x4a00001212893i[CPU0 ] Enter to System Management Mode00001212904i[CPU0 ] RSM: Resuming from System Management Mode00001376925i[PCI ] setting SMRAM control register to 0x0a00001391791i[BIOS ] MP table addr=0x000fa570 MPC table addr=0x000fa4a0 size=0xc800001393613i[BIOS ] SMBIOS table addr=0x000fa58000001395781i[BIOS ] ACPI tables: RSDP addr=0x000fa6a0 ACPI DATA addr=0x00ff0000 size=0xf7200001398971i[BIOS ] Firmware waking vector 0xff00cc00001400766i[PCI ] i440FX PMC write to PAM register 59 (TLB Flush)00001401489i[BIOS ] bios_table_cur_addr: 0x000fa6c400001529106i[VBIOS ] VGABios $Id: vgabios.c,v 1.76 2013/02/10 08:07:03 vruppert Exp $00001529177i[BXVGA ] VBE known Display Interface b0c000001529209i[BXVGA ] VBE known Display Interface b0c500001532134i[VBIOS ] VBE Bios $Id: vbe.c,v 1.65 2014/07/08 18:02:25 vruppert Exp $00001570128i[XGUI ] charmap update. Font Height is 1600001856608i[XGUI ] charmap update. Font Height is 1600001876307i[BIOS ] ata0-0: PCHS=2/16/63 translation=none LCHS=2/16/6300005753481i[BIOS ] IDE time out00017844201i[BIOS ] Booting from 0000:7c00(0) Breakpoint 1, in 0000:7c00 (0x00007c00)Next at t=17844256(0) [0x000000007c00] 0000:7c00 (unk. ctxt): jmpf 0x07c0:0005 ; ea0500c007<bochs:7>
-
使用 u 命令查看 cpu 下一條即將運行的機(jī)器指令。
- <bochs:3> u /500007c00: ( ): jmpf 0x07c0:0005 ; ea0500c00700007c05: ( ): mov ax, 0x0600 ; b8000600007c08: ( ): mov ch, 0x00 ; b50000007c0a: ( ): mov cl, 0x00 ; b10000007c0c: ( ): mov dh, 0x18 ; b618
-
使用 xp 命令可以查看物理內(nèi)存中的指定物理地址的內(nèi)容。查看物理內(nèi)存 0x7dfe 開始的2個字節(jié)的內(nèi)容。
-
/2bx:打印2個字節(jié)(參數(shù)b為1個字節(jié),還有h、w)
/13c:將13個字節(jié)作為 ASCII 碼對應(yīng)的字符打印出來
- <bochs:4> xp /2bx 0x7dfe[bochs]:0x00007dfe <bogus+ 0>: 0x55 0xaa
-
使用 n 命令讓 cpu 運行一條機(jī)器指令后,停止并等待輸入新的命令。
- <bochs:5> nNext at t=17844257(0) [0x000000007c05] 07c0:0005 (unk. ctxt): mov ax, 0x0600 ; b80006
-
使用 xp 命令查看中斷向量表中的 0x10 號中斷處理程序的入口邏輯地址。
- <bochs:6> xp /1wx 0x40[bochs]:0x00000040 <bogus+ 0>: 0xc0000152
-
2.5 BIOS
x86 架構(gòu)的 cpu 在上電后處于實模式,在實模式下,cpu 最多只能訪問 1MB 物理內(nèi)存,其中 BIOS 占用的物理段的物理地址空間為:0xc0000~0xfffff。cpu 上電后,運行的第1條機(jī)器指令的物理地址為 0xffff0,該物理地址正好位于 BIOS 占用的物理段內(nèi),因此 cpu 上電后首先運行 BIOS。
BIOS 主要工作:
BIOS 運行完畢后,內(nèi)存狀態(tài)為:
圖2.5.1 1MB 物理內(nèi)存空間3. 匯編程序詳解
代碼清單(test1/bootsect.s) .code16 BOOTSEG = 0x7c0ljmp $BOOTSEG, $go go:movw %cs, %axmovw %ax, %dsmovw %ax, %esmovw $msg, %bpmovb $0x13, %ahmovb $0x01, %almovb $2, %blmovw msg_len, %cxmovb $0, %dhmovb $0, %dlint $0x10jmp . msg:.ascii "hello, world." msg_len:.word . - msg.org 0x1fe.word 0xaa553.1 偽指令
在匯編程序中,以.開頭的匯編語句叫作偽指令,匯編器 as 不會把偽指令匯編成機(jī)器指令。
- .code16:在實模式下,cpu 只能運行16位的機(jī)器指令,該偽指令的作用是:告訴匯編器,把匯編程序 bootsect.s 中的匯編、鏈接為16位的機(jī)器指令。
- .ascii "hello, world.":在可執(zhí)行文件 bootsect.bin 中的偏移地址空間 0x20~0x2c 中定義字符串“hello,world.”。
- .word . - msg:在可執(zhí)行文件 bootsect.bin 中的偏移地址空間 0x2d~0x2e 中定義一個2字節(jié)大小的數(shù)據(jù)(x86架構(gòu)中使用小端模式,低字節(jié)存放在物理內(nèi)存的低地址字節(jié))。
- .org 0x1fe:將第22行偽指令對應(yīng)的數(shù)據(jù)在可執(zhí)行文件 bootsect.bin 中的偏移地址設(shè)置為 0x1fe,若不是用該偽指令,則第22行偽指令對應(yīng)的數(shù)據(jù)的偏移地址為 0x2f,并將可執(zhí)行文件 bootsect.bin 中偏移地址 0x2f~0x1fd 中的內(nèi)容用0填充。
3.2 符號
在匯編程序中,使用符號表示一個數(shù),有兩種方式:
- 使用=表示。如代碼清單第2行所示,符號 BOOTSEG 值為 0x7c0。
- 使用:表示。則符號表示=后面的匯編語句對應(yīng)的機(jī)器指令或數(shù)據(jù)在可執(zhí)行文件中的偏移地址。符號 go 的值為 0x5,符號 msg 的值為 0x20。
在將匯編程序 bootsect.s 匯編為可執(zhí)行文件 bootsect.o 的過程中,匯編器 as 首先把匯編程序 bootsect.s 中所有的符號用其表示的數(shù)替換。
3.3 設(shè)置代碼段
機(jī)器指令的邏輯地址和物理地址是多對一的關(guān)系,即一個物理地址可以由多個邏輯地址計算得到。
BIOS 把bootsect.bin 從引導(dǎo)扇區(qū)加載到物理內(nèi)存的物理地址空間 0x7c00~0x7dff 后,將寄存器 [cs:ip] 賦值為可執(zhí)行文件 bootsect.bin 的第一條機(jī)器指令的邏輯地址 [0x0:0x7c00],代碼段和數(shù)據(jù)段的物理起始地址為 0。當(dāng) cpu 運行 可執(zhí)行文件 bootsect.bin 時,代碼段的物理起始地址為 0x7c00,故可執(zhí)行文件 bootsect.bin 的第一條機(jī)器指令需要對寄存器 [cs:ip] 重新賦值。
ljmp 匯編指令的作用是為寄存器 [cs:ip] 賦值,在匯編指令中寄存器前加 % 。
3.4 設(shè)置數(shù)據(jù)段
通常代碼段設(shè)置完成后,必須設(shè)置數(shù)據(jù)段。在實模式下,cpu 中除了用于保存代碼段的物理段起始地址右移4位的寄存器 cs 外,還有用于保存數(shù)據(jù)段的物理段起始地址右移 4 位的寄存器——數(shù)據(jù)段寄存器 ds 和數(shù)據(jù)段寄存器 es。
cpu 運行可執(zhí)行文件 bootsect.bin 時,代碼段和數(shù)據(jù)段的物理段起始地址都為 0x7c00,所以只需要將寄存器 cs 的值賦給寄存器 ds 和寄存器 es 即可,如代碼清單5~7行所示,不能將一個段寄存器的值直接賦給另外一個段寄存器。
每條 mov 匯編指令都有一個后綴:
- b:賦值給寄存器的數(shù)據(jù)大小為1個字節(jié);
- w:賦值給寄存器的數(shù)據(jù)大小為2個字節(jié);
- l:賦值給寄存器的數(shù)據(jù)大小為4個字節(jié);
3.5 中斷處理程序
BIOS 在創(chuàng)建中斷系統(tǒng)時,在 1MB 物理內(nèi)存的最后 256KB 物理地址空間內(nèi)保存了大量的中斷處理程序。這些中斷處理程序用于訪問系統(tǒng)中已有的硬件,訪問不同的硬件需要調(diào)用不同的中斷處理程序,每個中斷處理程序都有唯一的編號——中斷號。
常用中斷號舉例:
- 0x10:往顯示器的屏幕上打印字符。
- 0x13:從硬盤讀取數(shù)據(jù)。
調(diào)用中斷處理程序前,需要借助 cpu 中的寄存器傳遞參數(shù)。例如:調(diào)用 0x10 中斷處理程序往顯示器的屏幕上打印字符前,需要給 0x10 號中斷處理程序傳遞參數(shù),告訴它在屏幕的什么位置顯示什么內(nèi)容,內(nèi)容的長度以及屬性。
-
寄存器 ah:0x13 表示向屏幕打印字符串。
-
寄存器[es:bp]:保存字符串的首字符在數(shù)據(jù)段中的邏輯地址。
-
寄存器 cs:保存字符串的長度。
-
寄存器 (dh, dl):字符串在屏幕上的起始坐標(biāo),寄存器 dh 為行號(0~24),寄存器 dl 為列號(0~79)。
-
寄存器 al:指定光標(biāo)和字符的屬性。
- 0:表示字符的屬性保存在寄存器 bl 中,光標(biāo)停留在字符串的首字符
- 1:表示字符的屬性保存在寄存器 bl 中,光標(biāo)停留在字符串的尾字符
- 2:表示字符的屬性緊跟在字符之后,光標(biāo)停留在字符串的首字符
- 3:表示字符的屬性緊跟在字符之后,光標(biāo)停留在字符串的尾字符
-
寄存器 bl:若寄存器 al 的值為 0 或者 1,保存字符的屬性值。
-
字符屬性:
-
字符和背景顏色對照表:
二進(jìn)制顏色二進(jìn)制顏色 0000 黑色 1000 灰色 0001 藍(lán)色 1001 淡藍(lán)色 0010 綠色 1010 淡綠色 0011 青色 1011 淡青色 0100 紅色 1100 淡紅色 0101 紫紅色 1101 淡紫紅色 0110 棕色 1110 黃色 0111 銀色 1111 白色 -
使用 mov 指令賦值:
3.6 調(diào)用中斷處理程序
設(shè)置好為 0x10 號中斷處理程序傳遞參數(shù)的寄存器后,在匯編程序 bootsect.s 使用 int 匯編指令調(diào)用 0x10 號中斷處理程序,cpu 執(zhí)行第15行匯編指令對應(yīng)的機(jī)器指令的過程如下:
將當(dāng)前寄存器 [cs:ip] 中的邏輯地址(第16行匯編指令)保存起來。
將 0x10 號中斷處理程序的入口邏輯地址加載到寄存器[cs:ip]之中。每個中斷處理程序的入口邏輯地址都保存在中斷向量表中。中斷處理程序的入口邏輯地址按照中斷號依次存放在中斷向量表中,可以通過 0x10 號中斷處理程序的中斷號 0x10 從中斷向量表中獲取 0x10 中斷處理程序的入口邏輯地址[0xc000:0x152],將入口邏輯地址賦值給寄存器[cs:ip]后,cpu下一條運行的是 0x10 號中斷處理程序的第1條指令,物理地址為0xc0152。
運行 0x10 號中斷處理程序。
將 cpu 保存的第16行匯編指令對應(yīng)的機(jī)器指令的邏輯地址,重新賦值給 [cs:ip],因此 cpu 重新進(jìn)入 bootsect.bin 中運行。
3.7 使 cpu 進(jìn)入死循環(huán)
因為 cpu 會不停地根據(jù)寄存器 [cs:ip] 中的邏輯地址轉(zhuǎn)換后的物理地址,從物理內(nèi)存中讀取機(jī)器指令,然后對其解析、執(zhí)行,因此需要一條讓 cpu 進(jìn)入死循環(huán)的機(jī)器指令作為可執(zhí)行文件的最后一條機(jī)器指令,如代碼第16行所示,與 ljmp 不同的是,jmp 只給寄存器 ip 賦值。cpu 會一直循環(huán)運行第 16 行指令,不會將第1個虛線方框中的部分?jǐn)?shù)據(jù)看做機(jī)器指令,進(jìn)行取指、解析、執(zhí)行。
3.8 引導(dǎo)扇區(qū)標(biāo)志
BIOS 會將引導(dǎo)扇區(qū)中的可執(zhí)行文件拷貝到物理內(nèi)存,但是在拷貝前,會檢查引導(dǎo)扇區(qū)中的可執(zhí)行文件 bootsect.bin 的最后兩個字節(jié)的值是不是0x55和0xaa,若不是,則 BIOS 不會拷貝。
總結(jié)
以上是生活随笔為你收集整理的Linux内核——实模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 七牛云 上传图片问题
- 下一篇: ik分词器的下载和使用