Linux内核——实模式

文章目录

  • 项目目录
    • 1. 汇编程序的运行条件
      • 1.1 可执行文件
      • 1.2 偏移地址
      • 1.3 硬盘
      • 1.4 代码段和数据段
      • 1.5 逻辑地址
      • 1.6 物理地址
      • 1.7 cpu 取指
    • 2. x86 虚拟机 bochs 实验环境
      • 2.1 安装 bochs
      • 2.2 汇编、链接程序
      • 2.3 制作硬盘镜像文件
      • 2.4 启动 bochs
      • 2.5 BIOS
    • 3. 汇编程序详解
      • 3.1 伪指令
      • 3.2 符号
      • 3.3 设置代码段
      • 3.4 设置数据段
      • 3.5 中断处理程序
      • 3.6 调用中断处理程序
      • 3.7 使 cpu 进入死循环
      • 3.8 引导扇区标志

项目目录

root:[.]
+--.DS_Store
+--bochsrc.bxrc
+--print_tree_file.py
+--run.sh
+--test1
   | +--bootsect.s
   | +--run_test1.sh

1. 汇编程序的运行条件

cpu 只能识别、运行机器指令,无法运行汇编程序 bootsect.s 中的汇编指令,所以只有把汇编程序 bootsect.s 汇编、链接生成二进制可执行文件 bootsect.bin ,并将其加载到物理内存后,cpu 才能逐条从物理内存中取指、解析、执行可执行文件 bootsect.bin 中的机器指令。

1.1 可执行文件

我们使用 as 汇编器和 ld 链接器把汇编程序 bootsect.s 汇编、链接成可执行文件 bootsect.bin。

可执行文件由可以被 cpu 直接运行的机器指令和机器指令运行过程中需要访问的数据组成。可执行文件 bootsect.bin 如图1.1所示,机器指令在前面,每个实线方框表示一条指令;数据紧跟在机器指令的后面,每个虚线方框表示一个(组)数据。可执行文件 bootsect.bin 由13条机器指令和3个(组)数据组成,其中,机器指令由汇编文件 bootsect.s 中的汇编指令汇编、链接得到,数据由汇编文件 bootsect.s 中的伪指令汇编、链接得到。

Linux内核——实模式_第1张图片

图1.1.1 可执行文件bootsect.bin

1.2 偏移地址

可执行文件中的每个字节都有唯一的地址——偏移地址。如图1.1(1)所示,第一个字节 0xea 的偏移地址为0,每条机器指令和每个(组)数据都有唯一的偏移地址,是它们的第1个字节的偏移地址。第一个实线方框中的机器指令的偏移地址为0,第2个实线方框中的机器指令的偏移地址为5。

表1.2.1 汇编程序bootsect.s

Linux内核——实模式_第2张图片

1.3 硬盘

只有把可执行文件加载到物理内存后,cpu才能逐条从物理内存中读取可执行文件的机器指令,并解析和执行机器指令。但是因为保存在物理内存中的数据在掉电之后会消失,所以需要将可执行文件保存在掉电后数据不会消失的硬盘中。在运行可执行文件之前,再将其从硬盘加载到物理内存。

我们可以把硬盘看做是一个由扇区组成的一维数组,每个扇区大小为512B,其中第一个扇区叫作引导扇区(本书中所有实验中使用的硬盘大小为1MB,共计2048个扇区,引导扇区的扇区号为0)。

1.4 代码段和数据段

可执行文件加载到物理内存后,把占用的一段物理内存叫作物理段,每个物理段都有一个属性:物理段起始地址。将可执行文件 bootsect.bin 加载到物理内存后,占用的物理段的物理地址空间为 0x7c00~0x7dff(512B),物理段的起始地址为0x7c00。

当物理内存中的可执行文件 bootsect.bin 在 cpu 中运行时,把机器指令所在的物理段叫作代码段,把数据所在的物理段叫作数据段。可执行文件 bootsect.bin 中包含了机器指令和数据,所以代码段和数据段共用同一个物理段,因此代码段和数据段起始地址都是 0x7c00。

1.5 逻辑地址

