(第4章 2)突破512字节的限制

 

一、代码

     启动的过程中,引导扇区boot sector(boot.asm,07c00h开始,最多512bytes)负责把加载器Loader(loader.asm,长度不受限制)载入内存并且把控制权交给她。Loader再加载操作系统内核之前,还要作准备保护模式等一系列工作,就很可能超过512bytes了。

 

     loader.asm代码在这里只是虚晃一枪,其具体功能编写放在“第五章 内核雏形”,不再赘述。

 

     boot.asm中灰色部分为代码“主线”,基本思路为:

(1)LABEL_SEARCH_IN_ROOT_DIR_BEGIN~~~LABEL_FILENAME_FOUND:在FAT12文件系统的“根目录区”中找到"loader.bin"对应的条目。

   执行后[es:di]指向该条目内部的一个字节,而非条目的开始,根据P105“根目录分区的条目格式”可知,[es:di]指向条目中的位置如下图所示:

                                    [DIR_NAME] [DIR_ATTR] [保         留]  [DIR_WrtTime] [DIR_WrtDate] [DIR_FstClus] [DIR_FileSize]

长度(单位:byte):         8+3                 1                10                        2                          2                           2                      4                  

                                 LOADER_ _BIN      *               **********             **                         **                        **                     ****

                                                                    |

                                                                   di: 是字符'N'后面的那个字节

 

(2)LABEL_FILENAME_FOUND子程序:后面ReadSector中会用到int 13中断读磁盘,故在此准备3个寄存器内容——

ax <-- (全局)扇区号

es <--  BaseOfLoader (09000h)

bx <--  OffsetOfLoader (0100h)

   在ReadSector中,会将(全局)扇区号ax 转换成 “柱面号ch”,“磁头号(即盘面号)dh”,“起始扇区号cl”,从而定位到磁盘上的一个扇区。另外,在ReadSector中另行指定“cl为待读扇区数”。int 13h的2号功能(ah=2)会将磁盘上的这若干个扇区读到[es:bx]开始的内存中。

 

(3)LABEL_GOON_LOADING_FILE~~~jmp BaseOfLoader:OffsetOfLoader之前:注意到根据“根目录区条目”只能找到“loader.bin”在磁盘上的“第一个扇区”,因此本部分在FAT表中顺藤摸瓜地找到loader.bin在磁盘上的“后续所有扇区号(这里也是簇号)”,并通过call ReadSector载入内存。

 

(4)jmp BaseOfLoader:OffsetOfLoader这条指令:(前面已经将磁盘中若干个扇区上的loader.bin文件载入BaseOfLoader:OffsetOfLoader开始的连续内存中了,这里)跳转到loader.bin,将控制权交给loader.bin。在下一章中将分析loader完成的功能——加载OS内核+跳入保护模式。

 

loader.asm

org	0100h

	mov	ax, 0B800h
	mov	gs, ax
	mov	ah, 0Fh				; 0000: 黑底    1111: 白字
	mov	al, 'L'
	mov	[gs:((80 * 0 + 39) * 2)], ax	; 屏幕第 0 行, 第 39 列。

	jmp	$		; Start

   

 

boot.asm

 

;%define_BOOT_DEBUG_; 做 Boot Sector 时一定将此行注释掉!将此行打开后用 nasm Boot.asm -o Boot.com 做成一个.COM文件易于调试

 

%ifdef_BOOT_DEBUG_

org  0100h; 调试状态, 做成 .COM 文件, 可调试

%else

org  07c00h; Boot 状态, Bios 将把 Boot Sector 加载到 0:7C00 处并开始执行

%endif

 

;================================================================================================

%ifdef_BOOT_DEBUG_

BaseOfStackequ0100h; 调试状态下堆栈基地址(栈底, 从这个位置向低地址生长)

%else

BaseOfStackequ07c00h; Boot状态下堆栈基地址(栈底, 从这个位置向低地址生长)

%endif

 

BaseOfLoaderequ09000h; LOADER.BIN 被加载到的位置 ----  段地址

OffsetOfLoaderequ0100h; LOADER.BIN 被加载到的位置 ---- 偏移地址

 

RootDirSectorsequ14; 根目录占用空间

SectorNoOfRootDirectoryequ19; Root Directory 的第一个扇区号

SectorNoOfFAT1equ1; FAT1 的第一个扇区号 = BPB_RsvdSecCnt

