windows无盘启动技术开发之不同网卡使用同一个启动镜像的问题

                               by fanxiushu 2023-07-13 转载或引用请注明原作者。
这是一个非常烦的问题,也不是实现技术有多难,而是繁琐。
这也更进一步制约了无盘启动技术朝广泛以及更通用的方向发展,只能用在特定场所。
我所知道的,目前用得最多的地方就是网吧网咖了。

制作好的无盘启动镜像,动不动的,放到别的稍微不同的配置(主要是网卡配置)的机器上就启动不了。
哪像云桌面(简陋的来说就是我们通常理解的远程控制,传输图像的那种远程控制),
云桌面制作好的镜像,随便到哪个终端都能运行,基本不挑食。
当然它也有它的问题:运算压力都在服务器上。

先让我们来看看,无盘启动为何会发生这种问题。
在前面四篇文章阐述 Leagcy BIOS和UEFI的无盘引导程序开发的时候,
说过BIOS和UEFI引导程序,成功加载windows的基本内核文件和 start==0 的驱动到内存之后,
控制权就交给windows内核,windows接过了控制权 ,开始一点点的建立自己的势力范围,
windows的这个启动过程基本划分为三个阶段:
1, Boot-Start 阶段
2,系统初始化阶段
3,登录阶段。
其中我们最关心的就是  Boot-Start 阶段。
这个阶段,windows首先是执行引导程序加载到内存的基本内核(ntoskrnl.exe),以及hal.dll等基础库,建立基本的运行环境。
这个时候,各种基本环境建立起来,比如线程,各种EVENT,OBJECT等等。
然后windows内核开始运行BIOS或UEFI引导程序加载到内存的各种驱动(注册表中的services, 全部 start==0 的驱动)
在整个Boot-Start阶段,windows可以不访问磁盘,这是合法的。
当然如果有哪个处于 start==0的驱动,需要访问磁盘,得注意是否能成功,需要做好不能成功访问磁盘的准备。
但是在Boot-Start阶段,却是建立磁盘的阶段。
简单的说,Boot-Start阶段可以没有磁盘,但是必须在这个阶段建立起磁盘,能让windows进入下一个阶段前,正常访问磁盘。
否则没有磁盘系统,在进入下一个阶段前,
windows就会弹出经典的 7B ( INACCESSIBLE_BOOT_DEVICE )蓝屏界面。

建立磁盘的方式,就是执行磁盘驱动,让磁盘驱动控制磁盘硬件,从而建立起来磁盘访问系统。
因此,作为windows系统盘的磁盘驱动,必定是在boot-start阶段(也就是注册表中start==0)的驱动。

因此开发无盘启动,windows的虚拟磁盘驱动是必不可少的。而且必须是在boot-start阶段运行的。
而我们的虚拟磁盘驱动,要让它正常工作起来,就必须在boot-start阶段能正常进行网络通信。
要正常进行网络通信,windows中的NDIS驱动必不可少。
NDIS驱动分成三种(底层的NDIS小端口驱动,NDIS中间驱动, NDIS协议驱动)
NDIS小端口驱动,就是具体的网卡驱动。
因此要在 boot-start 阶段正常进行网络通信,网卡驱动也必须是start==0, 而默认情况下,网卡驱动基本都是start=3,
所以需要我们做些修改。
同时必须确定对应的网卡驱动能在boot-start阶段运行,
(可能有些网卡驱动使用了系统初始化阶段才能运行的某些库,虽然很少见,但是可能存在,
这种情况下,这个网卡不能作为无盘启动使用)
光能运行还不行,还得确保确实能在boot-start阶段能建立网络通信。
比如通常的无线网卡就没法在boot阶段建立网络通信。