当可执行文件被加载到物理内存的物理段之后,代码段中的机器指令和数据段中的数据就拥有了逻辑地址。在实模式下,逻辑地址的格式为:[右移4位后的代码/数据段的物理起始地址:偏移地址]。第一个实线方框中的机器指令的逻辑地址为:[0x7c0:0],第2个实线方框中的机器指令的逻辑地址为[0x7c0:5],第一个虚线方框中的数据逻辑地址为:[0x7c0:0x20],最后一个虚线方框的数据逻辑地址为:[0x7c0:0x1fe]。

1.6 物理地址

物理内存可以看做是一个由字节组成的一维数组,每个字节都有唯一的物理地址。物理内存中的第1个字节的物理地址为0。机器指令/数据的物理地址=代码/数据段的物理段起始地址+偏移地址

1.7 cpu 取指

cpu 中有2个用于 cpu 从物理内存中读取机器指令的寄存器:

  • 代码段寄存器 cs。
  • 机器指令偏移地址寄存器 ip。

寄存器 [cs:ip] 保存了 cpu 下一条需要从物理内存读取的机器指令的逻辑地址。

2. x86 虚拟机 bochs 实验环境

2.1 安装 bochs

在 Ubuntu 18.04 中安装 x86 虚拟机 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 install

2.2 汇编、链接程序

as 是汇编器,-o bootsect.o 表示生成的可执行文件的文件名为 bootsect.o。可执行文件 bootsect.o 还需要链接才能在 cpu 上运行。

ld 是链接器,将 bootsect.o 链接成 bootsect.bin。

  • --oformat binary:表示可执行文件 bootsect.bin 中只包含机器指令和数据;
  • -Ttext=0:表示第一条机器指令的偏移地址为0。
  • -o bootsect.bin:表示生成的可执行文件的文件名为 bootsect.bin。

run_test1.sh 程序内容:

# 汇编、链接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 命令即可实现汇编和链接功能。

2.3 制作硬盘镜像文件

在 bochs 中,需要使用硬盘镜像文件虚拟物理硬盘。

给上层 test 目录创建一个大小为 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:表示拷贝的目标文件路径,若不存在则创建。
  • bs=512:块的大小,单位为B。
  • count=2048:表示拷贝的文件的块的数量。

硬盘镜像文件制作好后,将可执行文件 bootsect.bin 拷贝到硬盘镜像文件 c.img 的引导扇区的命令:dd if=bootsect.bin of=../c.img bs=512 seek=0 conv=notrunc

  • seek=0:表示把可执行文件 bootsect.bin 拷贝到硬盘镜像文件 c.img 的引导扇区。
  • conv=notrunc:表示不改变目标文件的大小,若没有该项,则硬盘镜像文件 c.img 的大小会由 1MB 变为可执行文件 bootsect.bin 的大小512B。

2.4 启动 bochs

“硬盘”制作好后,要想启动 bochs,还需要一个配置文件(bochsrc.bxrc),文件内容如下:

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:指定物理内存的大小,单位为MB,bochs 中只有 16MB 物理内存。
  • cpu:指定 cpu 个数,仅有1个cpu。
  • boot:指定启动方式,硬盘启动。

run.sh 保存了启动 bochs 的命令,文件内容如下:

bochs -q -f bochsrc.bxrc
  • -q:跳过 bochs 启动后的配置界面。
  • -f bochsrc.bxrc:指定配置文件的路径。

运行sh run.sh 命令后,就相当于我们按下了一台电脑的开机键。bochs 首先进行一些自身的初始化工作,最终在终端输出如下信息,并等待用户输入调试命令。