DeltaSectorNoequ17; DeltaSectorNo = BPB_RsvdSecCnt + (BPB_NumFATs * FATSz) - 2

; 文件的开始Sector号 = DirEntry中的开始Sector号 + 根目录占用Sector数目 + DeltaSectorNo

;================================================================================================

 

jmp short LABEL_START; Start to boot.

nop; 这个 nop 不可少 ???1为何

 

; 下面是 FAT12 磁盘的头

BS_OEMNameDB 'ForrestY'; OEM String, 必须 8 个字节

BPB_BytsPerSecDW 512; 每扇区字节数

BPB_SecPerClusDB 1; 每簇多少扇区

BPB_RsvdSecCntDW 1; Boot 记录占用多少扇区

BPB_NumFATsDB 2; 共有多少 FAT 表

BPB_RootEntCntDW 224; 根目录文件数最大值(  14扇区 * 512字节/扇区 / 32字节/根目录区条目  )

BPB_TotSec16DW 2880; 逻辑扇区总数 (2880扇区 * 512字节/扇区 = 1.4M 字节 = 一个软盘的容量)

BPB_Media  DB 0xF0; 媒体描述符

BPB_FATSz16  DW 9; 每FAT扇区数

BPB_SecPerTrk  DW 18; 每磁道扇区数

BPB_NumHeads  DW 2; 磁头数(面数)

BPB_HiddSec  DD 0; 隐藏扇区数

BPB_TotSec32  DD 0; 如果 wTotalSectorCount 是 0 由这个值记录扇区数

BS_DrvNum  DB 0; 中断 13 的驱动器号

BS_Reserved1  DB 0; 未使用

BS_BootSig  DB 29h; 扩展引导标记 (29h)

BS_VolID  DD 0; 卷序列号

BS_VolLab  DB 'OrangeS0.02'; 卷标, 必须 11 个字节

BS_FileSysType  DB 'FAT12   '; 文件系统类型, 必须 8个字节  

 

LABEL_START:

mov  ax, cs

mov  ds, ax

mov  es, ax

mov  ss, ax

mov  sp, BaseOfStack

 

; 清屏

mov  ax, 0600h

mov  bx, 0700h

mov  cx, 0

mov  dx, 0184fh

int  10h

 

; 上面清屏用到了BIOS中断  int 10h

; int 10h是BIOS对屏幕及显示器提供的服务程序,
; 功能号AH=6:屏幕初始化或上卷,
; 参数AL=0:屏幕为空白,
; 背景和前景颜色BH = Background Color and Foreground color.
; 矩形区域:左上角(行ch,列cl)=(0,0), 右下角(行dh,列dl)=(0x18,0x4f)=(24, 79)

 

mov  dh, 0          ; "Booting  "

call  DispStr        ; 显示字符串

 

        ;  int 13h is a shorthand for BIOS interrupt call 13h, the 20th interrupt vector in an x86-based computer system. The BIOS typically sets up a real mode interrupt handler at this vector that provides sector-based hard disk and floppy disk read and write services using cylinder-head-sector(CHS) addressing.

        ; Drive Table:

        ;                           DL=00h,  1st floppy disk ("drive A:")

        ;                           DL=01h,  2st floppy disk ("drive B:")

        ;                           DL=80h,  1st hard disk 

        ;                           DL=80h,  2st hard disk

        ; Function Table:

        ;                           AH=00h, Reset Disk Drives

        ;                           AH=01h, Get Status of Last Drive Operation

        ;                           AH=02h, Read Sectors From Drive

        ;                           AH=03h, Write Sectors To Drive

        ; Parameters ...

        ; An interrupt handler, also known as an interrupt service routine (ISR), is a callback subroutine in microcontroller firmware, operating system or device driver whose execution is triggered by the reception of an interrupt.

xor  ah, ah; ┓

xor  dl, dl; ┣ 软驱复位

int  13h; ┛

 

; 下面在 A 盘的根目录寻找 LOADER.BIN

mov  word [wSectorNo], SectorNoOfRootDirectory;根目录分区的第一个扇区号SectorNoOfRootDirectory=19

LABEL_SEARCH_IN_ROOT_DIR_BEGIN:

cmp  word [wRootDirSizeForLoop], 0        ; ┓;根目录分区占用的扇区数wRootDirSizeForLoop