这个时候,我们使用NDIS协议驱动或中间驱动,就能跟这个网卡驱动通讯,建立起正常的网络通信渠道。
有人会说,何必使用NDIS协议驱动这么麻烦,直接使用TDI驱动或WSK驱动,建立起类似应用层的socket通信不更好!
想法是美好的,实际情况是TDI或WSK等驱动基本是在系统初始化阶段才建立起来的。
我也曾尝试过把TDI,TCPIP,WSK等驱动的start设置为 0,让它在boot-start阶段启动。
结果还是没成功,还是无法在Boot-Start阶段使用TDI,WSK这些库。
因此只能认为这些windows自带的驱动框架确实不能在boot-start阶段完全启动起来。

有人会说,windows自带的iSCSI协议的虚拟磁盘也能作为无盘启动使用,
也就是说它也是可以在Boot-Start阶段运行的。
而 iSCSI 是使用 TCP 协议通信的,也就是说TCPIP.sys在 boot-start 阶段被使用了起来。
而我这里的实际情况则是:使用通用的TDI,WSK框架,确实没法在boot-start阶段使用。
因此推测windows自带的iSCSI是通过某种特殊办法,在Boot-Start阶段建立了TCP通信
(比如在boot阶段绕过了WSK等框架,直接使用TCPIP,毕竟是微软自己的内核系统,想怎么搞都行;
其实我们也可以在NDIS协议驱动基础上,建立自己的TCPIP协议栈,
比如使用开源的LWIP等,但是效率肯定没有windows自带的TCPIP好,
而且就为了磁盘读写这种单一的网络请求,而绕这么一大圈,纯粹是找不自在),

具体我没仔细研究iSCSI协议如何在Boot阶段保持TCP通信的。
当然也欢迎有质疑的同学,提出自己的不同意见。

好了,回到正题。当使用NDIS协议驱动连接网卡驱动,建立网络通信,可以正常读写镜像服务器上的镜像的时候。
我们的虚拟磁盘驱动就能正常工作了。这个时候,boot-start阶段的磁盘驱动建立了起来。
windows正常进入下一步的系统初始化阶段,也不会再出现 INACCESSIBLE_BOOT_DEVICE 的蓝屏。

一切流程似乎都很美好,没啥可以指摘的。
可是一切问题都出在网卡驱动上,
地球上,网卡很多,发展了几十年,即便没上万,起码也得有好几千各种类型的通常在使用的网卡吧。
而且我也怀疑,估计早过万了。
于是乎,不同类型的网卡都对应不同的驱动。
A电脑上对应A网卡,在A电脑上制作的无盘镜像,是A网卡的。
如果把这个无盘镜像放到B电脑的B网卡上,因为镜像中没有B网卡驱动,
不认识B网卡,Boot-Start阶段无法建立网络通信,虚拟磁盘驱动无法跟服务端的镜像沟通,无法模拟出启动盘。
自然就会出现 7B的经典蓝屏了。

而且更玄乎的是:即便是A电脑,比如有多个PCI插槽位置,当把A网卡放到一号插槽位置,制作启动镜像。
然后再把A网卡换到二号插槽位置,结果先前制作的启动镜像也可能没法启动。
这是因为windows系统中,不同插槽位置,可能会生成不同的实例串,而一号插槽位置的网卡驱动生成的实例串与二号的不同。
于是当把A网卡插到二号插槽,依然找不到网卡驱动。
(这里不要把处于 Boot-Start阶段的驱动和windows正常运行起来能自动从网上下载安装硬件驱动混为一谈!)

这事情就奇葩了,
所以最保险的就是A电脑固定网卡位置制作的镜像,就供给A电脑使用,或者与A电脑相同的网卡配置使用。
如此显然无法满更多的足实际需求。

