想搞明白这个问题,你首先要知道“在数字信号层面,硬件设备究竟是什么样子”。
这是一颗典型的台式机CPU:
可以看到,CPU的屁股下面是一片片整整齐齐的铜刺。
这些刺的作用主要是给猫梳毛,用了那是人好猫也好:
谁用谁知道。
当然,这是CPU退休后的主要工作。
退休之前,这些刺的作用是插入这些洞洞:
我们可以看到,CPU的宝座下方,印刷电路板上有密密麻麻无数条细线一样的东西,就好像蜗牛爬出的痕迹一样。CPU那些“铜刺”实际上就连接在这些“细线”上面。
这些细线就是所谓的“印刷电路”,它的作用主要是给CPU上电刑。
这玩意儿你不严刑拷打,它就不乖乖干活……
咳咳。
好吧,说正经的。
CPU引脚的作用,一个是接受主板供电;另一个就是和形形色色的外部设备通讯:电信号通过印刷电路板,连通到线路板上大大小小方方正正的集成电路,指挥南桥北桥、集成网卡、声卡之类芯片工作;同时也接受它们发来的各种信号,针对性的解决外设们提出的问题。
CPU电信号并不仅仅和焊在主板上的芯片通讯;实际上,信号也会直接或间接的联通到旁边那些黑色蓝色的插槽里(在上面照片你看不见的地方,还会有白色褐色的各种插槽),管理你插在上面的各种东西。
这些引脚的作用都是事先规定好的。较新的台式机CPU引脚实在太多,我找了个老古董:
标红的那些引脚就可以和外设(以及内存)通讯。
现代CPU的引脚当然要多得多,但基本功能大同小异——除了换汤不换药的几百点差别之外,都是一回事。
嗯,大概就好像你玩的烟花和登月飞船的差别一样,大差不差。哈哈。
你可能马上会意识到一件事:
我买的主板可能来自很多很多不同的厂商,上面PCI/PCI-E插槽的数量、位置各不相同,将来用户插什么更是随心所欲——甚至,哪怕主板上用的声卡芯片本身,光小螃蟹一家都可能有alc882、883、887等等区别;再加上创旧啥的……
这么多五花八门的硬件,CPU怎么知道它们在哪里?怎么知道哪条线上连着哪种硬件?
简单说,当你按下电源键时,将会触发一个“自检”程序;这个程序会遍历总线上的所有硬件,把它们的信息搜集起来、存入BIOS指定区域(或其他平台规范的指定区域)。
在Linux下,这个表格的其中一部分(PCI总线上的设备)可以用lspci命令查看(这个说法并不准确,lspci可能并非简单的读取smbios数据;不过这里就不深入探讨了):
上面的信息含义如下:
对第一行:
00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02)
其中:00:00.0
- The bus number, device number, and function number, in that order.
依次分别是总线号、设备号和功能号;这相当于设备地址。Host bridge:
- Device class.
设备类型;它实际上另外的一串十六进制数字,这里做了转换,显示成了更用户友好的文本。Intel Corporation
- Device vendor.
设备提供商440FX - 82441FX PMC
- Device name.
设备名称[Natoma]
- Mode of operation.
操作(控制?)类型(rev 02)
- Revision number.
版本号
如果改用lspci -n,我们就能看到它的数字形式了:
在Linux下,如果你有个设备无法被正确驱动;那么用lspci看一下上面的型号信息,然后上网搜索驱动,多半就能解决问题了。
事实上,Linux内核也是用类似方式搜索磁盘上和每个硬件相匹配的设备驱动、然后用insmod相关调用自动加载的。
我们可以用lspci -k看到PCI总线上每个设备对应的驱动:
红线标出的内核模块可以用lsmod命令查看,也可以用rmmod命令从内核卸载、然后再用insmod加载(想玩的话,可以在本地机上拿声卡、网卡练练手;但别折腾显示器、硬盘等关键设备的驱动,会把系统搞死的)。
有了设备驱动之后,操作系统终于可以骄傲的宣布,我有打印机支持!我有网卡支持!
然后,我们的程序就可以把自己的工作转交给设备驱动程序,让它帮我们完成任务了。
这个我之前写过文章,这里就不再啰嗦了:
前面提到了,我们的程序需要委托设备驱动和设备打交道;那么,设备驱动又是什么样子呢?
很简单,它是一组等待操作系内核调用的、由操作系统统一定义了接口界面规范的标准函数;这些函数内部会把我们的请求翻译成设备自己能够识别的“命令字”,通过CPU引脚发送到对应总线地址上——当设备收到了某个数字(命令字)时,它就知道自己该做什么了。
知道了这个之后,我们是不是马上就可以起很多“坏心思”了?
比如,机器上并没有显卡;但我们在机器自检过程中往SMBIOS写个:
00.0f.0 0300:15ad:0405
是不是就可以“欺骗”操作系统,让它以为总线00、设备号0f、功能号0对应的设备就是一块显卡?
然后,既然识别到了显卡,操作系统是不是就会傻呵呵的尝试为它加载一个驱动?
好了,驱动加载了,现在我们可以有两个办法“整活”。
办法一,驱动是我们自己写的;那么,当需要“把内容显示到屏幕”时,我不执行out指令(intel CPU上,in/out指令转门用来和外设通讯)——因为显卡就不存在,我能out到哪里?——而是搞一个vnc服务端,把屏幕内容转发到网卡或别的什么地方。
实际上,某些驱动甚至都不需要和SMBIOS中的信息对应。我说有,它就有。反正你想要显示什么,找我!
网卡也是类似路数。不过,Linux/Windows下,标准做法是搞一个假的“tun/tap设备”,它将被识别为网卡;而这个设备会把用户调用socket之类接口发来的网络报文给它(也就是我们写的程序);我们再把这个报文转发给物理网卡、USB或者声卡(比如把信号调制在音频中);反过来也对,我们可以把物理网卡、USB、声卡的mic接口传来的信息解码,再通过tun/tap设备发给用户,这就成功制造了一块虚拟网卡。
办法二,驱动就用标准的intel/nVidia自己的驱动;但我把驱动程序最终输出的IN/OUT指令都截掉,别让它真的发出去(因为设备并不在那个地址,或者虽然在,但不能让它用,否则会把宿主机的状态搞乱)——比如,可以利用AMD/intel的硬件虚拟化功能,这种功能会把最外层的、物理机上的操作系统置于ring -1,虚拟机程序则位于ring 0;当ring 0想执行特权指令时,CPU不会直接执行,而是触发一个中断,交给位于ring -1的物理机操作系统决定是否放行(或者是否需要改写,比如把本应传给物理网卡的数据传递到tun/tap设备)。这样一来,物理机操作系统就可以阻止或者重定向这些操作,既允许虚拟机执行各种动作、又避免它访问到敏感区域造成破坏了。
当然,很多设备,比如显卡,它的具体控制指令字以及详细参数是商业秘密,因此蹲在IN/OUT层面是没法有效模拟的。
那么,VMware等虚拟机就需要搞一个自己的虚拟显卡(或其他设备),直接从驱动层面就把用户请求截下来、然后渲染到自己的窗口里(或转发给其他设备)——驱动接口是标准化的,在这里截,动作就是可理解、可转发、可模拟的;不然到了IN/OUT水平,你都没人家的spec,还怎么搞?这也是为什么绝大多数虚拟机没法支持显卡物理加速的根本原因(当然,可以从openGL/DX 3D层面转发,不过技术上仍然存在很多麻烦,总之很难做到功能完整、性能不打折扣)。
而通讯协议有成文标准的,比如标准网卡、硬盘之类无需特殊驱动的设备,就可以在更低的层面(可以是IN/OUT指令,也可以是块设备层面;或者像网卡一样,专门搞出一个tun/tap设备来)拦下相关访问,从而搞出一个性能几乎不打折扣的、功能完整的虚拟设备——实际上,对于显卡,如果不使用它的2d/3d加速功能、仅仅当一块标准的VGA兼容显卡用的话,也是可以在较低层次兼容的。
原因很简单,这些东西有标准,任何厂商做都一个样,那么自然就可以“指令级模拟”它;相应的,没有统一标准、一家一个样的,那自然就没法“指令级模拟”,这种就必须装人家自带的驱动、我们也至多能模拟到“标准驱动接口”水平。
当然,如你所见,我这里很“糊涂”的把Windows/Linux以及虚拟/半虚拟混在一块乱说。
原因很简单,这些东西基本原理就这么个样子;但具体实现各家都不一样;甚至同一家在不同设备上都各有不同。因此这里只泛泛的讲一下基本思路,故意让你没法对号入座——前面就提到过,请学会看本质,不要纠结于细节。
除非你现在就有个任务,比如在Windows平台上实现一个半虚拟化的虚拟机、且要求你实现一个支持OpenGL/DX 3D的虚拟显卡:但你真能接到这种任务,还需要看我的入门科普吗?
总之,一言以蔽之:硬件设备对CPU来说是不存在的,它仅仅是往一组引脚输出电信号或者从引脚接受电信号而已(真·缸中之脑);而CPU对软件来说也是不存在的,软件仅仅是一组指令序列而已,这组指令你用人脑解读、草稿纸执行都行——除了慢点,没啥区别。
那么,只要你能接受CPU的电信号、并正确按设备标准把响应反馈回CPU的引脚,那么你就完全可以通过USB、网卡或别的什么东西远程模拟一个硬件,就好像它真的插在电脑主板上一样。
类似的,只要你有办法执行程序中的每条指令、并给出符合期望的执行结果——不管你是用真实的CPU执行还是写一个程序“假装”CPU存在(比如在intel CPU上用模拟器软件跑摩托罗拉CPU的程序),只要行为符合预期,程序就会“觉得”自己被人在“真实”的硬件上执行了。
换句话说,电脑系统的本质就是数字信号。只要你想办法把一组正确的数字信号适时混进输入输出流中去,你就可以模拟一切——就好像人脑的一切都是神经信号,那么理论上完全可以用模拟出来的神经信号欺骗一个泡在培养皿里的、孤零零的大脑,让它觉得自己是一个完整的人、享受了精彩的人生一样。
在操作系统的后面,应用程序的视角里,永远只有软件接口。
所以在软件接口的后面是个真的硬件还是个别的什么玩意,有什么不可以呢?
题主的问题可能是:在windows下装了个虚拟机之后,在windows里多了一个“网络设备”。
这问题说简单点吧。其实不管你物理上有没有这个“网卡”,windows本来都是认不出这个网卡的,windows之所以能知道有一个网卡,是因为有这个网卡的驱动程序。
道理差不多,懂了吧?