查看: 7209|回复: 13

[教 程] GRUB相关知识

[复制链接]
发表于 2012-10-18 20:23:31 | 显示全部楼层 |阅读模式
转自不点

首先,Grub4DOS 是什么?这个要给出概括。试试看......

Grub4DOS 是服务于 PC BIOS 平台的一个开源的引导管理器,是基于 GNU GRUB legacy 开发出来的,以 GPL2 授权协议发布。

这个引导管理器有什么特点?也要给以概括。试试看......

它可以用来启动 DOS、Windows、Linux 等多种操作系统,也可以用来启动其他引导管理器。它自己可以经由 MBR、PBR、CDROM、PXE 启动,也可以从 DOS、Win9x、Linux 等操作系统之下启动,或者被其他引导管理器如 fbinst、NTLDR、BOOTMGR、SYSLINUX、GNU GRUB、LILO 启动。它具有磁盘、光盘仿真功能(基于实模式 BIOS),可以用来启动软盘、硬盘、光盘映像。

不重要的(或者过时的)功能 “ 启动 CDROM ”(cdrom --init),此处可以不用提及,而可以在后续的文档内容中简单涉及。

另外,也不用提及 USB,因为虽然 USB 是一种新的介质,但却不是一种新的启动方式。比如说,我们只有仿真软盘、仿真硬盘、仿真光盘的说法,却不存在仿真 USB 的说法。在 PC BIOS 平台下,USB 的启动方式就是软盘、硬盘、光盘的启动方式(之一)。将来的新型介质,例如 TF 卡、CF 卡、1394 口,等等,都是类似的。由于 DOS 和实模式是一个很好的测试平台,有着大量的、丰富的软件资源,所以,新型介质的硬件开发者大概都会采用 DOS 和 BIOS 来进行启动测试。尽管某些商家希望通过强制性淘汰 BIOS 制造不兼容来获取利益,但制造不兼容的结果,首先使得硬件制造商自己受到损失,因为他们自己被迫丢弃熟悉的 BIOS 知识结构,被迫采用陌生的开发环境,面临着匮乏的可用软件资源。就算硬件制造商内部工程师大换血全面采用 EFI,它也面对着各个阶层 PC 用户的承受能力问题。它可能遭到一些阶层的用户的(不由自主的)抵制。只要有一个环节脱钩,EFI 就遇到障碍了。我个人根据网上透露的资料分析后认为,EFI 是一个恶意的规范,对用户而言是恶意的,它更方便了操作系统制造商对用户进行各种控制和限制,用户将失去原有的自由。另一方面,EFI 也有可能以失败而告终,硬件制造商有可能被迫长期兼容 BIOS。
 楼主| 发表于 2012-10-18 20:23:56 | 显示全部楼层
在粗略介绍了 grub4dos 的主要特点之后,接下来就是分专题进行详细介绍。

grub4dos 有两个核心文件,grldr 和 grub.exe,分别用于不同的场合。核心文件是如何构成的呢?那些想深入了解 grub4dos 内幕的人,通常很关心这个问题。

所以,应该有一个专门的介绍。题目就叫做:

核心文件的结构

grub4dos 有两个核心文件,grldr 和 grub.exe,分别用于不同的场合。

在 grub4dos 发展的早期,只有 GRUB.EXE 这一个核心文件,没有 GRLDR 文件。GRUB.EXE 起初只是 DOS 可执行文件格式,它由两部分构成:

头部 + 主体

“ 头部 ” 是为 “ 主体 ” 的运行做准备的。“ 主体 ” 就是 GNU GRUB 的核心代码 pre_stage2。后来,grub.exe 也可以当作 DOS 设备驱动格式用 DEVICE=grub.exe 加载,甚至 grub.exe 也成为一个合法的 Linux 内核格式,可以被其他许多引导工具加载。不过,“ 头部+主体 ” 这个总体结构没有变化。

GRLDR 也是类似的,由 “ 头部 + 主体 ” 构成。与 “ GRUB.EXE 是为 DOS 而生 ” 的情况类似,GRLDR 就是为 NTLDR 而生的。它甚至曾经被称为 “ grub for ntldr ”。grub4dos 的核心文件都碰巧具有这样一个简单的、单一的总体结构,这使得它们容易被分析和掌握。

GRLDR 的头部占用 16 扇区,也就是 8K,即 8192 个字节,或者(用十六进制表示)0x2000 字节。GRLDR 的头部会被 NTLDR 加载。遗憾的是,NTLDR 不能够把 GRLDR 完整加载到内存,只能刚好加载 8K 大小的头部。这就是 “ 为什么 GRLDR 的头部正好是 8K ” 的原因。这样,当头部获得控制后,它就有一个首要的任务,即寻找 “ 主体 ” 部分在哪里,然后加载主体部分到内存。实际的执行步骤是,头部在各个硬盘各个分区的根目录寻找整个 GRLDR 文件(如果都失败,那么最后会尝试在第一软盘 fd0 的根目录寻找 GRLDR 文件),找到 GRLDR 之后就加载完整的 GRLDR 文件到内存,并递交控制权。

这里顺便提及 grldr.mbr 文件。它主要被用于安装到 MBR 上。它的长度是 18 个扇区(即 9K)。它的任务和功能是寻找并加载 GRLDR 文件。这有点类似于 GRLDR 头部的作用。但 GRLDR 头部是被 NTLDR 加载,而 grldr.mbr 是被 BIOS 加载,所以,这还是有差别的。grldr.mbr 末尾的两个扇区是用来探测和适应 BIOS 的磁盘几何参数的。所以,grldr.mbr 的长度是 18 个扇区,比 grldr 的头部(16 扇区)多了两个扇区。grldr.mbr 也能被 NTLDR 加载,但是,NTLDR 也只能加载 grldr.mbr 的开头的 16 扇区(8K),不能加载末尾的两个扇区。NTLDR 的升级版 —— BOOTMGR —— 却能够完整加载 grldr.mbr 到内存,这是因为 BOOTMGR 可以加载长达 64K 的文件。而 grldr.mbr 只有 9K,所以,它能够被 bootmgr 完整加载到内存。bootmgr 的 64K 加载能力还使得它可以被用来加载 wee63.mbr 文件。wee63.mbr 是另外一个启动软件 WEE 的核心文件。WEE 是一个基于 grub4dos 而开发出来的微型启动管理器。

从版本 0.4.5 开始,grub.exe 也能够被 ntldr 加载(在 boot.ini 的末尾添加 c:\grub.exe="grub.exe" 这样一行便可,完全类似于 grldr 的情况)。

grldr 并非只能被 ntldr 加载,它也可以被其它启动软件加载。例如,fbinst、syslinux 都可以加载 grldr。原则上讲,任何启动软件(经过改造以后)都可以加载 grldr,因为加载它是很容易的,只要把 grldr 放在 16 字节对齐的内存地址处便可递交控制权了。另外,凡是能够加载 ntldr 的引导管理器,也都能加载 grldr。这是因为 ntldr 总是被加载在固定地址 2000:0000 处,而如果 grldr 也被加载在此处,当然是可以正常运转的,因为 2000:0000 是一个 16 字节对齐的地址。

当网卡的 PXE BIOS 加载 grldr 时,它把 grldr 加载在 0000:7C00 处,这也是一个 16 字节对齐的地址。所谓 “16 字节对齐”,就是“可以被 16 整除”的意思。

无论 grldr(或 grub.exe)被什么东西加载(以及加载在何处),这个被加载了的 grldr(或 grub.exe)都会自动执行一个附加的步骤,即把主体部分(pre_stage2)放置在固定地址 0000:8200 处。这就是说,待到主体部分即将取得控制权的一刻,它总是位于 0000:8200 处。

前面已经交代了 grldr 和 grub.exe 的宏观结构都是 “头部+主体”。但是,“头部” 和 “主体” 都有更精细的结构。
 楼主| 发表于 2012-10-18 20:24:05 | 显示全部楼层
(续)

GRLDR 的头部固定占用 16 扇区(8K)。但是,GRUB.EXE 的头部很长,而且不是固定的长度。因此首先需要确定这个头部的长度。

在 grub.exe 的偏移 0x1F0 处的一个字节,就表示头部所占用的扇区数。

偏移 0 处当然是 DOS 可执行文件的 MZ 标志。偏移 0x02 处的两个字节本来应该是 DOS 的 “EXE file last sector in bytes”,但是已经被 GRUB4DOS 用 EB XX 这条 JMP 指令覆盖掉了。这样做其实破坏了 EXE 的格式,但破坏得不严重,仍然能够被 DOS 认可。接下来的 24 个字节当然是 DOS 的 EXE 头部所需要的其它一些固定域。

从偏移 0x20 开始,是 DOS 设备驱动程序文件的一些格式域。再接下来就是第一扇区的可执行代码了。这个代码模拟 GRLDR 的第一扇区。其实,grub.exe 的开头的 16 扇区(8K)也模仿 GRLDR 的头部(即开头的 8K),完成类似于 GRLDR 的功能,这就使得 grub.exe 可以像 GRLDR 那样被 NTLDR 加载,即,在 boot.ini 中用 C:\GRUB.EXE="GRUB.EXE" 的方式也可以启动 GRUB4DOS。

在第一扇区的末尾处,从偏移 0x1F1 至 0x1FF,是 Linux 内核格式的一些域。这使得 GRUB.EXE 可以被别的软件当作 Linux 的内核来加载和启动。这很重要,因为从某种意义上说,Linux 内核格式也具有事实工业标准地位。所以,把 GRUB.EXE 装扮成 Linux 内核,可以方便其它软件调用 GRUB4DOS。Linux 内核的格式域其实还延伸到第二扇区开头的 48 个字节(参见 Linux 技术文档)。紧接着 Linux 内核的格式域之后,有很多 0x90 字节,这是机器指令 NOP(空操作),目的是适应不同版本的 NTLDR。不同版本的 NTLDR 在加载 GRLDR/GRUB.EXE 时,会把控制转移到这个区域中的不同位置(而不是跳转到 GRLDR/GRUB.EXE 的偏移 0x00 处),所以,我们才需要用很多 NOP 来适应这种情况。这些 NOP 一直延伸到偏移 0x269 处,紧接着在偏移 0x26A 处,当然就是一条跳转指令了,跳转到 GRLDR 的处理代码去执行,开始 GRLDR 的启动过程。这就是 GRUB.EXE 模仿 GRLDR 被 NTLDR 加载时的大致情况。

知道了头部的长度就是 0x1F0 处的一个字节所表示的头部的扇区数,这就等于说,已经可以知道主体部分起始于何处了。

相对于 GNU GRUB legacy 而言,头部是 grub4dos 特有的。头部的唯一作用就是加载主体到内存。而主体就是 GNU GRUB legacy 的核心代码 pre_stage2。因此,当主体获得控制时,头部就没有必要存在了。头部就像三级运载火箭中的一个级,它的目的和作用就是把主体送上轨道,然后自己也就废弃了。当主体获得控制以后,此时再来找头部就找不到了,因为它已经从内存中消失了。

grub4dos 也改造了 GNU GRUB legacy 的核心文件 pre_stage2(它就成为了 grub4dos 的主体部分)。grub4dos 在主体的开头安排了很多核心变量,可以让用户读取或写入。有些变量是不可以写入的,一旦写入,就破坏了 grub4dos 的运行环境。而有些变量是可以写入的,用户通过写入这些变量,来控制 grub4dos 的行为方式。

主体部分在内存中的位置是固定的,它被头部加载在物理地址 0x8200 处,此处可以看作是主体的固有 “轨道”。因此,很多变量都在 0x8200 之后的一个较小的区域之内。当然,grub4dos 内核中的许多结构都属于用户可访问的信息,它们分散在全部内存空间中。例如,0x800 处的 4K 字节是未压缩的内置菜单;0x110000 处的 256K 字节是压缩的内置菜单;而内存虚拟盘的映像通常位于内存的顶部。grub4dos 内核对于内存的使用似乎有些“破碎”的感觉,但这是有一个发展过程的,我们不希望后续的 grub4dos 版本会给用户带来麻烦,因此要尽量保持兼容性。而兼容性的要求,就使得 grub4dos 对内存的使用有些古怪。

