| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 2519 人关注过本帖
标题:【求助】C标准库一书中的“原语”一词求解释【共同学习】
取消只看楼主 加入收藏
c10121209
Rank: 1
等 级:新手上路
帖 子:18
专家分:5
注 册:2013-1-25
收藏
得分:0 
fflush(。。。)
程序代码:
/***
*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 编辑 ]
2013-01-28 14:39
快速回复:【求助】C标准库一书中的“原语”一词求解释【共同学习】
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.101064 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved