- 引入BCD
系统启动是一件困难的事情,面对困难,在实现的时候尽量简单化,是个保守而易用的原则。所以Windows系统最初的启动配置文件是一个文本格式的文件(boot.ini),系统启动管理器通过解析文本内容,提取启动参数。
启动配置数据(Boot Configuration Data,简称BCD)是在Vista的时候引入的新机制,目的是希望把启动弄得复杂一点——不不,抱歉我说反了,目的是用一个综合而安全的方案,满足日渐复杂的启动需求。更进一步说,BCD的引入是希望把现存和可能的多种启动方式,和系统启动过程的扩展和配置需求,集成在统一的接口中进行管理和使用。这个统一的配置接口能够覆盖各种需求,包括不同的启动方式,支持休眠与唤醒,实现系统的回退安装,支持启动初期的硬件检测等。
人们对BCD的态度褒贬不一。从肯定的方面看,当一个事物的复杂性增加时,换用更灵活的手段对它重新管理和呈现,是发展的必然。就好比从蜗居换入广厦,室内装修和布置方案,自然是可以大作改观的。从批评的一方看,很多开发者认为,BCD以元数据的方式保存配置信息,数据本身不具有可读性,同时BCD的结构设计得相当复杂,对于学习和使用都缺乏亲和性。他们一致的看法是,虽然boot.ini简陋,但BCD却过于激进,更受青睐的方式是grub方式的配置文件,grub是一种在Linux系统上被广泛使用的启动管理器,其配置文件是文本文件格式的,可直接使用文本编辑器进行编辑和查看,同时还达到了功能完备的目的。
纷争虽然存在,但学习是唯一手段。作为既成的事实,BCD在Windows系统上的重要性业已根深蒂固,如此重要的系统基础设施,已经没有推翻重来的可能性。
- UEFI和BCD
BCD和UEFI有莫大的关联,实际上可以认为,BCD很大程度上是为了配合新固件平台的使用与推广而提出的。读者可能还不知道,在UEFI平台上,系统的很多BCD配置是直接和UEFI的配置参数关联的,通过BCD编辑器进行参数编辑的时候,相关内容会直接更新到UEFI的配置内存中(一块专属UEFI的NVRAM)。当然,BCD也适用于老的BIOS平台,只不过结构简单了一些。这一节,会先针对UEFI平台进行介绍。
一个完整的BCD配置称为一个库(Store),保存在一个独立的文件中。系统中可以存在多个库文件,但只有一个会在系统启动时被使用,它被称为系统库(sysstore)。默认情况下,系统库的位置是固定的,位于启动目录下的名为BCD的那个文件就是。但在特殊情形下,可以临时改变系统库的路径,BCD编辑器提供了这个功能。这里说到的启动目录,它在UEFI平台下,指的是位于EFI启动分区(ESP,EFI System Partition)下的微软的boot目录,而在BIOS平台下,一般就是系统分区下的boot目录。
进一步看的话,我们发现BCD库其实就是一个注册表仓储文件(hive file),并且系统库(sysstore)会被系统的配置管理模块自动挂载到注册表的本地根节点上。进入Windows系统,打开注册表管理器,BCD系统库的注册表路径是:
HKEY_LOCAL_MACHINE\BCD00000000。
UEFI平台对系统有一个独立的数据分区需求,这个独立的数据分区就是所谓的EFI启动分区(ESP),它必须是在系统安装的时候就被创建出来的,并被格式化为FAT32格式,一般只用来存放和UEFI启动相关的文件和应用。UEFI是一个扩展性非常强的平台,它能运行在保护模式下,使用虚拟页地址,通过暴露多种API接口来支持各种应用和驱动扩展。通过UEFI启动的各种系统启动管理器程序,其实都是符合UEFI编程标准的应用程序,所以我们看到它们的名称后缀是.efi。Windows的启动管理器程序,默认的名称是bootmgfw.efi。Linux Grub启动管理器的名称是grubx64.efi。
图示:EFI系统分区在磁盘管理器中的位置
- BCD库和对象
BCD库以对象(Object)为基本单位来组织配置数据,每个BCD对象有一个唯一的ID,全局而言,只能通过这唯一的ID来识别对象。但有些特殊的对象还拥有易识别的别名,也可以通过别名来识别对象。每个对象根据其类型的不同,当然会拥有很多内容各异的配置项(Element),每个配置项由名称和值组成,可以认为是一个键值对(Key Value Pair)。
BCD对象的种类很多,我自己归纳之后,总结出了一个BCD对象的三层模型:顶层对象是固件的启动管理器,是UEFI固件的内部程序;二级对象是EFI应用,主要包括不同OS的启动管理器程序;三层对象是OS的系统加载器和启动应用。
图示:三层结构
先讲顶层对象,它是UEFI平台独有的,在BCD库中代表了UEFI的启动配置信息。顶层对象是全局唯一的,所以拥有一个别名{fwbootmgr}。别名总是赋给那些全局唯一的对象,比如{current}表示当前启动加载其对象,它也是唯一的。
可通过顶层对象来配置二级对象在UEFI启动界面上的显示顺序和默认项,以及UEFI等待启动的超时值。下面是一个例子:
图示:固件启动管理器对象
不管什么厂商的UEFI,都会显示一个启动选项菜单,这是最基本的功能。启动菜单所显示的内容,很大一部分就来自BCD的配置,并参考BCD的顶层对象的Displayorder项来决定显示顺序。但追根到底,启动菜单是UEFI的一个内部逻辑,BCD的配置仅仅是它的一个参考来源,最终显示成什么样子是由UEFI自主决定的。所以我们在看一个实际的UEFI启动菜单的时候,看到的内容未必和BCD完全一样,主要的不同是会多出来一些可选项。典型例子是UEFI会把传统的通过磁盘MBR启动的方式,作为Legacy项列举出来。
图示:UEFI平台的启动菜单包含了多种来源的信息
顶层对象的default项用来配置UEFI的默认启动项。上例既有Windows系统的启动项,又有一个Ubuntu系统的grub启动项,可通过下面的命令将{bootmgr}配置为默认项。
bcdedit /set {fwbootmgr} default {bootmgr}
顶层对象的timeout项其实很有趣,它表示UEFI进入系统启动过程的等待时间。UEFI的效率是很高的,系统自检和固件初始化并不花费很多时间,这无形中提高了用户体验,在没有太多感知的前提下,主机就迅速进入到正式的OS启动过程了。但有时候我们出于工程上的需要,却希望系统启动不要那么块,因为我们想进入UEFI/BIOS中进行配置或查看一些信息,至少要给我们一个反应时间,找到并按下快捷键才行。我在自己的几台主机上测试了以下,发现台式机的默认超时值是1s,超级本是0s。我便把顶层对象的timeout设置为5s,这样启动时我就有足够的时间找到并按下快捷键了。人生的节奏太快了,像星矢的天马流星拳一样,要达到每秒五百击才合格。我有点反应不过来,所以希望UEFI启动能慢一点,让我等个三五秒,在三次眨眼的时间里想想该干什么。
再来讲二级对象,代表了各种固件应用程序的配置,包括唯一的Windows的启动管理器程序(别名是{bootmgr}),以及非Windows系统的启动管理器,甚至还可以加入对传统的MBR启动方式的支持。二级对象的多少,不同的系统各异,看各人的折腾能力。但至少会有一个{bootmgr}对象,这是用户进入Windows系统的必经之路。
BCD是Windows的系统组件,所以{bootmgr}当然也就是最重要的二级对象。用户可以通过配置该对象的device/path项来更改Windows启动管理器程序的位置,默认位置是:ESP-partition-root\EFI\MICROSOFT\BOOT\BOOTMGFW.EFI。{bootmgr}更重要的作用是管理所有的三级对象,并通过项displayorder和toolsdisplayorder对三级对象进行排序,通过项default设置默认加载项。
三级对象非常重要,它们是正式进行系统加载的起点,管理着各种系统启动程序。从启动程序所能起到的作用看,三级对象分成两种,一种是启动应用对象(boot application),所有与系统启动相关的程序的配置对象,都属于这一类,如系统加载器对象。另一种是启动工具对象(boot tools),启动工具的执行是有一定的时间的,不会无限执行下去直到关机为止,执行完成后会重新回到启动选择过程。系统自带的内存检测程序,就是一种启动工具。
加载器对象用来配置一个真实OS系统的加载,默认的执行程序是winload.efi(BIOS下名称是winload.exe),和启动管理器程序位于同一个目录下。加载器对象的可配置项非常多,我常用到一些调试相关的项,如debug/halbreakpoint/bootdebug等。
唤醒对象(resume object)是一种特殊的加载器对象,当系统从休眠中醒来时,通过它来确定要执行怎样的唤醒逻辑。唤醒对象又是加载器对象的被引用对象,由加载器对象的resumeobject项来指定。
- 引用对象
在BCD库中,除了上述三层对象外,还有一些其它的零散对象,我称它们为引用对象。这些对象是被三层对象所引用而发生作用的,比如上面提到的系统唤醒,当系统加载器对象被选择后,发现系统此前处于休眠状态,就立刻参考resumeobject项的设定并使用唤醒对象的配置。调试配置对象(dbgsettings)也是个典型的引用对象,它可以被启动管理器对象和系统加载器对象所引用,当它们的deubg项被配置为true时,调试配置对象就会发生作用。其它的引用对象还有ems对象、全局配置对象等。
- 传统模式
传统模式(旧BIOS平台,或者使用了UEFI的CSM兼容模式)平台相对简单很多。BCD仍然被使用着,但不会和固件发生关系,所以顶级对象是缺失的。
启动时,BIOS选择启动磁盘,触发19h中断而开始引导过程。首先经由MBR获取分区信息并确定启动分区,然后跳转到启动分区的boot sector执行,再调用系统启动管理器。BCD库此时将被使用,{bootmgr}对象发挥作用,用户将有机会选择启动加载器进入系统,或执行启动工具进行系统检测。此后的操作,与UEFI模式相似。
参考:
1. MSDN:Bcdedit command-line options
2. MSDN:How to Modify the BCD Store Using Bcdedit
9,020 total views, 2 views today