fflush(。。。)
程序代码:
大概总结一下:
标准库的IO操作分了两个缓冲区,一个是由操作系统之上的代码实现的一个字符数组,一个是window系统的缓存缓冲区。
刷新一个流的时候,先将字符数组缓冲区中的数据使用win32API函数WriteFile写到系统中,再利用win32API函数FlushFileBuffers刷新系统的缓存缓冲区。
刷新缓冲区要求写状态的流,不管什么流调用刷新操作都会将字符数组缓冲区清空。
如果流的状态为可读可写,那么他的状态会在 读 和 写 之间切换。
[ 本帖最后由 c10121209 于 2013-1-28 14:53 编辑 ]
程序代码:/***
*fflush.c - 刷新一个流的缓冲区
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
*目的:
* 定义 fflush() - 刷新一个流的缓冲区
* _flushall() - 刷新所有流的缓冲区
*
*******************************************************************************/
#include <sect_attribs.h>
#include <cruntime.h>
#include <stdio.h>
#include <file2.h>
#include <io.h>
#include <mtdll.h>
#include <internal.h>
/* 通过 flsall() 的值来区分 _flushall() 和
* fflush(NULL) 的调用行为
*/
#define FLUSHALL 1 //flush all
#define FFLUSHNULL 0 //fflush null
/* fflush(NULL) 和 flushall() 的核心程序
*/
static int __cdecl flsall(int);
/***
*int fflush(stream) - 刷新一个流的缓冲区
*
*目的:
* 如果文件以写和缓冲的方式打开, 刷新缓冲区. 如果以有问题的
* 方式刷新缓冲区, 设置流的状态为错误。
* Always flushes the stdio stream and forces a commit to disk if file
* was opened in commit mode.
*
*进入:
* FILE *stream - 将刷洗的流
*
*退出:
* 成功返回0, 失败或没有缓冲区
* 返回EOF and 如果失败设置文件错误标记.
* 影响 FILE 结构体的参数有: _ptr, _cnt, _flag.
*
*Exceptions:
*
*******************************************************************************/
int __cdecl fflush (
FILE *stream
)
{
int rc; //定义返回值
/* 如果参数为NULL,刷新所有的流
*/
if ( stream == NULL )
return(flsall(FFLUSHNULL));
//刷新单个流的操作
_lock_str(stream); //锁定流
__try {
rc = _fflush_nolock(stream); //具体操作
}
__finally {
_unlock_str(stream); //解锁流
}
return(rc);
}
/***
*_fflush_nolock() - 刷新在一个流上的缓冲区 (此时流已经被锁定)
*
*/
int __cdecl _fflush_nolock (
FILE *str
)
{
/* 如果参数为NULL,刷新所有的流。
*/
if ( str == NULL )
return(flsall(FFLUSHNULL));
if (_flush(str) != 0) {
/* 调用失败, 不要尝试提交 */
return(EOF); //返回EOF 说明操作失败了。
}
/* 底层io提交 to 确保数据写入到磁盘 */
if (str->_flag & _IOCOMMIT) {
return (_commit(_fileno(str)) ? EOF : 0);
}
return 0;
}
int __cdecl _flush ( //刷新单个流的操作
FILE *str
)
{
FILE *stream;
int rc = 0; /* 预设正确返回结果 */
int nchar;
/* 初始化指向流的指针 */
stream = str;
if ((stream->_flag & (_IOREAD | _IOWRT)) == _IOWRT && bigbuf(stream)
&& (nchar = (int)(stream->_ptr - stream->_base)) > 0)
/*这里做了三个判断:
1.判断流的状态只有可写一种,猜测:在可读可写的文件中文件的状态会在读和写之间切换。
2.bigbuf 不明白是什么意思,也许是说明这个文件有缓冲区。
3._base 指向缓冲区的头, _ptr 指向缓冲区的最后一个字符的下一位置。代表有待写的字符。*/
{
if ( _write(_fileno(stream), stream->_base, nchar) == nchar ) { //输出操作的底层原语
/* 如果这是一个可读可写文件, 清除它的写状态
* 使下一个操作能是一个读。
*/
if ( _IORW & stream->_flag )
stream->_flag &= ~_IOWRT;
}
else {
stream->_flag |= _IOERR; //出现错误了。
rc = EOF;
}
}
stream->_ptr = stream->_base; //不管读写操作是否成功都清空缓冲区。
stream->_cnt = 0;
return(rc);
}
// <fileno.c>
int __cdecl _fileno (
FILE *stream
)
{
_VALIDATE_RETURN((stream != NULL), EINVAL, -1); //检验流不是NULL
return( stream->_file ); //返回流的文件句柄
}
// end <fileno.c>
// <commit.c>
//这个函数负责提交操作系统中的缓冲数据
int __cdecl _commit (
int filedes
)
{
int retval; //定义返回值
/* 如果句柄值超出返回, 投诉 */
_CHECK_FH_RETURN( filedes, EBADF, -1 );
_VALIDATE_RETURN((filedes >= 0 && (unsigned)filedes < (unsigned)_nhandle), EBADF, -1);
_VALIDATE_RETURN((_osfile(filedes) & FOPEN), EBADF, -1); //一堆检验
_lock_fh(filedes); //锁定文件句柄
__try {
if (_osfile(filedes) & FOPEN) { //如果(if) filedes这个文件句柄已经打开(FOPEN)
if ( !FlushFileBuffers((HANDLE)_get_osfhandle(filedes)) ) { //_get_osfhandle(int) 获得文件的实际句柄 这里的句柄是封装的
//windows API: BOOL FlushFileBuffers(HANDLE h); 刷新文件的缓冲区并把缓冲区中的所有数据写入到文件。
//参数h,是一个已经打开了的文件的句柄,必须有可写权限。
//成功返回非0,失败返回0,错误信息由 GetLastError() 返回。
//摘录一段MSDN:
/*To open a file for unbuffered I/O, call the CreateFile function with the FILE_FLAG_NO_BUFFERING flag.
This prevents the file contents from being cached.
However, the file metadata may still be cached. To flush the metadata to a disk, use FlushFileBuffers*/
/*以没有缓冲的方式打开一个文件,在调用CreateFile时使用FILE_FLAG_NO_BUFFER标记
这会阻止文件内容存在缓存
无论如何,文件的元数据依然会写入缓存,刷新元数据到磁盘,依然用FlushFileBuffers*/
retval = GetLastError();
}
else {
retval = 0; /* 返回成功 */
}
/* map the OS return code to C errno value and return code */
if (retval == 0)
goto good; //成功后直接跳转
_doserrno = retval;
}
errno = EBADF;
retval = -1; //失败返回-1
_ASSERTE(("Invalid file descriptor. File possibly closed by a different thread",0));
good :
; } //这里有意思,接了一个空语句
__finally {
_unlock_fh(filedes); //解锁句柄
}
return (retval);
}
// end <commit.c>
/***
*int _flushall() - flush all output buffers
*
*Purpose:
* 刷新所有输出流的缓冲区, 清除多有输入流的缓冲区。.
*
*Entry:
* None.
*
*Exit:
* 返回打开的流的数量。
*
*Exceptions:
*
*******************************************************************************/
int __cdecl _flushall (
void
)
{
return(flsall(FLUSHALL));
}
/***
*static int flsall(flushflag) - flush all output buffers
*
*Purpose:
* Flushes all the output buffers to the file and, if FLUSHALL is passed,
* clears all input buffers. Core routine for both fflush(NULL) and
* flushall().
*
* 所线程注意: 所有线程锁的申请和释放都在这个程序里面执行.
*
*Entry:
* int flushflag - 这个参数决定被要求的语义, 有两个法定值: FLUSHALL 和 FFLUSHNULL
*
*Exit:
* if flushflag == FFLUSHNULL then flsbuf returns:
0, 如果成功
* EOF, 只要有一个失败。
*
* if flushflag == FLUSHALL 返回被成功刷新的流的数量
*
*Exceptions:
*
*******************************************************************************/
static int __cdecl flsall (
int flushflag
)
{
int i;
int count = 0;
int err = 0;
_mlock(_IOB_SCAN_LOCK); //锁定总表
__try {
for ( i = 0 ; i < _nstream ; i++ ) {
if ( (__piob[i] != NULL) && (inuse((FILE *)__piob[i])) ) { //__piob可能指向FILE或_FILEX
/*
* 锁定流. 直到确定流在使用中才做这个操作来避免创建多余的线程锁
* 代价就是在流被锁定后不得不再测试一次。
*/
_lock_str2(i, __piob[i]); //锁定流
__try {
if ( inuse((FILE *)__piob[i]) ) { //再次测试,因为这个流可能在锁定之前的瞬间被关闭。
if ( flushflag == FLUSHALL ) { //这个模式下任何状态的流都会被调用刷新操作
if ( _fflush_nolock(__piob[i]) != EOF )
count++; //成功后的计数
}
else if ( (flushflag == FFLUSHNULL) &&
(((FILE *)__piob[i])->_flag & _IOWRT) ) { //只对有写状态的流才会调用刷新操作
if ( _fflush_nolock(__piob[i]) == EOF )
err = EOF; //有一个失败就返回错误。
}
}
}
__finally {
_unlock_str2(i, __piob[i]); //解锁流
}
}
}
}
__finally {
_munlock(_IOB_SCAN_LOCK); //解锁总表
}
if ( flushflag == FLUSHALL ) //跟据情况返回。
return(count);
else
return(err);
}大概总结一下:
标准库的IO操作分了两个缓冲区,一个是由操作系统之上的代码实现的一个字符数组,一个是window系统的缓存缓冲区。
刷新一个流的时候,先将字符数组缓冲区中的数据使用win32API函数WriteFile写到系统中,再利用win32API函数FlushFileBuffers刷新系统的缓存缓冲区。
刷新缓冲区要求写状态的流,不管什么流调用刷新操作都会将字符数组缓冲区清空。
如果流的状态为可读可写,那么他的状态会在 读 和 写 之间切换。
[ 本帖最后由 c10121209 于 2013-1-28 14:53 编辑 ]