让我们回到 Leagcy BIOS和UEFI上来,
上面说得这么热闹,几千乃至上万的各中类型网卡在windows都对应着各种不同的驱动的问题,
而在BIOS和UEFI中,好像完全没这方面的概念,现在随便哪块网卡,直接插上,就能在BIOS和UEFI中设置PXE网络启动 。
那是因为在BIOS和UEFI中,规范了PXE网络启动的接口,
任何要支持网络启动的网卡,都必须符合BIOS和UEFI提供的 UNDI 接口规范。
那么问题来了,windows中也提供了 NDIS接口规范啊,为何会造成两个差别这么大。
这是因为NDIS规范把具体硬件和windows上层协议隔离开来,设计NDIS目的就是为了让上层不关心任何与硬件相关的细节。
硬件的各种麻烦的中断,IO等交给网卡驱动处理,只向上提供一个统一的接口即可。
也就是硬件本身可以随意做,只需要硬件厂商开发的驱动能提供统一的NDIS协议接口就可以。
而BIOS和UEFI中的UNDI规范,更像是规范一个硬件接口,
也就是硬件只能这么做,只能提供这么一个硬件接口,才能符合UNDI规范。
而BIOS和UEFI本身的定位就是基本的前期IO系统,只需要基本的通信功能即可,这么规定没啥问题。
而如果像windows,linux这些系统把硬件要求固定死,
显然很不利于硬件发展以及在windows,linux系统上发挥最大的硬件性能。

于是可能就有另外一种想法,为何不把UNDI功能集成到windows内核中,
也就是windows内核提供一套类似BIOS或UEFI那样的统一接口函数,直接访问本来是提供给BIOS使用的网卡UNDI接口。
这个我没法回答,因为毕竟对底层硬件方面了解不是太多。
最大的推测可能是到了windows启动阶段,网卡厂商的网卡驱动全面接管网卡硬件之后,UNDI接口已经失效,
或者UNDI接口无法形成一套有效的能在windows使用的通用接口。
(这就像是在现在的windows想要通过 Leagcy BIOS的 13H中断访问硬盘一样无法办到。)
如果真的这么好做,我相信UNDI早就集成到windows内核中了,否则发展了这么多年,Intel还在大量推广UEFI等情况下。
也找不到现代系统内核(不限于windows,linux中也找不到)中对UNDI的支持。

说了这么多,可如何解决这个问题呢?
好像都没啥称得上很完善的办法。

一个目前来说比较常用的办法,就是提取真实电脑的网卡信息,然后合并到镜像文件中。
首先在新的不同配置的网卡的真实电脑中,提取网卡在注册表中的信息。
主要是三个地方,当然有些网卡可能不止这些地方,因此会需要特殊对待。
A,\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\XXXX, XXXX是对应网卡服务名
B,\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\PCI\VEN_XXX&DEV_XXX&SUBSYS_XXX&REV_XXX
C,\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}\XXX,
找到对应网卡的具体位置,然后把上面这些位置从注册表导出来,
再找到对应的sys文件,也可能包括dll文件。
这样,大体算收集完成。
然后把注册表信息和对应的sys文件,合并到镜像文件中。

以上说的比较笼统。我这么做过,不过没成功,主要是测试对象是vmware虚拟机中,而且对应win7系统的虚拟机。
没有多余的真实机器来测试,只好vmware中测试。
在测试vmware虚拟机过程中,如果镜像是WIN7,使用NDIS5 才能启动到无盘,使用NDIS6 却无法成功,
只有vmware中装镜像是WIN10才能使用NDIS6启动到无盘。
后来调试才发现,vmware虚拟机在启动win7过程中,
不知道什么原因,如果是NDIS6协议驱动,Boot-Start阶段,无法得到网卡的Bind消息通知,
也就是NDIS6协议驱动的BindAdapterHandlerEx回调函数得不到调用,
就好像是vmware底层模拟的网卡硬件压根不存在一样。但是正常进入系统,却又能得到调用。
所以基本确定是vmware的问题。后来也懒得再去做测试了.