头部都是用 assembly 写成的。与 GRLDR 的头部相对应的源程序文件是 grldrstart.S。与 grub.exe 的头部相对应的源程序文件是 dosstart.S。主体也需要一些 assembly 代码。主体的 assembly 代码大多都是在 asm.S 中,也有一些是内嵌于 C 语言的源程序文件中的。

asm.S (的编译结果)就是主体从头部那里接管到控制权的地方,它位于主体的最开头。它的第一条指令就是一个远跳转。

偏移 0x05 处是一个控制字节,可以被用户改变,用来控制 grub4dos 的行为。
偏移 0x06 处的两个字节是 GNU GRUB legacy 的内部版本号。
偏移 0x08 处的四个字节是表示启动盘的启动分区的分区号。这里的分区号并不是我们通常理解的分区号,而是稍有不同。四字节中的最高字节应该总是 0x00,最低两个字节通常都是 0xFF,而真正的分区号就记录在次高的字节中。grub4dos 启动的时候,会自动把启动盘的启动分区号填写在这里。启动分区的编号是:0,1,2,3 为主分区,4,5,6,……,0xFE 为逻辑分区。分区号如果是 0xFF,则表示启动盘不含分区表,例如,当启动盘是软盘或光盘的时候就是如此。

偏移0x11 处的一个字节也是一个控制字节。它的最低位(位 0)用来控制 grub4dos 是否强制认为所有的设备都支持 LBA。用户可以写入它,但通常是不需要写入的,它的默认值是 0,即并不强制认为所有的设备都支持 LBA。它的次低位(位 1)用来指示当前正在运行的 grub4dos 是否被(某个启动软件)当作 Linux 的内核来启动的。启动时,grub4dos 自己会写入它,用户不应该写入它。其它位目前未定义。

偏移 0x6C 处的 4 字节指示内置菜单在什么地方。外部程序可以据此确定内置菜单的起始位置,然后就可以导入、导出内置菜单了。(具体如何计算内置菜单的起始位置,此处暂不涉及。)

偏移 0x80 处的四个字节记录了启动盘的盘号,00 是软盘,0x80 是硬盘。光盘通常是 0x81 - 0xFF 之间的某个值。不同的 BIOS 为启动光盘赋予的盘号也不同。只有最低的一个字节不是 00,其它三个字节应该总是 00。grub4dos 在启动时自动记录启动盘的盘号,用户不需要写入它。启动盘的盘号以及分区号是有可能被改变的。configfile 命令就能够改变它们。BIOS 只使用 00 - 0xFF 之间的盘号,但是,grub4dos 的盘号采用 4 字节的整数。grub4dos 的 (md) 设备所对应的盘号就是 0xFFFF,因此,(md) 设备也可以用 (0xFFFF) 来表示。

偏移 0x138 处的 4 字节表示 DOS 启动盘的信息。最低字节表示 DOS 启动盘的盘号。00 是软盘,0x80 是硬盘。次低字节表示 DOS 启动盘的最大磁头号(即,磁头数 - 1)。次高字节表示 DOS 启动盘的每道扇区数。最高字节目前没有定义。

偏移 0x13C 处的 4 字节表示 DOS 启动盘的启动分区的隐藏扇区数(或者等价地说,是启动分区的起始扇区号)。

当 grub4dos 是从 DOS 下启动的时候,grub4dos 会自动设置这些信息。但是,外部程序加载 GRLDR 或 GRUB.EXE 的时候也可以设置这些信息。如果偏移 0x138 处的四字节整数不等于 0,那么 grub4dos 将强制把 0x138 处的一个字节所表示的盘号当作启动盘,也强制把 0x13C 处的值当作启动分区的起始扇区号(前提条件是,启动分区必须是一个主分区,而不能是逻辑分区)。此时,当然也根据偏移 0x139 和偏移 0x13A 处的值来强制设置启动盘的几何参数。

