总线是计算机系统中的桥梁和公路。对于要学习计算机系统的人来说,如果不理解总线,那么很多认知就没办法落到实处,想不清两样东西是如何连接起来,数据是如何从一点到另一点的。
最近两三年,做了比较多的底层开发,更加感觉到总线技术的重要性,同时也对总线的理解有了一些新的感悟。
最近一段时间,又接触了几个总线有关的问题,触发我把以前的认知和理解都掏出来,”翻炒“数轮。在这样翻来覆去的思考中,有时也会冒出一些新奇的想法,产生一些新的”连接“。这些新连接把一个领域和另一个领域连通起来。
跳线之苦
我读大学时买的486电脑用的是ISA(Industry Standard Architecture)总线。这个总线的历史悠久,始于1981年,伴随着IBM PC从80年代走到我读书的90年代。
读书时,我对计算机系统的理解还模模糊糊,当时也没有仔细学习这个总线,因此也不理解它的优点和缺点。
大学毕业后,我做了一段时间的视频监控软件,当时的摄像头都是模拟摄像头,要通过一块视频捕捉卡才能把画面显示在计算机里。
我最初使用的视频捕捉卡就是ISA接口的。长长的一块卡,插在主板的ISA插槽上。
安装这个卡时,常常遇到一个非常头疼的问题,那就是因为“资源冲突”而无法安装驱动程序。
当年的计算机大多放在办公桌下面。安装卡时要把机器拉出来,拔掉电源,打开机箱,插上卡,然后盖上机箱,插电,开机,安装驱动程序。
而安装驱动程序时,经常跳出安装失败的错误框。遇到这样的问题后,就要关机,拔掉电源,打开机箱,把刚才插上的卡拔出来,通过卡上跳线帽(jumper)来修改资源配置。
卡使用的资源主要是中断和I/O地址,卡设计时,支持几种中断和I/O配置,以跳线方式来选择。选择新的跳线方式后,要重复前面的过程,插卡,盖机箱,插电,开机,再安装驱动程序。
PCI和PnP技术
我遭受跳线之苦的时间是1997年前后。其实当时已经有了更先进的PCI总线。但这是我后来才知道的。因为PCI总线虽然在1992年就宣布了,但是直到1997年时还没有流行起来。
PCI总线是英特尔领导的。它的最大特色就是即插即用(Plug and Play,PnP)。而即插即用说到底就是解决让人头疼的手工改跳线问题。
系统资源是有限的,对于某个资源,一块卡用了,那么另一块卡就不能用。对于ISA,只能通过修改跳线来选择别的。而PCI的PnP技术就是可以通过软件方式配置资源。
再进一步讲,每个PCI设备都有一个配置空间。上层软件可以通过这个配置空间来为它动态配置资源。
配置空间并不大,一般只有几十个字节。但这几十个字节的配置空间解决了大问题。有了这个空间后,上层软件不仅可以方便地查询卡的各种信息,而且还可以通过修改这些空间的数据来改变卡的状态。这种通过软件来自动配置硬件的思路显然比跳线要先进多了,是一次伟大的进步。
软件支持
那么,先进的PCI技术为什么没有一发布就流行起来呢?答案是要开发软件。要实现资源的动态分配,最适合做这件事的当然是操作系统。
最早支持PnP的操作系统是Windows 98。
在1998年年初的Comdex大会上,比尔盖茨携自己的TA演示PnP功能,自动安装驱动时,当场蓝屏,观众席上大笑声、口哨声、鼓掌声、唏嘘声,响成一团。比尔盖茨到底是大将风度,解嘲说:“这一定是我们还没有正式发布Windows 98的原因。”
Windows 98正式发布后,特别是Windows 2000的推出,PnP功能日益稳定,并不断流行起来。今天,PnP支持已经成为事实标准,所以年轻一代,再也没机会体验我当年经历的“跳线之苦”。
神奇结构体
用程序员的话来讲,所谓的PCI配置空间就是一个结构体,这个结构体定义了PCI设备的关键属性,包括它的厂商ID、设备ID、I/O资源,中断等。
在Linux内核的源代码中,可以看到一个名为pci_dev的结构体,它的大多数字段都是与PCI配置空间中的属性对应的。
从数据建模的角度来看,PCI设备结构体就是PCI设备的基本模型。
从接口的角度讲,PCI配置空间就是软件和硬件之间的一个标准接口。通过这个接口,驱动程序可以用“编程的方式”来查询和配置PCI设备。
在C语言中,结构体是常用的编程技术。但是,最初版本的C语言并没有结构体,汤普森用这个版本的C改写Unix时,觉得很不好用,这促使丹尼斯•里奇做改进,引入了结构体。有了结构体后,Unix开始彻底C语言化。
软件成熟后,PCI技术迅猛发展,成为PC系统的脊梁,至今仍是X86生态中的最重要总线。
宇宙总线USB
在PCI总线的白皮书中,封面上的PCI总线全称叫PCI Local Bus。
这个Local的含义是本地和局部的意思。具体来说,PCI总线是用在系统内部的,如果有机箱的话,那么是机箱里的。今天虽然也有所谓“内衣外穿”的方式来把PCIe总线用到系统外部,但是并不流行,成本也高。于是便有了一个问题,如何连接机箱外部的设备。
大约在1995年左右,Intel开始研发外部总线技术,解决这个问题。当时有多种备选方案,后来被选中的,就是今天流行的USB。
USB的全称非常大气,或者说非常霸气,“Universal Serial Bus”,可以翻译为“宇宙串行总线”,或者“通用串行总线”。
20多年后的今天,USB总线真的同行全球,乃至全宇宙。就连很多电源插座和拖线板上,都配了USB口,用来给USB设备供电。而且,USB口的通用程度比墙上的插座还好。墙上的插座还有中国和美国的差别。而USB不存在这样的问题,大家只要一看到熟悉的USB口,那么就插上用。
事实证明,USB总线的设计者没有吹牛,宇宙串行总线真的做到了畅行全宇宙,对得起它的名字。
描述符
如果说PCI总线的精髓是把PCI设备抽象为一个通用结构体的话,那么USB总线的精髓就是为USB总线定义了很多个结构体,这些结构体不是固定的大写,它的内容和大写是可以变化的,USB总线的设计者给这种可以灵活变化的结构体取了一个新的名字,叫USB描述符。
上图中的这段话代表USB技术的精髓,我在准备IOT课程的讲义时,特别把它摘录到了讲义里。
“USB设备使用描述符来报告它们的属性。描述符是一种定义格式的数据结构。每个描述符的开头都是一个1字节长的字段,描述整个描述符的大小,后面跟着的一个字节用来确定描述符的类型。”
多么灵活、经济而又高明的设计啊!
一切尽在描述符
因为描述符的长度灵活,类型多样,因此,几乎所有信息都可以用描述符来表达。
有一天,一个朋友遇到问题,他们开发的设备在BIOS里面显示的厂商名是Unknown。
他是熟悉PCI总线的,所以知道PCI总线是用厂商ID来决定厂商名,并且沿着这个思路想到工具网站去注册。
而当我看到这个问题后,我觉得不是这么回事。有了USB的描述符技术后,PCI总线的做法已经out了,不仅是注册繁琐,而且使用时还要根据ID查表。而USB描述符就灵活多了,名字就写在设备的固件里,你要读,就读描述符就行了。
有了USB的做法后,根据厂商ID来查名字或者装驱动的做法用的越来越少了,取而代之的是使用描述符,缺什么就读描述符。
举例来说,下面是挥码枪的设备描述符,里面报的厂商名是格蠹,产品名是Nano Target Probe(NTP)。
重要的是,描述符信息就写在固件里,修改特别方便。
在我保存的工具中,有个叫siv的小工具,可以查询系统中的设备,遇到PCI设备时,会根据它的pcidev.txt文件查找设备名称。
但这种做法显然存在更新不及时的问题,维护也不方便,更重要的是违背谁的数据谁自己管的原则。
泛化和抽象
这些年我经常思考的一个问题是“抽象”。有时是专门思考这个概念,有时是把它应用到某个场景。
回顾我们前面提到的三种总线技术,ISA是缺少抽象的。每种设备都有专门的驱动程序。驱动程序和硬件之间是靠“约定”来协同的。它们了解对方的方法是靠自己的知识。
相对ISA,PCI总线显然前进了一大步,把PCI设备抽象为一个固定的结构体,驱动程序可以通过这个结构体来查询硬件,动态配置硬件。从产品的角度来看,这样更有利于实现通用的驱动程序,方便软硬件的兼容。
而USB技术,则又前进一大步,把PCI使用的固定结构体扩展为变化无穷的描述符,这样一来,就无所不能,无所不包,所以能畅行于宇宙了。也因为此,今天的USB设备,用户使用时,很少遇到驱动程序的问题。
通过这三大总线,可以看到建模和抽象的重要性。ISA太缺少抽象,所以有跳线之苦,PCI总线有所抽象,使用至今,而USB总线在PCI的抽象基础上再做抽象,把固定结构体改为不固定的描述符,通用性达到了一个新高度。
你是谁?
如果把驱动程序和硬件设备比作两个人,那么三大总线的驱动和设备之间的对话方式是迥然不同的。模拟几个对话方便大家理解。
ISA驱动:喂,你是谁啊?
ISA设备:我是谁,你还不知道么?
PCI驱动:喂,你是谁啊?
PCI设备:读我配置空间里的厂商ID,然后查PCI标准委员会公布的厂商名录就知道了。
USB驱动:喂,你是谁啊?
USB设备:亲,读我的描述符就知道了。
三种方式,显然USB的最灵活,最方便。
玄而又玄
我信儒家,但偶尔也读一下道家的作品。对于道家说的话,我常常不喜欢。因为道家的话,太宽泛,太虚,左右逢源,不具体,不明确。比如一句“道可道,非常道”就有很多种解释,怎么说都行。
但是在深入思考抽象的价值后,我增加了对道家的认同。在抽象方面,道家做得非常早,也做得非常好。
关于抽象的价值,道家也有个非常具有道家特色的归纳。
“此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。”
“玄之又玄,众妙之门”。
什么是玄呢?翻遍我手里的道家著作,都没有太好的解释。我给它个解释,玄就是抽象。
玄而又玄就是在抽象的基础上再做抽象。
2020年疫情期间,我开了一门《在调试器下理解计算机系统》的在线课程,其中有一讲是关于PCI总线的。借着这个机会,我把总线有关的知识做了一次总结。
2021年,我实际参与两款ARM产品的开发,一款是使用ARM A核的GDK8,另一款是使用ARM M核的GDK3。在开发这两款产品和配套课程的时候,我仔细学习了ARM生态中的总线——AMBA。
在今年4月的杭州研习班间隙,我与学员们一起漫步葛岭,刚好经过葛岭上的道家圣地——抱朴道院。
在道院门口,我与格友们驻足休息,一边欣赏风景,一边介绍了我的三大总线和玄而又玄的理解。
(写文章很辛苦,恳请各位读者点击“在看”,也欢迎转发)
*************************************************
正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生
扫描下方二维码或者在微信中搜索“盛格塾”小程序,可以文章和有声读物
也欢迎关注格友公众号