jz  LABEL_NO_LOADERBIN                      ; ┣ 判断根目录区是不是已经读完

dec  word [wRootDirSizeForLoop]             ; ┛ 如果读完表示没有找到 LOADER.BIN

 

 

        ; 从第 ax 个 Sector 开始, 将 cl 个 Sector 读入 es:bx 中

        ; call ReadSector后,[BaseOfLoader:OffsetOfLoader] (即[es:bx]),开始存放根目录分区的一个扇区的数据

mov  ax, BaseOfLoader

mov  es, ax                        ; es <- BaseOfLoader

mov  bx, OffsetOfLoader   ; bx <- OffsetOfLoader于是, es:bx = BaseOfLoader:OffsetOfLoader

 

mov  ax, [wSectorNo]         ; ax <- Root Directory 中的某 Sector 号

mov  cl, 1

call   ReadSector

 

; 比较[ds:si] (即"LOADER BIN")和 [es:di](即根目录区中每个条目中 文件的名称)

mov  si, LoaderFileName; ds:si -> "LOADER  BIN"

mov  di, OffsetOfLoader; es:di -> BaseOfLoader:0100 = BaseOfLoader*10h+100

 

cld

mov  dx, 10h    ; 一个扇区大小/根目录区一个条目大小=512byte/32byte=10h 个 ==> 一个扇区有多少条目

 

LABEL_SEARCH_FOR_LOADERBIN:

cmp  dx, 0                                                                    ; ┓循环次数控制,

jz  LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR    ; ┣如果已经读完了一个 Sector,

dec  dx                                                                         ; ┛就跳到下一个 Sector

 

; 下面(粗体斜体部分)实际上是在检查“一个扇区”中的“一个根目录区条目”中是否有loader.bin这个名称出现

; ∴在这里有两个循环,外层循环挨个读取根目录区的每个扇区,内层循环在指定扇区中挨个读取每个条目。

mov  cx, 11

LABEL_CMP_FILENAME:

cmp  cx, 0

jz  LABEL_FILENAME_FOUND; 如果比较了 11 个字符都相等, 表示找到

dec  cx

lodsb                       ; ds:si -> al

cmp  al, byte [es:di]

jz  LABEL_GO_ON

jmp  LABEL_DIFFERENT; 只要发现不一样的字符就表明本 DirectoryEntry 不是

; 我们要找的 LOADER.BIN

LABEL_GO_ON:

inc  di

jmp  LABEL_CMP_FILENAME;继续循环

 

LABEL_DIFFERENT:             ; 一个根目录区中的条目长20h (bytes),详见P105

and  di, 0FFE0h                                                                   ; else ┓di &= E0 为了让它指向本条目开头

add  di, 20h                                                                          ;        ┃

mov  si, LoaderFileName                                                      ;        ┣ di += 20h  下一个目录条目

jmp  LABEL_SEARCH_FOR_LOADERBIN                            ;        ┛

 

LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:

addword [wSectorNo], 1

jmpLABEL_SEARCH_IN_ROOT_DIR_BEGIN

 

LABEL_NO_LOADERBIN:

mov  dh, 2                                       ; "No LOADER."

call  DispStr                                     ; 显示字符串

 

; 由于上面有这样一句:“jz  LABEL_FILENAME_FOUND”,所以程序不会执行如下的预编译,而直接跳到LABEL_FILENAME_FOUND去 !

%ifdef  _BOOT_DEBUG_

mov  ax, 4c00h                                      ; ┓

int     21h                                               ; ┛没有找到 LOADER.BIN, 回到 DOS

%else

jmp  $                                                     ; 没有找到 LOADER.BIN, 死循环在这里

%endif

 

在找到文件的这一刻[es : di] 指向根目录区中相应条目的DIR_NAME中'N'字母后的那个字符,注意到一个“根目录区的条目”的结构如下:(总共长度为0x20h 即32个字节)

;  名称                             偏移

;  DIR_NAME                    0x0

;  DIR_ATTR                    0xB

;  保留位                           0xC

;  DIR_WrtTime                0x16

;  DIR_WrtDate                0x18

;  DIR_FstClus                  0x1A

;  DIR_FileSize                  0x1C

LABEL_FILENAME_FOUND:                                 ; 找到 LOADER.BIN 后便来到这里继续

mov  ax, RootDirSectors

 

and  di, 0FFE0h                                                    ; di -> 当前条目的开始