如果 0x138 处的四字节整数值保持 0 不变,那么还有另外一种办法来强制设置启动盘的信息。首先把软盘分区或者某个主分区(注:它不可以是逻辑分区)的第一扇区(即含有 BPB 的引导扇区)加载到 0000:7C00,然后再把控制权交给 grub4dos,这样,grub4dos 也会检测到 0000:7C00 处的启动盘的信息,并据此强制设置启动盘的信息(尤其是几何参数)。这是模仿 NTLDR 的启动逻辑。NTLDR 就是根据 0000:7C00 处的 BPB 信息来决定启动盘的。
 楼主| 发表于 2012-10-18 20:24:15 | 显示全部楼层
如何使用grub4dos

对于多数没有或很少接触过linux的windows用户来说,刚开始使用grub时离不开菜单。也就是说,使用grub前,我们要准备menu.lst文件。

下面是一个menu.lst的例子。(以#开始的行,表示注释,不执行)

# 默认延迟时间(秒)
timeout 30

# 第一项为默认值
default 0

# 设置图形背景文件
splashimage (hd0,0)/boot/grub/xp2008.gz

# 设置中文支持的字体文件
fontfile (hd0,0)/boot/grub/fonts

title 使用map启动本地硬盘上的瑞星杀毒软盘镜像文件
map (hd0,0)/boot/grub/rav.img (fd0)
map --hook
chainloader (fd0)+1
rootnoverify (fd0)

title 使用memdisk启动本地 Win98 软盘镜像文件
kernel (hd0,0)/boot/grub/memdisk.gz
initrd (hd0,0)/boot/grub/win98.img

title 使用memdisk启动本地硬盘上的瑞星杀毒软盘压缩镜像文件
root (hd0,0)
kernel /boot/grub/memdisk.gz c=80 h=12 s=36 floppy
initrd /boot/rav.zip

title 启动第一主分区(hd0,0)上的操作系统
rootnoverify (hd0,0)
makeactive
chainloader +1

title 启动第二主分区(hd0,1)上的操作系统
rootnoverify (hd0,1)
makeactive
chainloader +1

title 重启
reboot

title 关机
halt

参考这个例子,我们就可以根据自己需要对菜单进行修改。

比如从网上下载了效率源的镜像文件,文件名为XLY.IMG。我们只需把
title 使用memdisk启动本地 Win98 软盘镜像文件
kernel (hd0,0)/boot/grub/memdisk.gz
initrd (hd0,0)/boot/grub/win98.img

改成
title 硬盘检测修复工具 效率源 (memdisk)
kernel (hd0,0)/boot/grub/memdisk.gz
initrd (hd0,0)/boot/grub/xly.img

或者改成
title 硬盘检测修复工具 效率源 (map)
map (hd0,0)/boot/grub/xly.img (fd0)
map --hook
chainloader (fd0)+1
rootnoverify (fd0)

同时把xly.img文件保存到第一主分区(一般是C:)的boot\grub目录下。
当然也可以保存到其它路径。

比如xly.img文件保存到C:\boot下,则menu.lst文件相应改为:
map (hd0,0)/boot/xly.img (fd0)

又如xly.img文件保存到D:\test下,则menu.lst文件相应改为:
map (hd0,4)/test/xly.img (fd0)
(参看下面的几点提示d)

从上面的例子可以看出,启动软盘镜象,有两种方式,即用memdisk或用map。

用memdisk时,img文件可压缩;在img文件大小为非1.44MB或2.88MB标准镜像时,需要指定CHS参数。img文件的CHS参数可用winimage获得。
【提示:参数H, S是关键参数,必须准确无误;参数C设定错了,也不影响引导。】
另外,菜单文件中的文件路径可用相对路径(如:/boot/rav.zip)或绝对路径(如:(hd0,0)/boot/grub/xly.img)表示。

map方式对软盘映像大小没有限制,但要求必须连续存放。
map --mem则无此限制,但要求有足够的内存(使用超大映像文件时要注意这点)。
此映像将被拷贝到一个内存区域,映像本身可以是非连续的,甚至可以是被压缩的。


比如:
map --mem /boot/xxx.ima (fd0)
map --hook
chainloader (fd0)+1
rootnoverify (fd0)

官方文档“Grub4dos高级功能”中对map和memdisk的区别进行了说明:
  • map是GRUB4DOS内置的功能,而memdisk是一个外部的程序
  • map可以直接映射磁盘上的文件,而memdisk必须要把文件装载到内存里。
  • map可以把影像文件映射为第二只硬盘,而而memdisk只能映射为第一只硬盘。
  • map有自动生成MBR的功能,而memdisk没有。因此memdisk只能使用磁盘影像,不能使用文件系统影像。
几点提示:
a, 菜单文件中所用的memdisk.gz文件,不包括在grub4dos的发行包里,需自行准备。可从SysLinux软件包或从其它基于grub的工具(如本人的GGhost一键恢复)中获得。

b, 菜单中所用的其它文件如xp2008.gz(背景文件)、fonts(中文字体文件)及各种镜像文件等,均需拷贝到文件中指定路径。 fontfile命令不可单独使用,需与splashimage一起使用。

c, 菜单文件中的命令全部使用小写。

d, 初学者使用绝对路径时要注意GRUB对设备的命名方法。系统的第一个硬盘驱动器表示成(hd0),其上的第一个分区表示为(hd0,0),也就是说对于硬盘,采用(hdx,y)的形式来表示,x、y都是从0开始计数的,x表示硬盘号,y表示分区号。
由于主分区只能有四个,所以第一硬盘的四个主分区分别用(hd0,0)~(hd0,3)来表示;逻辑分区则从(hd0,4)开始算,即第一逻辑分区用(hd0,4),第二逻辑分区用(hd0,5)来表示,依次类推。
一般机子的硬盘都是一个主分区,其余是逻辑分区。因此C盘用(hd0,0),D盘用(hd0,4)来表示。 光盘用(cd)表示,第一软驱用(fd0)表示。

 楼主| 发表于 2012-10-18 20:24:28 | 显示全部楼层
关于G4D的一些“经典”问题的搜罗解答

Q1:
大硬盘(128G以上),无法启动GRLDR,可是在Windows下一切正常。
Answer:
因为BIOS有缺陷,往往无法访问比较靠后的文件(GRUB仰赖BIOS来访问磁盘),但是Windows有自己的磁盘驱动,所以没有问题。
Workaround:
把GRLDR移到磁盘靠前的位置

Q2:
在硬盘上的某某文件(ISO、IMG等)无法直接被map映射,加了--mem参数可以。
Answer:
因为那个文件不是连续存放在磁盘上的。直接map需要文件连续的存放在存储器上,
Workaround:
利用磁盘整理工具整理一下该文件即可(比如说WinContig)。

Q3:
我用--mem的方式来启动PE,可惜在启动时遇到了蓝屏(注:不是7B蓝屏,直接map也不会蓝屏)。
Answer:
因为你的BIOS有问题(或者叫做恶意封杀也行),对GRUB4DOS的map兼容不好。
Workaround:
使用新版的GRUB4DOS启动,在map语句后,map --hook前加一句:map --e820cycles=x(x的值在0-3之间,一般的新机器用3,一些品牌机要用0)

Q4:
我用新版的G4D把font文件放到第一个盘的第一个区上,用fontfile (hd0,0)/fonts这个语句,可惜无法显示中文。
Answer:
fontfile是旧版的命令,新版应该使用VBE模式。
Workaround:
①打开你的菜单
②从http://code.google.com/p/grub4dos-chenall/downloads/list找到unifont.hex.gz这个文件并下载,放到某目录(假设是(hd0,0)/boot/grub)
③把fontfile (hd0,0)/fonts这句删掉,换成:


graphicmode -1
font (hd0,0)/boot/grub/unifont.hex.gz


④用UTF8格式保存你的菜单。

转自无忧
 楼主| 发表于 2012-10-18 20:24:35 | 显示全部楼层
预留
 楼主| 发表于 2012-10-18 20:24:43 | 显示全部楼层
预留
 楼主| 发表于 2012-10-18 20:24:58 | 显示全部楼层
预留
 楼主| 发表于 2012-10-18 20:25:06 | 显示全部楼层
预留
发表于 2012-10-18 20:57:22 | 显示全部楼层
感谢天意出教程。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表