不过在真实电脑上,这么收集网卡信息,应该是没多大问题的,至少许多网卡都不会有太大问题。
目前好像大部分提供无盘启动的软件,都有类似的 网卡PNP提取工具,或者叫网卡PNP收集工具。
更先进点的,还可能是提供了一个数据库,专门从服务器上提供, 然后自动给镜像添加需要的网卡信息。

比如举一个可能的自动添加网卡信息的实现方案:
Leagcy BIOS或UEFI引导程序运行的时候,获取到网卡的硬件ID信息,然后发给服务器端。
于是服务器端判断无盘镜像中是否存在对应的网卡驱动,
如果没有,则从数据库中提取对应windows网卡驱动信息,然后合并到无盘镜像中。
接着再来响应引导程序的磁盘读写请求。这样到windows引导阶段,自然就不会再出现找不到网卡驱动的问题了。

所以,这些都不是很难解决的技术问题,而是很繁琐。那么多网卡,都去收集一遍,
而且针对特殊网卡,还得特殊处理,去多方面尝试。
而且随着windows版本的繁多,网卡驱动版本的繁多,以及不断新出现的网卡,遇到的实际问题可能更多。
当然经历了这么多年发展,无盘技术在这方面搜刮的网卡数据库应该是比较丰富的了。
所以,这里就不再进一步去啰嗦了。

----------------------------------------------------------------------------------------------------------------
除了上面讲述的,还有其他办法吗? 当然是有的。
我的目标是尽量让windows自己原汁原味的去安装网卡驱动,而不是收集网卡驱动然后合并到镜像文件中。
而让windows自己安装网卡驱动,就得让无盘镜像去启动不同配置的电脑,
而不同配置的电脑,因为网卡不同,是无法直接启动镜像的,于是乎,这似乎成了一个悖论。

但是我们可以换个思路,弄一个移动硬盘,没错,移动硬盘。
在移动硬盘中安装需要制作镜像的操作系统,这是可以办到的,
熟悉Windows To Go的安装制作的,对这个操作肯定不会陌生。
比如在A电脑使用移动硬盘安装操作系统,顺便当然也把A网卡的驱动安装了,其他驱动也一起安装了。
然后把移动硬盘插到不同配置的B电脑上,在B电脑上运行移动硬盘的操作系统,把B网卡的驱动也装上了。
然后就是C电脑,如此循环。直到完成。
然后制作无盘镜像上传到服务端。
以后这个移动硬盘都做同样的用途,如果需要安装新的软件,或者新的不同配置的电脑,都同样来一遍,然后上传新镜像。

虽然好像有些麻烦,但胜在简单,保险,网卡和各种驱动都是原汁原味的安装和更新。

比如,还有一个办法,弄一个USB接口的千兆有线网卡。
这个网卡有些需求,就是保证在不同电脑,生成的驱动实例必须一致,这个应该比较容易满足。
正规的USB设备都有唯一序列号,序列号保证了不同电脑都能生成唯一的实例。

然后在A电脑装上系统 ,同时装上这个USB网卡驱动。然后上传镜像。
接着在B电脑启动无盘,正常情况下,镜像中没有B网卡驱动,是无法启动的。
但是可以把USB网卡插到B电脑中,镜像中有USB网卡的驱动。

不过这里可能有些小问题,BIOS或UEFI可能 不认USB网卡作为PXE启动网卡。
于是可能就会出现PXE启动过程使用的是B电脑内置的网卡,
而到了windows启动阶段,才能使用USB网卡。
也就是出现了前后不一致,这需要无盘启动程序对这种情况提供一个支持。
使用USB网卡启动到windows之后,按照正常方式安装B网卡驱动。
这个安装的驱动就直接装到无盘镜像中了。

所以从通用性来说,还是使用移动硬盘的方式通用一点。

以上两种办法都是让某些硬件动起来,要么让硬盘动起来,要么让网卡动起来。
相信经常折腾无盘启动的,应该还有更多的解决办法。

你可能感兴趣的:(磁盘驱动,windows,windows,驱动开发,无盘启动)