add  di, 01Ah                                                         ; di -> 首 Sector

; 此时 [es:di] 指向根目录区条目中LOADER.BIN对应的那个条目的开头(一个条目32字节长度)

 

mov  cx, word [es:di]

push  cx                                                                 ; 保存此 Sector 在 FAT 中的序号

add  cx, ax

add  cx, DeltaSectorNo                                          ; cl <- LOADER.BIN的起始扇区号(0-based)

; 此时的cx = word [es:di] + RootDirSectors + DeltaSectorNo

; word [es:di] ==> LOADER.BIN这个文件在FAT12格式数据区中对应的簇号/扇区号

; RootDirSectors ==> 14, 即根目录区占据的扇区数

; DeltaSectorNo  ==>  19-2, 其中19是根目录区之前的扇区数

 

mov  ax, BaseOfLoader

mov  es, ax                                                             ; es <- BaseOfLoader

mov  bx, OffsetOfLoader                                        ; bx <- OffsetOfLoader

mov  ax, cx                                                             ; ax <- Sector 号

 

LABEL_GOON_LOADING_FILE:

push  ax                                       ; `.

push  bx                                       ;  |

mov  ah, 0Eh                                       ;  | 每读一个扇区就在 "Booting  " 后面

mov  al, '.'                                            ;  | 打一个点, 形成这样的效果:

mov  bl, 0Fh                                        ;  | Booting ......

int  10h;  |

pop  bx                                       ;  |

pop  ax                                       ; /

 

mov  cl, 1

call  ReadSector     ; 从第 ax(相对整个磁盘起始的扇区号) 个 Sector 开始, 将 cl 个 Sector 读入 es:bx 中

 

;  此时,ax为当前在FAT中的扇区号/簇号

pop  ax                    ; 取出此 Sector 在 FAT 中的序号

call  GetFATEntry

cmp  ax, 0FFFh

;  此时,ax为下一个要读扇区在FAT中的扇区号/簇号

 

; 下面需要将“FAT中的扇区号/簇号“ax 转换为“在整个磁盘中的扇区号”ax

jz  LABEL_FILE_LOADED

push  ax; 保存 Sector 在 FAT 中的序号

mov  dx, RootDirSectors

add  ax, dx

add  ax, DeltaSectorNo

add  bx, [BPB_BytsPerSec]

jmp  LABEL_GOON_LOADING_FILE

LABEL_FILE_LOADED:

 

mov  dh, 1; "Ready."

call  DispStr; 显示字符串

 

; *****************************************************************************************************

; 这一句正式跳转到已加载到内存中的 LOADER.BIN 的开始处,

; 开始执行 LOADER.BIN 的代码。

; Boot Sector 的使命到此结束

jmpBaseOfLoader:OffsetOfLoader

; *****************************************************************************************************

 

 

 

;============================================================================

;变量

;----------------------------------------------------------------------------

wRootDirSizeForLoopdwRootDirSectors; Root Directory 占用的扇区数, 在循环中会递减至零.

wSectorNodw0; 要读取的扇区号

bOdddb0; 奇数还是偶数

 

;============================================================================

;字符串

;----------------------------------------------------------------------------

LoaderFileNamedb"LOADER  BIN", 0; LOADER.BIN 之文件名

; 为简化代码, 下面每个字符串的长度均为 MessageLength

MessageLengthequ9

BootMessage:db"Booting  "; 9字节, 不够则用空格补齐. 序号 0

Message1db"Ready.   "; 9字节, 不够则用空格补齐. 序号 1

Message2db"No LOADER"; 9字节, 不够则用空格补齐. 序号 2

;============================================================================

 

 

;----------------------------------------------------------------------------

; 函数名: DispStr

;----------------------------------------------------------------------------

; 作用:

;显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)

DispStr:

movax, MessageLength

muldh

addax, BootMessage

movbp, ax; ┓

movax, ds; ┣ ES:BP = 串地址

moves, ax; ┛

movcx, MessageLength; CX = 串长度

movax, 01301h; AH = 13,  AL = 01h

movbx, 0007h; 页号为0(BH = 0) 黑底白字(BL = 07h)

movdl, 0

int10h; int 10h

ret

 

 

;----------------------------------------------------------------------------

; 函数名: ReadSector

;----------------------------------------------------------------------------

; 作用:

;从第 ax 个 Sector 开始, 将 cl 个 Sector 读入 es:bx 中

ReadSector:

; -----------------------------------------------------------------------

; 怎样由扇区号求扇区在磁盘中的位置 (扇区号 -> 柱面号, 起始扇区, 磁头号)

; -----------------------------------------------------------------------

; 设扇区号为 x

;                           ┌ 柱面号 = y >> 1

;       x           ┌ 商 y ┤

; -------------- => ┤      └ 磁头号 = y & 1

;  每磁道扇区数     │

;                   └ 余 z => 起始扇区号 = z + 1

pushbp

movbp, sp

subesp, 2; 辟出两个字节的堆栈区域保存要读的扇区数: byte [bp-2]

 

movbyte [bp-2], cl

pushbx; 保存 bx

movbl, [BPB_SecPerTrk]; bl: 除数

divbl; y 在 al 中, z 在 ah 中

incah; z ++

movcl, ah; cl <- 起始扇区号

movdh, al; dh <- y

shral, 1; y >> 1 (其实是 y/BPB_NumHeads, 这里BPB_NumHeads=2)

movch, al; ch <- 柱面号

anddh, 1; dh & 1 = 磁头号

popbx; 恢复 bx

; 至此, "柱面号, 起始扇区, 磁头号" 全部得到 ^^^^^^^^^^^^^^^^^^^^^^^^

movdl, [BS_DrvNum]; 驱动器号 (0 表示 A 盘)

.GoOnReading:

movah, 2; 读

moval, byte [bp-2]; 读 al 个扇区

int13h

jc.GoOnReading; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到正确为止

 

addesp, 2

popbp

 

ret

 

;----------------------------------------------------------------------------

; 函数名: GetFATEntry

;----------------------------------------------------------------------------

; 作用:

;找到序号为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax 中

;需要注意的是, 中间需要读 FAT 的扇区到 es:bx 处, 所以函数一开始保存了 es 和 bx

GetFATEntry:

pushes

pushbx

pushax

movax, BaseOfLoader; `.

