- VxWorks设备驱动开发详解
- 曹桂平等编著
- 3189字
- 2020-08-27 06:25:18
5.3 VxWorks内核驱动支持——“免费的午餐”
VxWorks除了提供I/O子系统对VxWorks下所有的设备类型(当然除了网络设备)进行管理外,其本身还提供一些驱动程序,这些驱动程序中有些作为驱动中间层(如TTY驱动、TFFS驱动、USB I/O层驱动),帮助简化底层硬件设备驱动的设计;有些则是直接的底层驱动,驱动一个虚拟设备工作。对于中间层驱动,我们在本书具体驱动章节中进行介绍,本节将介绍VxWorks内核提供的虚拟设备驱动。所谓虚拟设备,即完全使用软件进行模拟工作的设备,这些设备通过封装成特定的结构,完成特定的功能,大多建立在内存之上,用于数据的转换或者某种任务间通信机制。最常见且使用较多的有如下虚拟设备:管道设备(pipeDrv)、虚拟内存设备(memDrv)、RAM磁盘设备RamDisk(ramDrv)以及网络设备(netDrv)。
5.3.1 管道虚拟设备驱动支持
VxWorks下管道设备主要被用以任务间通信。管道虚拟设备直接工作在VxWorks I/O子系统的管理之下,管道设备在内核初始化过程中通过调用pipeDrv函数完成自身的初始化。pipeDrv函数在usrRoot中被调用,代码如下所示。
#ifdef INCLUDE_PIPES pipeDrv (); /* install pipe driver */ #endif /* INCLUDE_PIPES */
为了在VxWorks内核映像中包含管道虚拟设备组件,必须定义INCLUDE_PIPES宏。其中,pipeDrv函数完成管道虚拟设备相关资源的分配和内核结构的初始化,调用iosDrvInstall函数向I/O子系统注册管道虚拟设备驱动。
注意
pipeDrv函数并非进行管道虚拟设备的创建,故用户在使用管道虚拟设备之前还必须使用iosDevAdd函数首先创建一个管道设备。VxWorks内核为此提供了更高层次的接口函数pipeDevCreate,用户使用该函数完成管道设备的创建,其后就可以使用open函数打开该管道设备,进而进行读写、控制操作。
pipeDevCreate函数调用原型如下。
STATUS pipeDevCreate ( char *name, /* name of pipe to be created */ int nMessages, /* max. number of messages in pipe */ int nBytes /* size of each message */ );
pipeDevCreate函数参数中:
● 参数1(name):管道设备节点名。该名称在调用open函数将作为路径名使用用以打开该管道设备。
● 参数2(nMessages):创建的管道设备可存储的最大消息数量。
● 参数3(nBytes):每个消息的最大长度。
VxWorks下管道的底层实现基于消息队列,故此处参数2、参数3的含义同消息队列创建时输入的参数含义。这也是在管道创建时需要传入这两个参数的原因所在。
pipeDevCreate实现中最后将调用iosDevAdd函数将这个管道设备添加到I/O子系统中。pipeDevCreate函数调用之后,用户就可以使用调用pipeDevCreate函数传入的设备名作为路径调用open函数打开这个管道进行读写操作,代码示例如下。
void pipe_test(){ int pfd; //管道设备名为“/pipe/x”,最大消息数量为10,每个消息的最大长度为1024B。 pipeDevCreate("/pipe/x", 10, 1024); pfd = open("/pipe/x", O_WRONLY,0); if(pfd<0){ printf("open pipe error.\n"); return; } write(pfd, "hello,VxWorks.", 14); close(pfd); return; }
以上示例简单地演示了管道的使用方式,管道虚拟设备是VxWorks下任务间通信机制之一,使用较多,VxWorks通过底层驱动的方式以消息队列为基础模拟出一个信息通道,我们将其称为管道。
注意
若要在内核中添加管道设备支持,必须包含INCLUDE_PIPES宏定义,否则管道设备将不可用。
5.3.2 虚拟内存设备驱动支持
虚拟内存设备即将一段内存空间模拟成一个设备供用户进行读写。虚拟内存有些类似于管道,但是相比较VxWorks内核对管道的封装,内核对虚拟内存设备的处理则较为简单,实际上,它仅仅作为一个“设备”,内核没有对其进行任何封装。此类设备大多数时候被用做驱动程序设备设计的典型示例,因为其只需要一段可用的内存即可,无须任何实际设备的支持。
虚拟内存设备在内存中模拟出一个设备或者更确切地说是一个文件,用户在创建出一个虚拟内存设备后,可以使用open函数打开该设备,向该设备写入任何数据,这些数据被原封不动地写入表示设备的内存区域中,当然也可以将这个写入的内容重新读出来,其外在表现其实就是一个文件,可以移动文件偏移指针,读取固定偏移处的数据或向该处写入数据。从数据读写和存储方式来看,虚拟内存设备可以被作为最原始的任务间通信手段,不过数据的同步需要任务自身完成或者说给任务预留了很大的灵活性。
1.虚拟内存设备创建函数
虚拟内存设备并非仅用做设备驱动编写的教案,其具有一个很有用的使用场合:在下载型启动方式下,当VxWorks内核是压缩型映像时,虚拟内存设备就显得非常有用。我们将在其后代码示例中进行详细介绍,首先我们讨论一下虚拟内存设备的相关接口函数。
虚拟内存设备驱动并不作为VxWorks内核初始化的一个组成部分。只当用户需要使用虚拟内存设备提供的功能时,可以及时通过调用memDrv函数完成虚拟内存设备驱动的注册,该调用iosDrvInstall函数向VxWorks I/O子系统注册虚拟内存设备驱动。在完成驱动的注册后,在使用设备之前,我们还必须创建一个虚拟内存设备的节点,VxWorks为此提供了memDevCreate函数,该函数调用原型如下。
STATUS memDevCreate ( char * name, /* device name */ char * base, /* where to start in memory */ int length /* number of bytes */ );
参数说明:
● 参数1(name):虚拟内存设备节点名,也是open函数调用时使用的路径名。
● 参数2(base):虚拟内存设备所使用的内存基地址。
● 参数3(length):虚拟内存设备所使用的内存大小。
注意
用户在调用memDevCreate函数创建虚拟内存设备时必须自己指定该设备所用内存的基地址以及大小,不可以让memDevCreate自己负责分配,即参数2不可以为NULL。用户可以通过预留一段内存空间专门用做虚拟内存设备的使用空间。如果只在内核启动过程中使用内存设备,那么就可以直接使用任何空闲的内存空间。后文的内存设备使用示例中就是直接使用空闲内存空间作为内存设备使用。memDevCreate函数将最终调用iosDevAdd函数完成虚拟内存设备的添加。此时用户就可以使用open函数打开内存设备进行操作了。
2.虚拟内存设备使用实例
虚拟内存设备的使用场合不多,但是在某些情况下却很有用。下面是一种使用虚拟内存设备的情况,作为虚拟内存设备的使用示例。
下载启动方式中,由bootrom完成VxWorks内核映像的下载。一般情况下,这个被下载的VxWorks映像文件都是非压缩的,故通过bootrom直接下载到RAM_LOW_ADRS地址处即可。但是某些情况下我们会将映像文件进行压缩(注意,这个压缩是对映像文件的整体压缩,而不是指压缩版本的VxWorks映像,对于下载方式下的VxWorks映像,其只经过一次链接,即所有的代码都是非压缩的),这可能是对某种工作方式的试验,如先通过网络下载方式试验VxWorks映像压缩后的工作情况,进而压缩后的映像文件写入Flash,由bootrom从本地Flash中下载压缩映像启动。
注意
此处再次声明,上面所说的压缩生成非压缩版本的VxWorks内核映像文件后,在命令行下使用deflate工具对整个文件的压缩,而并非VxWorks内核映像中一部分是压缩的,另一部分是非压缩的。下面一段中的压缩也是指对映像文件调用deflate后得到的VxWorks映像。
下载启动方式下,VxWorks映像一般放置在主机服务器上,由目标机中的bootrom通过网络将这个映像下载到目标机RAM中(这个下载过程由bootLoadModule函数完成,相关内容在后面介绍),进而跳转到RAM中执行VxWorks内核映像,完成VxWorks操作系统的启动。
实际上,bootrom最后是调用内核函数bootLoadModule完成VxWorks内核映像的解析和下载工作的。bootLoadModule是由VxWorks内核提供的,其将根据VxWorks映像文件格式(ELF)调用具体的处理函数对文件进行解析,将文件中代码和数据通过网络直接从主机文件中复制到其链接地址处(即RAM_LOW_ADRS),最后返回入口地址。bootLoadModule调用原型如下:
STATUS bootLoadModule ( FAST int fd, /* fd from which to read module */ FUNCPTR *pEntry /* entry point of module */ );
通常情况下,我们传递一个非压缩的VxWorks内核映像文件的文件描述符和一个入口函数指针给bootLoadModule函数。对于非压缩版本的内核映像文件,我们直接通过网络方式打开主机上的VxWorks内核映像文件,将返回的文件句柄传递给bootLoadModule函数即可。然而对于压缩的VxWorks映像文件,我们就不能简单地通过网络打开主机上这个压缩的VxWorks映像文件,将返回的文件句柄传给bootLoadModule函数,我们首先需要将这个压缩版的VxWorks映像下载到本地(即目标机)内存中,对其解压缩,然后将解压缩后的映像封装成文件形式,对其调用open函数,再将这个open函数返回的文件句柄传入bootLoadModule函数才能达到目的,此处虚拟内存设备就起着将解压缩后的VxWorks映像的封装成文件形式的作用。
由于内核函数最终将VxWorks内核代码和数据复制到RAM_LOW_ADRS指向的内存区域,故解压缩后的VxWorks映像必须在另一个内存地址处,且不可与RAM_LOW_ADRS处内核映像结束地址(end变量指向)重合(否则会出现一些异常的问题)。我们可以将其放置在RAM_HIGH_ADRS处。而对于VxWorks映像压缩文件的下载地址,则可以放置在RAM_LOW_ADRS处。
总结一下:我们首先通过网络将VxWorks映像文件经过deflate函数压缩后的版本下载到RAM_LOW_ADRS地址处,然后调用inflate函数对压缩文件解压缩,解压到RAM_HIGH_ADRS地址处,然后使用虚拟内存设备将解压缩后位于RAM_HIGH_ADRS处的VxWorks映像封装成文件,对其调用open函数,此时返回的文件句柄就指向一个正常格式(指没有经过deflate处理)的VxWorks内核映像,将这个句柄传递给bootLoadModule函数,bootLoadModule函数将对其进行解析,将文件中的代码和数据复制到RAM_LOW_ADRS指向的地址处,这与正常情况下通过网络的方式复制代码和数据达到相同的效果,只不过此时打开的文件位于本地内存中,而通常打开的文件位于主机服务器上。
这种工作方式看似没有必要,不过确是实际中常用的一种方式。首先,VxWorks启动方式选择的是bootrom+VxWorks,VxWorks是非压缩版本的,即VxWorks内核映像的生成只经过一次链接过程。但是我们对整个VxWorks内核映像文件进行压缩,VxWorks工具箱专门提供了命令行方式下工作的deflate命令对文件进行压缩。可能某些读者有疑问:既然如此,为何不采用对VxWorks内核映像使用二次链接生成方式,这样也可以进行压缩。如果这样做,就只能采用ROM型启动方式,因为下载启动方式下,VxWorks自身无法完成解压缩工作,必须由bootrom完成,而对于二次链接方式生成的VxWorks内核映像,bootrom基本上是束手无策。故一般采用一次链接方式生成一个非压缩的映像文件,而后采用deflate工具对整个映像文件进行压缩。这样bootrom就可以使用inflate函数对整个文件进行解压缩。对整个文件进行压缩,并利用bootrom进行解压缩,采用bootrom+VxWorks启动方式的环境是:bootrom较小(500KB左右),故可以将其烧入平台EEPROM或者容量较小的Nor Flash中,VxWorks内核映像可能非常大(如40MB以上),即使采用二次链接也无法使用目标板上有限的Nor Flash资源(注意:ROM型VxWorks映像必须具备在线执行能力)的介质中。此种情况下,以上所介绍的方式就可以解决该问题,一般目标板上EEPROM(4MB左右)或者Nor Flash(16MB左右)资源有限,但是Nand Flash资源却十分丰富(可达256MB),此时可以将bootrom烧入EEPROM中,而将VxWorks写入Nand Flash中,由于嵌入式系统下Nand Flash大多需要同时用做平台文件系统存储,故需要尽量减小VxWorks内核映像的大小,一般都是对其调用deflate,对整个映像文件进行压缩。bootrom先从Nand Flash中读取VxWorks压缩文件,而后解压缩,最后还是需要通过调用bootLoadModule函数对解压后文件进行解析。故其中还是必须将解压后内存中内容封装成文件形式。
经过deflate压缩后的映像文件无论存在于何处(网络主机上或是本地Nand Flash中),bootrom首先将其下载到RAM_LOW_ADRS地址处,然后调用inflate函数进行解压缩,并将解压缩后的内容存放到RAM_HIGH_ADRS地址处。现在的问题就是这个解压缩后的内容仍然是一个文件的格式,有文件头、段头部、符号表等,我们要调用bootLoadModule函数对这个解压缩后的内容进行解析,但是bootLoadModule函数只接收文件描述符,故我们需要将RAM_HIGH_ADRS开始的内存空间封装成一个文件形式,然后对其进行打开操作,返回一个文件描述符,之后才能调用bootLoadModule函数完成解析。虚拟内存设备就是为此设计的。下面是利用虚拟内存设备将从RAM_HIGH_ADRS开始的内存空间封装成文件的代码。
printf("Load image … \n"); memDrv(); memDevCreate("/mem_vx", RAM_HIGH_ADRS, IMAGE_MAX_SIZE); if((fd=open("/mem_vx", O_RDONLY, 0))< 0){ printErr("Cannot open memory device. \n"); goto err_out; } if(bootLoadModule(fd, pEntry) != OK){ printErr("Error Loading Image: errno = %d.\n", errnoGet()); goto err_out; }
以上这段代码示例演示了虚拟内存设备的使用方法,也显示了虚拟内核设备在某些情况下十分有效。最后,bootLoadModule函数将对文件进行解析,将文件中的代码和数据段复制到RAM_LOW_ADRS地址处,并将pEntry初始化为指向sysInit函数,该函数是下载启动方式下VxWorks内核的入口函数。
5.3.3 ramDisk设备驱动支持
ramDisk设备也是基于内存的一种设备,但其与虚拟内存设备的使用场合大不相同。虚拟内存设备将一段内存空间封装成一个文件的形式;而ramDisk则在这段内存空间创建一个文件系统,如同基于硬盘的文件系统一样,可在其中建立目录和文件。在完成ramDisk文件系统的创建后,其操作方式完全等同于一般的基于硬盘的文件系统,但是ramDisk文件系统中所有的改变只存在于内存中。换句话说,一旦系统掉电,其中的内容将全部丢失。这在某些特殊情况下将十分有用,如某些高密级的应用中,可使用ramDisk保存核心参数,以文件系统方式进行操作,可以对各种参数分类保存,便于程序的使用。硬件设计上的某些特殊处理同时可以从硬件角度保证参数的安全性。
ramDisk设备驱动可在VxWorks操作系统初始化过程中进行注册,这个注册的任务由ramDrv函数完成。ramDrv在usrRoot中被调用,其调用环境如下。
#ifdef INCLUDE_RAMDRV ramDrv (); /* initialize ram disk driver */ #endif /* INCLUDE_RAMDRV */
故ramDisk组件的包含与否由INCLUDE_RAMDRV宏进行控制。
事实上,ramDrv函数是一个空实现,因为ramDisk并非直接由I/O子系统进行管理。ramDisk的使用方式是在一段内存空间创建一个文件系统,故ramDisk驱动工作在文件系统层之下。以常用的MS-DOS兼容型文件系统为例,ramDisk将通过dosFs层管理,ramDisk的使用方式与以上两种设备就有些差别。首先,我们需要调用ramDevCreate函数设置ramDisk所使用内存的某些参数。该函数调用原型如下。
BLK_DEV *ramDevCreate ( char *ramAddr, /* where it is in memory (0 = malloc) */ FAST int bytesPerBlk, /* number of bytes per block */ int blksPerTrack,/* number of blocks per track */ int nBlocks, /* number of blocks on this device */ int blkOffset /* no. of blks to skip at start of device */ );
ramDisk使用RAM内存模拟硬盘设备,故对内存参数的设置也是基于硬盘的一些参数。
参数说明如下:
● 参数1(ramAddr):ramDisk所使用的RAM内存基地址。以0参数传入时,表示由内核通过malloc函数调用分配内存,内存大小由参数2和参数4决定。
● 参数2(bytesPerBlk):指定每个块的大小。此处块的概念类似于硬盘中的扇区。
● 参数3(blksPerTrack):指定每道中的块数。此处道的概念类似于硬盘中的磁道。
● 参数4(nBlocks):总块数,可以理解为硬盘设备中的总扇区数。
● 参数5(blkOffset):ramDisk起始处跳过不使用(或用以特殊使用)的块数。该参数通常设置为0。
由于ramDisk并非由I/O子系统直接管理,而是在文件系统管理之下,故ramDrv函数并不调用iosDrvInstall函数向I/O子系统注册ramDisk驱动,ramDrv函数实际上是一个空实现。ramDisk的驱动实际上就是在ramDevCreate函数中完成注册的,注册的对象不是I/O子系统,而是文件系统。注册的驱动函数地址就被保存在ramDevCreate函数返回的BLK_DEV结构中。
这个BLK_DEV类型的返回值将被dosFsDevCreate函数用以创建一个文件系统设备,并同时将ramDisk驱动注册到文件系统中。
注意
在dosFsDevCreate函数中,块设备节点才被创建,此时块设备将如同其他设备一样通过iosDevAdd函数添加到系统设备列表中。用户每次对ramDisk中的文件进行操作时,请求首先通过I/O子系统传递给dosFs驱动层,然后通过dosFs驱动层传递给ramDisk驱动,完成请求的最终服务。
如下代码演示了ramDisk的使用方法。
BLK_DEV *pBlkDev; pBlkDev = ramDevCreate (0, 512, 400, 400, 0); if (dosFsDevCreate ("/ramdisk", pBlkDev, 20, NULL) == ERROR) { printErrno( ); }; /* 格式化。对于ramDisk,一般在创建后都需要进行格式化。*/ if (dosFsVolFormat ("/ramdisk", 2, NULL) == ERROR) { printErrno( ); }
如上代码中,ramDisk的内存大小为512×400B=200KB,且ramDevCreate的第一个参数为0,表示由内核调用malloc函数分配200KB的内存作为ramDisk使用。ramDevCreate函数返回一个BLK_DEV结构类型,这个结构将被用以调用dosFsDevCreate函数创建一个dosFS文件系统设备。所以事实上,这个ramDisk设备节点直到dosFsDevCreate函数才被创建。
注意
ramDevCreate并不创建设备节点,只是设定ramDisk所用内存的相关参数,初始化一个BLK_DEV结构,为dosFsDevCreate函数调用做准备。在dosFsDevCreate函数中才完成ramDisk设备节点的创建和ramDisk驱动向dosFs文件系统的注册。dosFsVolFormat用以格式化ramDisk内存区域,即在ramDisk设备上创建dosFs文件系统。
以上代码执行后,用户就可以在/ramdisk下创建目录和文件了,用户可以在Shell下使用如下命令对ramDisk进行试验。
-> devs -> cd "/ramdisk" -> mkdir "/test" -> copy "/tffs/VxWorks", "VxWorks" -> ls
如果一段内存区域之前已被用做ramDisk,而且已经创建过文件系统,现在需要重新挂载,那么此时就可以省去格式化这一步,而且必须省去,因为一旦进行格式化,所有的数据将丢失,重新挂载只是一种工作方式,一般情况下很少会使用,此处只给出相关代码,并做简单介绍。
pBlkDev = ramDevCreate (0xc0000, 512, 400, 400, 0); if (dosFsDevCreate ("/ramdisk", pBlkDev, 20, NULL) == ERROR) { printErrno( ); }
注意
ramDevCreate函数第一个参数必须是之前用做ramDisk内存空间的起始地址,这个地址可以是用户自行分配的,也可以是内核分配的(此时可以通过返回的BLK_DEV结构获取,实际上,中间还需要进行一次计算,因为BLK_DEV中并不保存这个地址,我们需要先从BLK_DEV得到RAM_DEV),ramDevCreate函数的后4个参数必须与之前调用ramDevCreate函数完全一致。
5.3.4 网络设备(netDrv)高层次驱动支持
1.netDrv驱动基本工作原理
网络设备高层次驱动支持通过RSH(Remote Shell protocol)或者FTP(File Transfer Protocol)协议将远程主机上的某个设备作为本地设备访问的一种方式,操作系统屏蔽了底层网络通信的所有细节,用户层通过标准的文件操作接口函数(open、read、write等)对远程主机上的文件进行打开、读写和控制。当使用RSH或者FTP打开远程主机上的一个设备时,整个文件将通过网络首先复制到本地RAM中,接下来将对本地RAM中的文件副本进行操作,最后关闭文件时,如果有修改,那么这个文件又将通过网络写回到远程主机上。由于操作系统将底层网络通信细节对用户进行了完全屏蔽,所以对于一个用户层应用而言,表象上就是在对一个本地文件进行操作。网络设备在开发阶段十分有用,我们可以将要测试的程序放置在远程主机上,通过网络设备的形式进行打开、测试、修改。由于这些程序的编译都是在远程主机上完成的,故操作上十分方便。
2.网络设备节点的创建
网络设备初始化在VxWorks内核初始化过程中完成,具体的是在usrNetInit函数(被usrRoot函数调用)中完成。用户必须定义INCLUDE_NET_INIT用以在初始化过程调用usrNetInit对整个网络栈进行初始化,同时必须定义INCLUDE_NET_REM_IO用以初始化网络设备。内核将根据bootline参数值确定远程主机IP、主机名,以及根据是否提供了密码决定是使用RSH还是FTP协议进行网络设备的创建。
网络设备节点名默认使用远程主机名,不过也进行了细微的修改,即在主机名后添加了一个冒号,假设主机为“vxw”,则网络设备节点名则为“vxw:”。
netDrv函数负责网络设备驱动的注册,其调用iosDrvInstall函数向I/O子系统注册网络设备驱动函数集合,为了使网络设备对用户可见,还必须调用netDevCreate函数创建一个网络设备节点。netDevCreate函数调用原型如下。
STATUS netDevCreate ( char *devName, /* name of device to create */ char *host, /* host this device will talk to */ int protocol /* remote file access protocol 0 = RSH, 1 = FTP */ );
上述代码中,
● 参数1(devName):网络设备节点名。
● 参数2(host):远程主机名。
● 参数3(protocol):访问协议设置。网络设备只支持两种访问协议,即RSH和FTP。
netDevCreate函数最后将调用iosDevAdd函数把这个网络设备添加到系统设备列表中。
usrNetInit函数在usrNetwork.c文件中定义,如下代码片段是该函数中用以初始化网络设备驱动层以及创建网络设备的过程。
#ifdef INCLUDE_NET_REM_IO if (netDrv () == ERROR) /* init remote file driver,注册网络设备驱动 */ NET_DIAG(("Error initializing netDrv; errno = 0x%x\n", errno)); else { sprintf (devName, "%s:", params.hostName); /* make dev name */ protocol = (params.passwd[0] == EOS) ? 0 : 1;/* pick protocol : 0-RSH, 1-FTP*/ /* create device */ //创建网络设备 if (netDevCreate (devName, params.hostName, protocol) == ERROR) NET_DIAG(("Error creating network device %s - errno = 0x%x\n", devName, errno)); else // ioDefPathSet函数将操作系统默认的根目录设置为网络设备。 if (ioDefPathSet (devName) == ERROR) /* set the current directory */ NET_DIAG(("Error setting default path to %s - errno= 0x%x\n", devName, errno)); } iam (params.usr, params.passwd); /* set the user id */ taskDelay (sysClkRateGet () / 4); /* 1/4 of a second */ #endif /* INCLUDE_NET_REM_IO */
“网络设备”这个词是从目标机的角度而言的,对于主机而言,其相当于FTP服务器某个用户的根目录。所以,网络设备是一个具有目录层次的类似于文件系统的块设备。实际上,读者可以将其等效为从目标机登录到一个FTP服务器上,所以通常的cd、ls、打开一个文件,以及读写文件等操作都可以使用网络设备。
我们将其称之为(网络)设备主要是因为从目标机操作系统来看,对其操作方式上就相当于对本地的一个(块)设备进行操作,然而本质上其底层驱动要比块设备驱动简单得多,netDrv仅仅对网络通信进行了封装,没有做任何优化。正如前文所述,对于netDrv机制下的网络设备,当对一个文件进行操作时,这个文件将首先从远程主机完全复制到本地RAM中,在文件关闭之前,所有的操作都是对本地这个副本进行的,最后当文件被关闭时,这个副本才被写回到远程主机上。底层实现上十分简单,仅仅对Socket通信进行了封装而已。虽然效率不高,但是对于用户而言,这种机制十分有用,在开发的早期基本上都在依赖网络设备进行程序的调试。
3.netDrv驱动中关键参数的获取
默认情况下,VxWorks内核启动过程中将根据bootline中的远程主机IP、远程主机名、用户名和密码等信息创建一个网络设备,并将操作系统根目录设置为指向这个网络设备,实际上就是将bootline中指定的用户在远程主机FTP(通常都是使用FTP)服务器上的根目录作为操作系统启动后的系统默认根目录。
注意
如果目标机VxWorks操作系统包含了FTP服务器,那么这个FTP服务器的根目录就是通过ioDefPathSet设置的路径。如果用户不进行改变,那么这个根目录就是网络设备。换句话说,就是远程主机FTP服务器上bootline中指定用户的根目录。如果用户需要将目标机上本地FTP服务器根目录作为本地文件系统中的某个目录,调用ioDefPathSet函数重新设置一下即可。
最后需要注意一点的是:网络设备与网络文件系统是两个完全不同的概念,VxWorks下的网络设备由netDrv负责驱动,其直接工作在I/O子系统下,较为简单;而网络文件系统则由nfsDrv驱动,其虽然也是直接工作在I/O子系统下,但是其底层使用网络文件系统协议,与netDrv的实现完全不同。由于网络文件系统需要远程主机对其进行配合,一般在嵌入式系统下使用较少,netDrv提供的功能基本可以满足网络传输文件的需要,且远程主机仅需要启动一个FTP服务器,即可完成目标机上网络设备的创建,使用较为方便。