fread和flushall的问题

来源:互联网 发布:java判断密码是否正确 编辑:程序博客网 时间:2024/06/09 18:13

今天测试写过的一段程序,比较频繁的出现一个fread错误:

贴代码:

int sendChannelInfo(SOCKET_OBJ *sock)
{
//读取channel.xml文件,封装为多个包发送
xstring filename=ProgramListPath;
filename+=_T("\\channels.xml");
FILE *fp=_tfopen(filename.c_str(),_T("rb"));
if (fp==NULL)
   return -1;

fseek(fp,0,SEEK_END);
size_t size=ftell(fp);
log(msgERROR,_T("sendChannelInfo:文件channels.xml长度=%ld!"),size);   //logA
fseek(fp,0,SEEK_SET);

size_t count=1024;
size_t pos=0;
while(count!=0)
{
   BUFFER_OBJ *sendbuf=GetBufferObj();
   programlist_response* resp=(programlist_response*)sendbuf->buf;  
   //log(msgERROR,_T("sendChannelInfo:pos=%d,fp=%d!"),pos,ftell(fp));   //logB
   //fseek(fp,pos,SEEK_SET);
   count=fread(resp->programlist,sizeof(UINT8),1024,fp);
   if (count==0)
   {
    int err=GetLastError();
    if(size>pos+1024)
     log(msgERROR,_T("sendChannelInfo:打开读取数据失败,pos=%d,buf=%d!"),pos,sendbuf->buf);
    FreeBufferObj(sendbuf);
    fclose(fp);
    return 0;
   }
   else
   {
    。。。。。。
   }

   if (PostSend(sock, sendbuf) != NO_ERROR)
   {
    FreeBufferObj(sendbuf);
    log(msgERROR,_T("sendChannelInfo:列表投递发送失败!"));
    fclose(fp);
    return -1;
   }
}
return 0;
}

错误描述:读文件的时候,fopen正常,得到正确的文件指针,之后的若干次读操作正确,但是不确定性的出错。每次出错后,count=fread()一句读取文件失败,返回0。feof()返回非0值,表明文件已经结束。但是,之前根据ftell的结果,文件还没结束。

检查读取的内容,发现有时候缺文件中间部分,还有时候缺少文件结尾。

添加logB处的代码,发现ftell()==pos,但是如果没有后面一个fseek,出错;如果加上fseek,正确。

上述函数处于多线程环境,但是本身只会被一个线程调用执行,之后还有类似的调用多次,传输多个文件。


解决办法和原因:
在logB处添加fseek代码,设置文件指针.

没有详细的原因分析,也没有发现原因.

估计是程序其他部分的执行,使得系统的文件管理部分出错,或者是一些内存被错误修改,造成fp指针的一些参数出错,无法正确定位下次的读写位置.


原因分析:
后来逐行调试跟踪分析fp指针的变化,发现在log操作之后,结构体中的一个指针值发生变化。更进一步的跟踪显示,在日志模块使用了flushall() 函数,该函数强制刷新所有的缓冲区,导致fp使用的读缓冲区被刷新(作废)。当再次调用fread的时候,系统重新读取文件,从上次没有取到缓冲区的部分 开始读,使得文件中间部分缺失;当文件较小的时候,前次读取就已经到文件结尾,也就不会有新的数据到达,使得文件尾缺失。

推测:
1、flushall函数关系OS的硬盘缓存,使得写缓冲区写到硬盘,读缓冲区失效。
2、fread函数实质上是从OS的缓冲区读写内容,WindowsXP的默认缓冲区4KB。fread修改FILE指针,包含对OS缓冲区的偏移信息的修改。
3、OS读写硬盘,放到系统缓冲区,修改OS自己的IO数据结构。
4、flushall不会重置硬盘读写的位置信息,即不修改OS的IO数据结构;也不影响FILE指针,包括ftell操作。
5、fseek操作,重新设置硬盘读写的位置,修改OS的IO数据结构,影响FILE指针。
6、3和4隐含一个矛盾:OS读写文件依赖的是自己的IO数据结构,从上次缓冲区读之后的位置开始操作;但是ftell却指向应用程序实际读取到的缓冲区 位置。flushall之后,如4所分析,这两个数据都不变化。考虑继续读写的情况,由于缓冲区内容失效,OS会读取4096之后的内容,但是应用程序却 需要FILE指针指向的单元。这样,fread操作,读取到的内容就是OS新读取到缓冲区的内容。如果OS缓冲区和应用程序缓冲区大小不一样,这时候的 fread就返回一组错误的数据。

上述程序就隐藏于这样一个环境。第一次执行fread之后,OS读取了4096的内容,标记自己的读取位置在 POSITIONos=4096Byte,应用程序由于指定了读取的大小,只能读取1024Byte,POSTIONapp=1024。之后的 PostSend中包含一个log函数,打印日志信息,在调试状态下会自动执行flushall,刷新缓冲区。从而使得该处的读缓冲区失效。while循 环的第二次执行,fread,这时候OS缓冲区失效,系统会继续读取文件内容,根据之前的POSITIONos=4096,读取4096之后的内容放到缓 冲区。应用程序由于没读到之前的内容,会以为此时的数据仍然是POSTIONapp。这种情况下,根据ftell,或者记数得到的文件偏移信息出错,对方 就不能正确得到数据。

出错原因分析:

对缓冲机制和刷新机制还不清楚,没有深入了解flush对各个组件的影响。


自己遇到的问题:

上面的描述虽然对我的问题没有帮助,但是值得学习。

我的问题是读取一个40m的视频文件的时候,总是只能够独到及时kb大小的文件,打开文件使用的“r”模式,后来修改成“rb”模式后,一切正常。

fread函数真的有的研究。。。。。。

http://js2854.sinaapp.com/post/107


原创粉丝点击