subax, 0100h;  | 在 BaseOfLoader 后面留出 4K 空间用于存放 FAT

moves, ax; /

popax

movbyte [bOdd], 0

movbx, 3

mulbx; dx:ax = ax * 3

movbx, 2

divbx; dx:ax / 2  ==>  ax <- 商, dx <- 余数

cmpdx, 0

jzLABEL_EVEN

movbyte [bOdd], 1

LABEL_EVEN:;偶数

; 现在 ax 中是 FATEntry 在 FAT 中的偏移量,下面来

; 计算 FATEntry 在哪个扇区中(FAT占用不止一个扇区)

xordx, dx

movbx, [BPB_BytsPerSec]

divbx ; dx:ax / BPB_BytsPerSec

  ;  ax <- 商 (FATEntry 所在的扇区相对于 FAT 的扇区号)

  ;  dx <- 余数 (FATEntry 在扇区内的偏移)

pushdx

movbx, 0 ; bx <- 0 于是, es:bx = (BaseOfLoader - 100):00

addax, SectorNoOfFAT1 ; 此句之后的 ax 就是 FATEntry 所在的扇区号

movcl, 2

callReadSector ; 读取 FATEntry 所在的扇区, 一次读两个, 避免在边界

  ; 发生错误, 因为一个 FATEntry 可能跨越两个扇区

popdx

addbx, dx

movax, [es:bx]

cmpbyte [bOdd], 1

jnzLABEL_EVEN_2

shrax, 4

LABEL_EVEN_2:

andax, 0FFFh

 

LABEL_GET_FAT_ENRY_OK:

 

popbx

popes

ret

;----------------------------------------------------------------------------

 

times 510-($-$$)db0; 填充剩下的空间,使生成的二进制代码恰好为512字节

dw 0xaa55; 结束标志

 

 

 

 

二、运行方法

方法一:Linux上装bochs虚拟机,然后在bochs虚拟机中运行(由于有时候bochs很难在Linux上安装成功,因此推荐用方法二)

1. 编译boot.asm和loader.asm

[hadoop@sam1 c]$ pwd

/home/hadoop/Desktop/OSImpl/一个操作系统的实现/chapter4/c

[hadoop@sam1 c]$ ls

a.img  bochsrc  boot.asm  loader.asm  Makefile

[hadoop@sam1 c]$ nasm boot.asm -o boot.bin

