- VxWorks设备驱动开发详解
- 曹桂平等编著
- 3777字
- 2020-08-27 06:25:18
第5章 VxWorks下设备驱动的内核结构层次
本章将介绍VxWorks下设备驱动的结构层次,首先介绍I/O子系统下维护的三张表及相互之间的关系,而后对内核已有驱动支持进行说明,并介绍VxWorks下文件系统的支持,最后讨论如何添加一个驱动到内核中。
5.1 认识VxWorks设备驱动内核基本层次
从上一章内容我们知道,底层设备驱动并不直接对用户可见,还需要经过操作系统中间层作为“桥梁”进行交互。这个操作系统中间层根据具体的应用可能又被分为几个子层次,我们将从用户层开始到底层硬件设备之间的所有由内核提供的中间软件层称为内核驱动层次(注意:底层驱动也是内核的一部分)。
为了保持应用层用户程序的平台无关性,操作系统为应用层提供了一套标准的接口函数,这些接口函数在所有的平台上都保持一致,只是随着平台的变化,底层驱动或接近驱动部分操作系统中间层可能会随着调整。如此一来,可以使用户程序脱离平台,增加了应用层开发的效率,避免了重复编码。通用操作系统下,将这套提供给应用层的标准接口函数从操作系统中独立出来,专门以标准库的形式存在,这样更进一步增加了应用程序的平台无关性,平台之间的差别完全被操作系统屏蔽。
VxWorks下也对应用层提供了一套标准文件操作接口函数,实际上与通用操作系统提供的一致,我们将其称作为标准I/O库,VxWorks下由ioLib.c文件提供。ioLib.c文件提供如下标准接口函数:creat、open、unlink、remove、close、rename、read、write、ioctl、lseek、readv、writev等。VxWorks操作系统区别于通用操作系统的一个很大的不同点是:VxWorks下不区分用户态和内核态,用户层程序可以直接对内核函数进行调用,而无须使用指令中断之类的机制或者存在使用权限上的限制。所以,VxWorks提供给应用层的接口无须通过外围库的方式,而是直接以内核文件的形式提供。用户程序可以直接使用ioLib.c文件定义的如上这些函数,这些函数名称与通用操作系统标准库下的函数名一致,是VxWorks对标准库的模拟。
由于是VxWorks内核提供,我们将ioLib.c文件中定义的如上这些函数看做是内核的一部分。ioLib.c文件提供的函数仅仅是一个最上层的接口,其并不完成具体的用户请求,而是将请求进一步向其他内核模块进行传递,位于ioLib.c模块之下的模块就是iosLib.c。我们将ioLib.c文件称为上层接口子系统,将iosLib.c文件称为I/O子系统,注意二者的区别。上层接口子系统直接对用户层可见,而I/O子系统则一般不可见(当然用户也可以直接调用iosLib.c中定义的函数,但一般需要做更多的封装,且违背了内核提供的服务层次),其作为上层接口子系统与下层驱动系统的中间层而存在。如图5-1所示显示了内核驱动层次。
图5-1 VxWorks内核驱动层次
从图5-1中可以看出,I/O子系统在整个驱动层次中起着十分重要的作用,其对下管理着所有类型的设备驱动。换句话说,所有类型(包括网络设备)的设备都必须向I/O子系统进行注册方可被内核访问。所以,在I/O子系统这一层次,内核维护着几个十分关键的数组用以对设备驱动、设备本身以及当前系统文件句柄进行管理。
1.USB设备驱动内核层次
对于USB设备,通过USB I/O层驱动与I/O子系统进行交互,而与底层的交互则通过USB栈完成,HC驱动即主机USB控制器驱动,VxWorks内核提供UHCI、OHCI、EHCI三种标准USB主机控制器的驱动支持,但是在嵌入式平台下有时会存在一个定制的USB主机控制器,此时可能需要编写底层HC驱动。
2.块设备驱动内核层次
块设备从当前应用来看,主要分为两大类:一类是长期以来使用的硬盘设备;另一类是Flash设备。硬盘设备驱动支持通过一个文件系统中间层,这个中间层在操作系统中十分关键,其对硬盘数据进行管理,提供硬盘数据的内核缓存区,避免频繁访问硬盘对整个系统性能造成非常不利的影响。对于硬盘这类块设备,直接在文件系统层下通过一个硬盘驱动即可完成。另一类块设备是Flash设备,Flash设备的最大特点是擦除次数有限,且擦除单元一般都较大,花费时间长,必须在文件系统层与Flash驱动层之间插入一个针对Flash设备这些特点的管理层,而且Flash设备每次写入的数据块相对硬盘设备都比较大,而在文件系统的统一管理下,我们需要将Flash设备模拟成一个硬盘设备,故在文件系统层和Flash驱动层还需要一个转换层(或称映射层),VxWorks内核提供TFFS(True Flash File System)中间层完成以上对Flash管理和映射的功能。TFFS中间层内部又进行分层,每层对应一个驱动文件,这一点在本书“Flash驱动”一章将有较详细的讨论。
3.字符设备驱动内核层次
字符设备即以字节流方式进行数据交互的设备,该类设备只能顺序读写数据,不能像块设备那样对同一数据进行反复操作。如一个ADC设备,其接收外界检波器模拟信号,转换为数字信号,通过串行接口将数据送给CPU,此时就需要一个驱动对这个ADC设备进行驱动以获取转换后的数据。从图5-1的层次来看,这个驱动将直接工作在I/O系统的管理之下。
串口设备本身也是一个字符设备,但是由于串口的使用范围广,为了提高串口驱动的编程效率以及串口数据的收发效率,VxWorks内核将串口设备与一般的字符设备区别对待,其提供一个TTY中间层驱动对串口数据进行管理(提供缓存)。TTY驱动向I/O系统注册,底层串口设备驱动对TTY注册。从底层驱动编程难易程度来看,TTY中间层的加入并非显著降低了底层串口的编程难度,但是TTY中间层提供的一个最关键的优点是极大地提高了数据收发的效率,而且也提供了很大的灵活性,如提供较多的选项用以对串口设备进行控制。
4.网络设备驱动内核层次
在第4章中,我们将设备类型从总体上分为了三类:字符设备、块设备和网络设备。其中字符设备和块设备均由I/O子系统进行管理,而网络设备由于其特殊的工作方式,将由另一套用户层标准接口函数(套接字函数)和内核网络栈进行支持。对于网络设备驱动的支持,现在使用较多的是VxWorks提供的MUX接口,在MUX接口下编写的底层网口驱动被称为增强型网口驱动(Enhanced Network Driver,END)。网络设备内核驱动层次如图5-2所示。
图5-2 网络设备内核驱动层次
网络数据由于其传输介质的不稳定性造成了网络设备内核驱动层次和应用层接口层的特殊性。网络编程应用层接口函数被称为Socket(套接字),其区别于其他所有的设备编程使用的标准接口函数。为了在网络上两台主机之间通信,我们首先必须约定一个协议,这样发送方以这个事先约定好的协议封装数据,接收方按这些协议进行解析,如此方能达到数据的传送,如果二者没有共同的协议作为约束,那么接收方根本就无法知道接收的报文中哪些是数据,哪些是为了使报文到达接收方的帮助信息。所以,网络数据传输从一开始就好设计好一套网络上所有的主机都必须遵守的通信协议。但是“一步到位”的协议设计方式为后期的扩展带来了极大的不便,如果一个新的更有效的协议被发明,那么网络上所有主机的网络驱动都要进行修改,所以网络协议从开始时就被设计为分层次的结构,每层使用本层上具有的协议对数据进行封装。
最经典的当前使用范围最广的就是四层分层结构,从上到下依次为:应用层、传输层、网络层、链路层。虽然OSI后来开发出了一个七层分层结构,但是基于四层分层结构的应用已经十分广泛。当然OSI也考虑到这一点,所以七层分层结构在核心层上(传输、网络、链路)设计成与四层分层结构兼容,这样原先的四层实现只需在传输层实现之上插入一个模块,即可完成对七层的支持,目前几乎所有声称支持七层分层结构的实现都是这样实现的。当然有些读者可能会有疑问,为何设计分这么多层?原因在于:
● 网络上传输一个报文不像主机内部传递一个报文,在网络上传输的报文首先要指定目的主机的IP地址以及任务绑定的端口号,同时为了使得接收方可以验证报文的合法性,还需要加上发送主机的IP地址以及报文发送任务绑定的端口号。
● 网络接口设备接收报文并不以IP地址作为判断依据,IP地址只是被操作系统网络栈实现代码使用,网络接口设备使用硬件地址,在以太网下称为MAC地址,一般是6B长度的字符串。所以,为了使一个主机能够从网络介质接收数据,报文中还必须提供网卡设备硬件地址。
● 为了共享网络,网络上每个主机每次发送的报文长度必须有限,然而用户层一次发送的数据并不关心这一点,所以在发送报文前还必须对报文进行切割,将原先的一个报文切割成几个报文分批发送,而在接收端又需要将这几个报文进行重组后发送给相关进程,这些都需要在不同的层次完成。
● 网络传输介质是不稳定的,可能有报文的丢失,不同的报文可能经过不同的传输路径到达,故一个先发送的报文可能后到达等,这些问题都需要接收双方进行考虑。所有的网络栈必须设计成多个层次,每个层完成不同的功能,如果所有的功能都集中在一起实现,那么代码将很复杂且不易维护和修改。分成多层可以在每层实现特定的功能,且在后期加入一个新的功能也较容易,不会对其他层的实现造成明显的影响。网络栈中“栈”的概念就是起源于操作系统对于网络报文的收发使用分层的结构。
网络栈的实现是操作系统实现的一个固定组成部分,VxWorks在基本的网络栈实现之下还实现了一个MUX接口层,专门与底层网络设备驱动进行交互。MUX接口层将底层网络设备驱动与核心网络栈实现隔离开来,其提供的接口函数极大地降低了底层网络设备驱动设计的复杂度,因为MUX层本身提供了一系列函数对网络栈的请求进行了响应,而且也提供一些辅助函数供底层驱动进行调用,帮助进行一些辅助功能的实现。VxWorks将在MUX接口下实现的网络设备驱动称为END驱动,以区别于早期版中的BSD驱动,即直接实现在BSD网络栈下的网络设备驱动。
网络传输方式下,由于需要对传输的数据经过分层次的特殊的封装,且需要对网络上两台主机之间通信的不稳定性进行处理,故网络设备并不工作在I/O子系统下,而是使用完全不同的另一种机制,包括应用层接口函数都是独立的一套函数集合。当然这种工作方式并非是VxWorks特有的,所有的操作系统对于网络设备编程都采用与其他设备(字符和块设备)不同的方式。当然所有的平台对于网络设备的处理都是基本一致的:应用层采用Socket编程接口,操作系统提供网络栈实现和网络设备驱动。