Windows下,实现不使用缓存的文件读写方式

来源:互联网 发布:热血仙境源码一键端 编辑:程序博客网 时间:2024/04/29 06:33

最近的项目是单片机上的COS开发,COS需要虚拟出几个文件,让开发板通过USB连接PC时,PC能当普通U盘读写。而COS在接到USB I/O中断时,从总线地址上读取数据、或者是把数据写回总线地址,供PC读取。

开始的时候,PC端读写文件才用C运行库提供的fopen()/fread()/fwrite()/fclose()这组函数,但是COS端往往不能接受到中断,从而无法响应。考虑到可能是PC操作系统作了缓冲,fread()/fwrite()读写数据实际上是操作的内存,而不是硬件。于是开始寻找不使用缓冲的文件读写方式……

 

首先想要到的是使用库函数:

void setvbuf(FILE *stream,char *buf,int type,unsigned size);

来设置不使用缓存,参数type有下列值,按照函数说明,如果传入 _IONBF 即设置文件不设缓存。

type 值 含义  _IOFBF 文件全部缓冲,即缓冲区装满后,才能对文件读写  _IOLBF 文件行缓冲,即缓冲区接收到一个换行符时,才能对文件读写  _IONBF 文件不缓冲,此时忽略buf,size的值,直接读写文件,不再经过文件缓冲区缓冲

但是实际上没有任何效果,估计Windows在封装这些函数时作了优化,即任何时候都使用缓存。

 

接着网上看到很多网友提到,在Linux/Unix下,open()/read()/write()/close()这组函数是不带缓冲的。虽说这是Linux/Unix下的一组函数,但是VS2010的运行库也封装了同样的函数(头文件:..\VC\Include\io.h),于是赶紧尝试。然而结果还是一样的失望,依然没有硬件中断响应。

为了找到原因,只好单步跟踪VS2010运行库的源代码,看这两组函数在Windows下到底是怎么封装。跟进去才发现,然后这两组库函数,到最终都是调用了Win32的文件API

CreateFile()ReadFile()WriteFile()CloseHandle()

虽然API CreateFile()带有参数可以控制是否带缓存,但是Windows在封装上面两组库函数时作了优化处理,忽略的这样的参数设置,也就是说在Windows下使用这两组库函数的话,都是带缓存的,去不掉!

 

最后看来只能直接使用Win32的API了,之前由于考虑到代码的跨平台,本想直接使用C的运行库函数,现在看来还得各个平台区分对待。我们先看看CreateFile()的定义:

HANDLE WINAPI CreateFile(  _In_      LPCTSTR lpFileName,  _In_      DWORD dwDesiredAccess,  _In_      DWORD dwShareMode,  _In_opt_  LPSECURITY_ATTRIBUTES lpSecurityAttributes,  _In_      DWORD dwCreationDisposition,  _In_      DWORD dwFlagsAndAttributes,  _In_opt_  HANDLE hTemplateFile);

其中参数dwFlagsAndAttributes有个选项为FILE_FLAG_NO_BUFFERING,其说明如下:

The file or device is being opened with no system caching for data reads and writes. This flag does not affect hard disk caching or memory mapped files.There are strict requirements for successfully working with files opened with CreateFile using the FILE_FLAG_NO_BUFFERING flag,

好了,就是它了!

于是赶紧把相关代码换成这套Win32的API,然后期待奇迹的出现……

然而……然而在打开文件之后,首先调用WriteFile()的时候返回出现“参数错误”的错误值!真是好事多磨,没办法,还是只能翻msdn。果然,在关于File Buffering的章节,有如下说明:

As previously discussed, an application must meet certain requirements when working with files opened with FILE_FLAG_NO_BUFFERING. The following specifics apply:•File access sizes, including the optional file offset in the OVERLAPPED structure, if specified, must be for a number of bytes that is an integer multiple of the volume sector size. For example, if the sector size is 512 bytes, an application can request reads and writes of 512, 1,024, 1,536, or 2,048 bytes, but not of 335, 981, or 7,171 bytes.•File access buffer addresses for read and write operations should be physical sector-aligned, which means aligned on addresses in memory that are integer multiples of the volume's physical sector size. Depending on the disk, this requirement may not be enforced.

原来没有了缓存,我们每次读写的数据长度就不是随意的了,需要为一个簇的大小的整数倍!而一般一个簇的大小为512bytes,所以我们这个时候要对数据补齐。也就说,如果我们的有效数据长度为200bytes,那么调用ReadFile()/WriteFile()的时候,需要指定长度为512;如果有效数据长度为600bytes,那么就应该是1024了。其实想想这也是情理之中的事情,因为没有了缓存处理,直接从硬件设备上读取数据,肯定是以簇为单位。

 好了,修正长度参数之后,一切如期望那么样,PC端每次读写文件,COS端都能收到中断了。最后总结两条:

1、Windows下使用不带缓存操作文件,只能使用CreateFile()带FILE_FLAG_NO_BUFFERING参数;

2、读写文件时,数据长度必须为512的整数倍。

 

原创粉丝点击