[hadoop@sam1 c]$ nasm loader.asm -o loader.bin

 

2. 创建a.img软盘镜像,并将boot.asm写入软盘第一个扇区作为“引导扇区”:

[hadoop@sam1 bochs-2.6]$ bximage

...

Do you want to create a floppy disk image or a hard disk image?

Please type hd or fd. [hd] fd

...

Please type 0.16, 0.18, 0.32, 0.36, 0.72, 1.2, 1.44, 1.68, 1.72, or 2.88.

 [1.44]

...

What should I name the image?

[a.img] 

...

[hadoop@sam1 bochs-2.6]$ dd if=/home/hadoop/Desktop/OSImpl/一个操作系统的实现/chapter4/c/boot.bin of=/home/hadoop/Desktop/OSImpl/bochs-2.6/a.img bs=512 count=1 conv=notrunc

 

 

注:必须要设置从软盘a.img启动,因此修改$bochs_home/.bochsrc为如下:

megs:32

romimage:file=/usr/share/bochs/BIOS-bochs-latest

vgaromimage:file=/usr/share/vgabios/VGABIOS-lgpl-latest.bin

 

#floppya: 1_44=freedos.img, status=inserted

#floppyb: 1_44=pm.img,status=inserted

floppya: 1_44=a.img, status=inserted

 

boot: a

 

#disable the mouse

 

#enable key mapping, using US layout as default.

keyboard_mapping: enabled=1, map=gui/keymaps/x11-pc-us.map

 

mouse: enabled=0

 

log:bochsout.txt

 

3. 把loader.bin作为一个普通的文件写入软盘a.img(当然也可以是其他盘)

[hadoop@sam1 bochs-2.6]$ sudo mount -o loop /home/hadoop/Desktop/OSImpl/bochs-2.6/a.img /mnt/floppy/

[sudo] password for hadoop: 

[hadoop@sam1 bochs-2.6]$ sudo cp /home/hadoop/Desktop/OSImpl/一个操作系统的实现/chapter4/c/loader.bin /mnt/floppy -v

`/home/hadoop/Desktop/OSImpl/一个操作系统的实现/chapter4/c/loader.bin' -> `/mnt/floppy/loader.bin'

[hadoop@sam1 bochs-2.6]$ sudo umount /mnt/floppy/

 

注:mount -o loop —— 

        -o: option

        loop: loop device. loop device is a pseudo('fake') device (actually just a file) that acts as a block-based device.

        (1)loop device 区别于loopback device. Sometimes, the loop device if erroneously referred to as 'loopback' device, but this term is reserved for a networking device in the Linux kernel (cf. loopback). The concept of the 'loop' device is distinct from 'loopback', although similar in name.

        (2)为什么命名为loop呢:在使用之前,一个loop设备必须要和一个文件进行连接,这种结合方式给用户提供了一个代替块特殊文件的接口。如果一个文件包含一个完整的文件系统,那么这个文件就可以像一个磁盘设备一样被mount起来。

             对于第一层文件系统,它直接安装(环绕)在我们的计算机物理设备上;而对于这种被mount起来的镜像文件(它也包含有文件系统),则建立在第一层文件系统上,就好像是在第一层文件系统上再环绕一层文件系统似的,所以称为loop.

 

 

4. 开始模拟

[hadoop@sam1 bochs-2.6]$ ./bochs 

 

结果截图:

 

 

方法二:直接在VMware添加一块硬盘,将boot.bin和loader.bin写到这块盘中,然后从这块盘启动

1. 在VMWare中添加一块硬盘后,给他分区、格式化为FAT12

    ***添加过程如图(注意:新添加的硬盘大小最大为32MB(FAT12格式限制)





 
我在添加新硬盘时,虚拟机是启动着的,需要
#reboot

#fdisk -l                             看到新硬盘没有分区的提示:Disk /dev/sdb doesn't contain a valid partition table
#mkfs.vfat -I -F 12 /dev/sdb      不分区(-I)直接把整块硬盘格式化为FAT12格式

2. 将boot.bin和loader.bin写到这块盘中
#cd /home/hadoop/osimpl/OSImplCode/chapter4/c/
#dd if=boot.bin of=/dev/sdb bs=512 count=1        

#cp loader.bin /dev/sdb

3. 然后从从新添加的硬盘/dev/sdb启动
#reboot

//重启时马上按F2,进入BIOS设置界面

 

效果图:

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值