Next at t=0
(0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b          ; ea5be000f0
<bochs:1> 
  • 第1行:t 的值表示,cpu 从上电开始已经运行了多少条机器指令。

  • 第2行:cpu 上电后运行的第1条机器指令的信息(数字均为16进制)

    • [0x0000fffffff0]:该机器指令在物理内存中的物理地址,在实模式下,cpu 最多只能访问 1MB 物理内存,即该机器指令的物理地址为 0xffff0;
    • f000:fff0:当前寄存器[cs:ip]的值,该机器指令的逻辑地址。
    • jmpf 0xf000:e05b:该机器指令对应的汇编指令;
    • ea5be000f0:机器指令的内容。
  • 第3行:用户输入调试命令的命令行。

    • 查看 cpu 中寄存器的值:r 命令和 sreg 命令。

    •   <bochs:1> r
        eax: 0x00000000 0
        ecx: 0x00000000 0
        edx: 0x00000000 0
        ebx: 0x00000000 0
        esp: 0x00000000 0
        ebp: 0x00000000 0
        esi: 0x00000000 0
        edi: 0x00000000 0
        eip: 0x0000fff0
        eflags 0x00000002: id vip vif ac vm rf nt IOPL=0 of df if tf sf zf af pf cf
        
        <bochs:2> sreg
        es:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
        	Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
        cs:0xf000, dh=0xff0093ff, dl=0x0000ffff, valid=7
        	Data segment, base=0xffff0000, limit=0x0000ffff, Read/Write, Accessed
        ss:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
        	Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
        ds:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
        	Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
        fs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
        	Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
        gs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
        	Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
        ldtr:0x0000, dh=0x00008200, dl=0x0000ffff, valid=1
        tr:0x0000, dh=0x00008b00, dl=0x0000ffff, valid=1
        gdtr:base=0x00000000, limit=0xffff
        idtr:base=0x00000000, limit=0xffff
      
    • 打断点是重要的调试手段之一,bochs 提供了多个打断点的命令,其中 vb 命令使用逻辑地址打断点,b 命令使用物理地址打断点,使用 blist 可以查看所有断点信息。断点打好后,使用 c 命令运行程序,当 cpu 下一条即将运行的机器指令被打了断点时,cpu 停止在该机器指令前,等待输入新的命令。

    •   <bochs:3> vb 0x0:0x7c00
        <bochs:4> b 0x7c00
        <bochs:5> blist
        Num Type           Disp Enb Address
          1 vbreakpoint    keep y   0x0000:00007c00
          2 pbreakpoint    keep y   0x000000007c00
        <bochs:6> c
        00000004661i[BIOS  ] $Revision: 12579 $ $Date: 2014-12-26 11:31:39 +0100 (Fr, 26. Dez 2014) $
        00000318049i[KBD   ] reset-disable command received
        00000320818i[BIOS  ] Starting rombios32
        00000321256i[BIOS  ] Shutdown flag 0
        00000321840i[BIOS  ] ram_size=0x01000000
        00000322261i[BIOS  ] ram_end=16MB
        00000362771i[BIOS  ] Found 1 cpu(s)
        00000376975i[BIOS  ] bios_table_addr: 0x000fa498 end=0x000fcc00
        00000704770i[PCI   ] i440FX PMC write to PAM register 59 (TLB Flush)
        00001032699i[P2ISA ] PCI IRQ routing: PIRQA# set to 0x0b
        00001032718i[P2ISA ] PCI IRQ routing: PIRQB# set to 0x09
        00001032737i[P2ISA ] PCI IRQ routing: PIRQC# set to 0x0b
        00001032756i[P2ISA ] PCI IRQ routing: PIRQD# set to 0x09
        00001032766i[P2ISA ] write: ELCR2 = 0x0a
        00001033536i[BIOS  ] PIIX3/PIIX4 init: elcr=00 0a
        00001041217i[BIOS  ] PCI: bus=0 devfn=0x00: vendor_id=0x8086 device_id=0x1237 class=0x0600
        00001043496i[BIOS  ] PCI: bus=0 devfn=0x08: vendor_id=0x8086 device_id=0x7000 class=0x0601
        00001045614i[BIOS  ] PCI: bus=0 devfn=0x09: vendor_id=0x8086 device_id=0x7010 class=0x0101
        00001045839i[PIDE  ] new BM-DMA address: 0xc000
        00001046455i[BIOS  ] region 4: 0x0000c000
        00001048489i[BIOS  ] PCI: bus=0 devfn=0x0b: vendor_id=0x8086 device_id=0x7113 class=0x0680
        00001048721i[ACPI  ] new irq line = 11
        00001048733i[ACPI  ] new irq line = 9
        00001048758i[ACPI  ] new PM base address: 0xb000
        00001048772i[ACPI  ] new SM base address: 0xb100
        00001048800i[PCI   ] setting SMRAM control register to 0x4a
        00001212893i[CPU0  ] Enter to System Management Mode
        00001212904i[CPU0  ] RSM: Resuming from System Management Mode
        00001376925i[PCI   ] setting SMRAM control register to 0x0a
        00001391791i[BIOS  ] MP table addr=0x000fa570 MPC table addr=0x000fa4a0 size=0xc8
        00001393613i[BIOS  ] SMBIOS table addr=0x000fa580
        00001395781i[BIOS  ] ACPI tables: RSDP addr=0x000fa6a0 ACPI DATA addr=0x00ff0000 size=0xf72
        00001398971i[BIOS  ] Firmware waking vector 0xff00cc
        00001400766i[PCI   ] i440FX PMC write to PAM register 59 (TLB Flush)
        00001401489i[BIOS  ] bios_table_cur_addr: 0x000fa6c4
        00001529106i[VBIOS ] VGABios $Id: vgabios.c,v 1.76 2013/02/10 08:07:03 vruppert Exp $
        00001529177i[BXVGA ] VBE known Display Interface b0c0
        00001529209i[BXVGA ] VBE known Display Interface b0c5
        00001532134i[VBIOS ] VBE Bios $Id: vbe.c,v 1.65 2014/07/08 18:02:25 vruppert Exp $
        00001570128i[XGUI  ] charmap update. Font Height is 16
        00001856608i[XGUI  ] charmap update. Font Height is 16
        00001876307i[BIOS  ] ata0-0: PCHS=2/16/63 translation=none LCHS=2/16/63
        00005753481i[BIOS  ] IDE time out
        00017844201i[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 下一条即将运行的机器指令。

    •   <bochs:3> u /5
        00007c00: (                    ): jmpf 0x07c0:0005          ; ea0500c007
        00007c05: (                    ): mov ax, 0x0600            ; b80006
        00007c08: (                    ): mov ch, 0x00              ; b500
        00007c0a: (                    ): mov cl, 0x00              ; b100
        00007c0c: (                    ): mov dh, 0x18              ; b618
      
    • 使用 xp 命令可以查看物理内存中的指定物理地址的内容。查看物理内存 0x7dfe 开始的2个字节的内容。

    • /2bx:打印2个字节(参数b为1个字节,还有h、w)

      /13c:将13个字节作为 ASCII 码对应的字符打印出来

    •   <bochs:4> xp /2bx 0x7dfe
        [bochs]:
        0x00007dfe <bogus+       0>:	0x55	0xaa
      
    • 使用 n 命令让 cpu 运行一条机器指令后,停止并等待输入新的命令。

    •   <bochs:5> n
        Next 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 架构的 cpu 在上电后处于实模式,在实模式下,cpu 最多只能访问 1MB 物理内存,其中 BIOS 占用的物理段的物理地址空间为:0xc0000~0xfffff。cpu 上电后,运行的第1条机器指令的物理地址为 0xffff0,该物理地址正好位于 BIOS 占用的物理段内,因此 cpu 上电后首先运行 BIOS。

BIOS 主要工作:

  1. 检测、初始化系统中的硬件。如显示器、硬盘等。
  2. 创建中断系统。在 1MB 物理内存的前 1KB 物理地址空间内初始化中断向量表,在最后 256KB 物理地址空间内保存中断处理程序。
  3. 将引导扇区中的可执行文件 bootsect.bin 拷贝到物理地址空间为 0x7c00~0x7dff 的物理内存中的物理段中。
  4. 将寄存器 cs 和 ip 分别赋值为 0x0和 0x7c00,因此 cpu 下一条运行的机器指令的物理地址为 0x7c00,即可执行文件 bootsect.bin 的第1条机器指令。

BIOS 运行完毕后,内存状态为:

Linux内核——实模式_第3张图片

图2.5.1 1MB 物理内存空间

3. 汇编程序详解

代码清单(test1/bootsect.s)
    .code16
BOOTSEG = 0x7c0
    ljmp  $BOOTSEG, $go
go:
    movw  %cs, %ax
    movw  %ax, %ds
    movw  %ax, %es
    movw  $msg, %bp
    movb  $0x13, %ah
    movb  $0x01, %al
    movb  $2, %bl
    movw  msg_len, %cx
    movb  $0, %dh
    movb  $0, %dl
    int   $0x10
    jmp   .
msg:
    .ascii "hello, world."
msg_len:
    .word . - msg
    .org  0x1fe
    .word 0xaa55

3.1 伪指令

在汇编程序中,以.开头的汇编语句叫作伪指令,汇编器 as 不会把伪指令汇编成机器指令。

  • .code16:在实模式下,cpu 只能运行16位的机器指令,该伪指令的作用是:告诉汇编器,把汇编程序 bootsect.s 中的汇编、链接为16位的机器指令。
  • .ascii "hello, world.":在可执行文件 bootsect.bin 中的偏移地址空间 0x20~0x2c 中定义字符串“hello,world.”。
  • .word . - msg:在可执行文件 bootsect.bin 中的偏移地址空间 0x2d~0x2e 中定义一个2字节大小的数据(x86架构中使用小端模式,低字节存放在物理内存的低地址字节)。
  • .org 0x1fe:将第22行伪指令对应的数据在可执行文件 bootsect.bin 中的偏移地址设置为 0x1fe,若不是用该伪指令,则第22行伪指令对应的数据的偏移地址为 0x2f,并将可执行文件 bootsect.bin 中偏移地址 0x2f~0x1fd 中的内容用0填充。

3.2 符号

在汇编程序中,使用符号表示一个数,有两种方式:

  • 使用=表示。如代码清单第2行所示,符号 BOOTSEG 值为 0x7c0。
  • 使用:表示。则符号表示=后面的汇编语句对应的机器指令或数据在可执行文件中的偏移地址。符号 go 的值为 0x5,符号 msg 的值为 0x20。

在将汇编程序 bootsect.s 汇编为可执行文件 bootsect.o 的过程中,汇编器 as 首先把汇编程序 bootsect.s 中所有的符号用其表示的数替换。

3.3 设置代码段

机器指令的逻辑地址和物理地址是多对一的关系,即一个物理地址可以由多个逻辑地址计算得到。

BIOS 把bootsect.bin 从引导扇区加载到物理内存的物理地址空间 0x7c00~0x7dff 后,将寄存器 [cs:ip] 赋值为可执行文件 bootsect.bin 的第一条机器指令的逻辑地址 [0x0:0x7c00],代码段和数据段的物理起始地址为 0。当 cpu 运行 可执行文件 bootsect.bin 时,代码段的物理起始地址为 0x7c00,故可执行文件 bootsect.bin 的第一条机器指令需要对寄存器 [cs:ip] 重新赋值。

ljmp 汇编指令的作用是为寄存器 [cs:ip] 赋值,在汇编指令中寄存器前加 % 。

3.4 设置数据段

通常代码段设置完成后,必须设置数据段。在实模式下,cpu 中除了用于保存代码段的物理段起始地址右移4位的寄存器 cs 外,还有用于保存数据段的物理段起始地址右移 4 位的寄存器——数据段寄存器 ds 和数据段寄存器 es。

cpu 运行可执行文件 bootsect.bin 时,代码段和数据段的物理段起始地址都为 0x7c00,所以只需要将寄存器 cs 的值赋给寄存器 ds 和寄存器 es 即可,如代码清单5~7行所示,不能将一个段寄存器的值直接赋给另外一个段寄存器

每条 mov 汇编指令都有一个后缀:

  • b:赋值给寄存器的数据大小为1个字节;
  • w:赋值给寄存器的数据大小为2个字节;
  • l:赋值给寄存器的数据大小为4个字节;

3.5 中断处理程序

BIOS 在创建中断系统时,在 1MB 物理内存的最后 256KB 物理地址空间内保存了大量的中断处理程序。这些中断处理程序用于访问系统中已有的硬件,访问不同的硬件需要调用不同的中断处理程序,每个中断处理程序都有唯一的编号——中断号。

常用中断号举例:

  • 0x10:往显示器的屏幕上打印字符。
  • 0x13:从硬盘读取数据。

调用中断处理程序前,需要借助 cpu 中的寄存器传递参数。例如:调用 0x10 中断处理程序往显示器的屏幕上打印字符前,需要给 0x10 号中断处理程序传递参数,告诉它在屏幕的什么位置显示什么内容,内容的长度以及属性。

  • 寄存器 ah:0x13 表示向屏幕打印字符串。

  • 寄存器[es:bp]:保存字符串的首字符在数据段中的逻辑地址。

  • 寄存器 cs:保存字符串的长度。

  • 寄存器 (dh, dl):字符串在屏幕上的起始坐标,寄存器 dh 为行号(0~24),寄存器 dl 为列号(0~79)。

  • 寄存器 al:指定光标和字符的属性。

    • 0:表示字符的属性保存在寄存器 bl 中,光标停留在字符串的首字符
    • 1:表示字符的属性保存在寄存器 bl 中,光标停留在字符串的尾字符
    • 2:表示字符的属性紧跟在字符之后,光标停留在字符串的首字符
    • 3:表示字符的属性紧跟在字符之后,光标停留在字符串的尾字符
  • 寄存器 bl:若寄存器 al 的值为 0 或者 1,保存字符的属性值。

    • 字符属性:在这里插入图片描述

    • 字符和背景颜色对照表:

    二进制 颜色 二进制 颜色
    0000 黑色 1000 灰色
    0001 蓝色 1001 淡蓝色
    0010 绿色 1010 淡绿色
    0011 青色 1011 淡青色
    0100 红色 1100 淡红色
    0101 紫红色 1101 淡紫红色
    0110 棕色 1110 黄色
    0111 银色 1111 白色

使用 mov 指令赋值:

  1. 数据在寄存器中。如代码第7行所示。
  2. 数据在机器指令中。如代码第8行所示。将 $ 后面的数字 0x20 (符号 msg 的值)赋值给寄存器 bp。
  3. 数据在内存中。如果符号前没有 $ ,则说明用来赋值的数据保存在物理内存中,而非偏移地址(符号的值)。类似于 cpu 将寄存器 [cs:ip] 中的机器指令的逻辑地址转换为物理地址后,使用物理地址从物理内存中读取机器指令,cpu 也将数据的逻辑地址转换为物理地址后,通过物理地址从物理内存中读取数据,不同之处是:1. 机器指令的逻辑地址中的段起始地址右移4位的值保存在代码段寄存器 cs,而数据的逻辑地址中的段起始地址右移4位的值默认保存在数据段寄存器 ds 中;2. 机器指令的逻辑地址中的偏移地址保存在寄存器 ip 中,而数据的逻辑地址中的偏移地址,通常由机器指令提供。如代码12行所示,将字符串“hello, world.”的长度13赋值给寄存器cx。

3.6 调用中断处理程序

设置好为 0x10 号中断处理程序传递参数的寄存器后,在汇编程序 bootsect.s 使用 int 汇编指令调用 0x10 号中断处理程序,cpu 执行第15行汇编指令对应的机器指令的过程如下:

  1. 将当前寄存器 [cs:ip] 中的逻辑地址(第16行汇编指令)保存起来。

  2. 将 0x10 号中断处理程序的入口逻辑地址加载到寄存器[cs:ip]之中。每个中断处理程序的入口逻辑地址都保存在中断向量表中。中断处理程序的入口逻辑地址按照中断号依次存放在中断向量表中,可以通过 0x10 号中断处理程序的中断号 0x10 从中断向量表中获取 0x10 中断处理程序的入口逻辑地址[0xc000:0x152],将入口逻辑地址赋值给寄存器[cs:ip]后,cpu下一条运行的是 0x10 号中断处理程序的第1条指令,物理地址为0xc0152。

Linux内核——实模式_第4张图片

图 3.6.1 0x10 号中断处理程序调用过程
3. 运行 0x10 号中断处理程序。
  1. 将 cpu 保存的第16行汇编指令对应的机器指令的逻辑地址,重新赋值给 [cs:ip],因此 cpu 重新进入 bootsect.bin 中运行。

3.7 使 cpu 进入死循环

因为 cpu 会不停地根据寄存器 [cs:ip] 中的逻辑地址转换后的物理地址,从物理内存中读取机器指令,然后对其解析、执行,因此需要一条让 cpu 进入死循环的机器指令作为可执行文件的最后一条机器指令,如代码第16行所示,与 ljmp 不同的是,jmp 只给寄存器 ip 赋值。cpu 会一直循环运行第 16 行指令,不会将第1个虚线方框中的部分数据看做机器指令,进行取指、解析、执行。

3.8 引导扇区标志

BIOS 会将引导扇区中的可执行文件拷贝到物理内存,但是在拷贝前,会检查引导扇区中的可执行文件 bootsect.bin 的最后两个字节的值是不是0x55和0xaa,若不是,则 BIOS 不会拷贝。

你可能感兴趣的:(Linux,内核学习笔记,linux,实模式,操作系统)