| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 2519 人关注过本帖
标题:【求助】C标准库一书中的“原语”一词求解释【共同学习】
取消只看楼主 加入收藏
c10121209
Rank: 1
等 级:新手上路
帖 子:18
专家分:5
注 册:2013-1-25
结帖率:100%
收藏
 问题点数:0 回复次数:10 
【求助】C标准库一书中的“原语”一词求解释【共同学习】
最近再看《C标准库》这本书,感觉里面的知识真是晦涩难懂。
       我有一个想法,按照书中给出的代码把stdio这个库自己写一遍,加深理解。但是发现书中提到的底层原语一词不是很明白。

       我理解的“原语”是标准库中底层真正去执行某一个动作的方法,或者说是接口,那么这种原语在不同的平台上是否就代表了操作系统的API呢?
        怎么在自己写的库中调用这种原语,我用的是VS2008。如果哪位大神能给小弟提供个具体编写的方法(在我的编译器上),小弟跪谢了。

        百度百科中的原语解释:
        原语通常由若干条指令组成,用来实现某个特定的操作。通过一段不可分割的或不可中断的程序实现其功能。
        
        求大神给我讲讲stdio这个库 中的层次关系 和 与操作系统 间的联系。。。。。

        祝大家 新年快乐!
搜索更多相关主题的帖子: 百度百科 编译器 接口 
2013-01-25 11:23
c10121209
Rank: 1
等 级:新手上路
帖 子:18
专家分:5
注 册:2013-1-25
收藏
得分:0 
求顶啊,求大神啊。若小弟真的搞明白了,会以总结帖的形式发出,服务大众。。。。
2013-01-25 11:26
c10121209
Rank: 1
等 级:新手上路
帖 子:18
专家分:5
注 册:2013-1-25
收藏
得分:0 
看了一下午,发现我的编译器vs2008中,<io.h> 算是最接近底层的接口了,那么所谓的原语就是它了吧。
在fopen(...)这个方法中 我的编译器分别调用了 _getstream()<internal.h> 和 _open()<io.h> 来获取流和打开文件。
2013-01-25 14:56
c10121209
Rank: 1
等 级:新手上路
帖 子:18
专家分:5
注 册:2013-1-25
收藏
得分:0 
你加我吧 1144920065 我公司上班时间不让上秋秋。
2013-01-25 16:31
c10121209
Rank: 1
等 级:新手上路
帖 子:18
专家分:5
注 册:2013-1-25
收藏
得分:0 
程序代码:
/***
*stream.c - 查找一个未使用的流
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*目的:
*       定义 _getstream() - 查找一个未使用的流
*
*******************************************************************************/

#include <cruntime.h>
#include <windows.h>
#include <malloc.h>
#include <stdio.h>
#include <file2.h>
#include <internal.h>
#include <mtdll.h>
#include <dbgint.h>

/***
*FILE *_getstream() - 查找一个未使用的流
*
*目的:
*       查找一个未使用的流并使其对调用者有效。 此函数打算只在库内使用。
*
*进入:
*       无参数. 扫描 __piob[]
*
*退出:
*       返回指向空闲流的指针, 或者 NULL 如果都已被使用.
        一个流成为被指定的状态,如果调用者决定使用 r w r/w 中的任意模式。
*
*       [多线程主意: 如果找到一个空闲流, 它被已锁住的状态返回.  解锁这个流是调用者的责任.]
*
*Exceptions:
*
*******************************************************************************/

FILE* _getstream ()
{
    FILE* retval = NULL;                                                           //定义返回值
    int i;

    /* Get the iob[] scan lock */
    _mlock(_IOB_SCAN_LOCK);                                                        //<mtdll.h> void _lock(int); 请求线程锁 _IOB_SCAN_LOCK锁ID 用于扫描存储流的表
    __try {

        /*
        * 通过 __piob 表循环查找一个空闲流, 或一个NULL结果.
        */
        for ( i = 0 ; i < _nstream ; i++ ) {                                       //<internal.h> _nstream(_NSTREAM_  512) 打开文件的最大数量

            if ( __piob[i] != NULL ) {                                             //<internal.h> void** __piob; 指向保存FILE资源地址的表的表头指针 推测:__iob[]的类型为 FILE* __iob[_nstream];
                /*
                * 判断如果流未在使用中,则返回此流.
                */
                if ( !inuse( (FILE *)__piob[i] ) && !str_locked( (FILE *)__piob[i] ) ) {
                    //<file2.h> #define inuse(s) ((s)->_flag & (_IOREAD | _IOWTITE | _IORW))
                    //<file2.h> #define str_locked ((s)->_flag & (_IOLOCKED))
                    //若流的当前状态不是 只读 只写 可读可写 锁定 则表达式为真
                    /*
                    * Allocate the FILE lock, in case it hasn't already been
                    * allocated (only necessary for the first _IOB_ENTRIES
                    * locks, not including stdin/stdout/stderr).  Return
                    * failure if lock can't be allocated.
                    */
                    if ( i > 2 && i < _IOB_ENTRIES )                               //流的下标大于2,小于_IOB_ENTRIES(20)
                        if ( !_mtinitlocknum( _STREAM_LOCKS + i ) )                //<mtdll.h> int _mtinitlocknum(int); 给出空闲ID号,分配一个新的,非预先分配的线程锁。失败返回0。
//#define _STREAM_LOCKS   16      /* 流的线程锁起始ID */
//#define _LAST_STREAM_LOCK  (_STREAM_LOCKS+_IOB_ENTRIES-1) /* 最后一个线程锁ID */
//#define _TOTAL_LOCKS        (_LAST_STREAM_LOCK+1)
                            break;

                    _lock_str2(i, __piob[i]);                                      //<mtdll.h> void _lock_file2(int, void*); 用新分配的线程锁_STREAM_LOCKS + i锁定文件__piob[i],在他的内部调用的是_lock。

                    if ( inuse( (FILE *)__piob[i] ) ) {
                        _unlock_str2(i, __piob[i]);                                //<mtdll.h> void _unlock_file2(int, void*); 解锁文件,内部调用_unlock。
                        continue;                                                  //若文件已经在使用中,则接着扫描。(不明其用意)
                    }
                    retval = (FILE *)__piob[i];                                    //赋值给返回值
                    break;
                }
            }
            else {                                                                 //预先准备的流资源数量为20,超过20执行下面的操作。
                /*
                * 将_piob[i]设置为新分配的_FILEX资源, 并返回指向它的指针.
                * 下面的操作进入windows底层。C标准规定同时打开文件的数量至少为8,
                * 如果打开超过20就要依靠window底层API调用,那么就没有可移植性了。
                */
                if ( (__piob[i] = _malloc_crt( sizeof(_FILEX) )) != NULL ) {

                    if ( !__crtInitCritSecAndSpinCount(
                        &(((_FILEX *)__piob[i])->lock), _CRT_SPINCOUNT ))
                    {
                        /*
                        * Failed to initialize the critical section because
                        * of lack of memory, clean up and return failure.
                        */
                        _free_crt( __piob[i] );
                        __piob[i] = NULL;
                        break;
                    }

                    EnterCriticalSection( &(((_FILEX *)__piob[i])->lock) );
                    retval = (FILE *)__piob[i];
                    retval->_flag = 0;
                }

                break;
            }
        }

        /*
        * 配置这个返回流的信息。.
        */
        if ( retval != NULL ) {
            /* 确保 _IOLOCKED 是预制的并且其他状态位为0 */
            retval->_flag &= _IOLOCKED;
            retval->_cnt = 0;
            retval->_tmpfname = retval->_ptr = retval->_base = NULL;
            retval->_file = -1;                                                        //文件描述符,此流未与任何的文件建立联系。
        }

    }
    __finally {
        _munlock(_IOB_SCAN_LOCK);                                                      //<mtdll.h> void _unlock(int); 释放线程锁 _IOB_SCAN_LOCK锁ID
    }

    return(retval);                                                                    //返回流资源的地址或NULL。
}
尝试解读了一遍_getstream(...)这个函数,求指正,看看我那里理解的不对。

如果自己实现这个方法,不知道线程锁那个地方有什么好的替换函数没有?

[ 本帖最后由 c10121209 于 2013-1-25 22:34 编辑 ]
2013-01-25 22:32
c10121209
Rank: 1
等 级:新手上路
帖 子:18
专家分:5
注 册:2013-1-25
收藏
得分:0 
当打开文件数量>=20,从 _iob[20] 中获得预先准备好的流资源。
当打开文件数量<20,则额外分配 _FILEX 结构体来产生新的流资源。
struct _FILEX
{
    FILE f;
    CRITICAL_SECTION lock;
};
_FILEX 封装了 "流f" 和 "临界区lock"。临界区用与流的线程保护。

找了一下临界区有关的API: <windows.h>

void InitializeCriticalSection(CRITICAL_SECTION*);      
初始化一个临界区。

void EnterCriticalSection(CRITICAL_SECTION*);
申请所有权,进入临界区。

void LeaveCriticalSection(CRITICAL_SECTION*);
移除所有权,离开临界区。

void DeleteCriticalSection(CRITICAL_SECTION*);
释放临界区资源。

临界区释放后不可以再以任何方法调用,也不可以释放一个被线程占用的临界区,会有错误产生。

推测:锁住 _iob[20] 用的是预先准备好临界区资源,而 _STREAM_LOCKS ~ _STREAM_LOCKS+19 就是它们在表中的ID了。
2013-01-26 10:02
c10121209
Rank: 1
等 级:新手上路
帖 子:18
专家分:5
注 册:2013-1-25
收藏
得分:0 
回复 9楼 meiyounichen
好啊,我已经加你了。
2013-01-26 15:58
c10121209
Rank: 1
等 级:新手上路
帖 子:18
专家分:5
注 册:2013-1-25
收藏
得分:0 
程序代码:
<fopen.c>  定义了 <stdio.h> 函数 fopen(...) {打开文件}

/***
*FILE *fopen(file, mode) - 打来文件
*
*目的:
*       打来文件指定一个流.  mode 决定了文件的打开方式和属性:
*       C标准提供的模式:
*        r 可读 w 清空后可写可建 a 不清空可写可建,写位置位于文件末尾。
*        r+ 不清空可读可写,写位置在文件开头。
*        w+ 清空后可读可写可建。
*        a+ 不清空可读可写可建,写位置位于文件末尾。
*        b 二进制文件模式。如果操作系统只有一种文件模式,加不加b都行。
*        第一个字母必须是r w a 之一,第二个字母是 + 或 b ,第三个字母是 + 或 b 或 没有。
*
*Entry:
*       char *file - 文件名或路径
*       char *mode - 文件访问模式
*
*Exit:
*       成功返回指向流的指针
*       失败返回NULL
*
*Exceptions:
*
*******************************************************************************/

FILE * __cdecl _tfopen (
        const _TSCHAR *file,
        const _TSCHAR *mode
        )
{
        return( _tfsopen(file, mode, _SH_DENYNO) );          //window实现的标准库中有一个fopen的安全版本,根据_tfsopen(...)的第三个参数决定使用的是哪个版本。
        //引用一下C语言的精神中的第一条“相信程序员”,觉得window的做法没多大意思。
}

//fopen(...) 的具体实现
FILE * __cdecl _tfsopen (
                         const _TSCHAR *file,
                         const _TSCHAR *mode
                         ,int shflag
                         )
{
    FILE *stream = NULL;                                      //接收流地址的变量
    FILE *retval = NULL;                                      //返回值

    _VALIDATE_RETURN((file != NULL), EINVAL, NULL);
    _VALIDATE_RETURN((mode != NULL), EINVAL, NULL);
    _VALIDATE_RETURN((*mode != _T('\0')), EINVAL, NULL);      //上面三个是对参数的合法性检验

    /* 获得一个空闲流 */
    /* [注意: _getstream() 返回一个锁住的流.] */

    if ((stream = _getstream()) == NULL)                      //获得流 从 _iob[] 中 或 新创建一个 _FILEX 资源
    {
        errno = EMFILE;                               //配置错误信息
        return(NULL);                                 //返回失败值
    }

    __try {  //异常处理块
        /* We deliberately don't hard-validate for emptry strings here. All other invalid
        path strings are treated as runtime errors by the inner code in _open and openfile.
        This is also the appropriate treatment here. Since fopen is the primary access point
        for file strings it might be subjected to direct user input and thus must be robust to
        that rather than aborting. The CRT and OS do not provide any other path validator (because
        WIN32 doesn't allow such things to exist in full generality).
        */
        if(*file==_T('\0'))                                   //文件名参数不允许为空
        {
            errno = EMFILE;                                   //配置错误信息
            return(NULL);                                     //返回失败值
        }

        /* 打开流,即将流与文件之间建立联系 */
#ifdef _UNICODE
        retval = _wopenfile(file,mode,shflag,stream);         //unicde字符集版本
#else  /* _UNICODE */
        retval = _openfile(file,mode,shflag,stream);          //ASCII字符集版本
#endif  /* _UNICODE */

        //_openfile(...)负责建立联系,打开文件,创建文件等的具体操作。调用成功返回与stream相同的值,失败返回NULL。

    }
    __finally {
        _unlock_str(stream);                                  //将流解锁,不管上一步是否成功,都将流解锁。
    }

    return(retval);                                           //返回
}

上面的_getstream(...) 确实有问题,我自己发现一个。
_iob[] 的元素类型就是 FILE。

欢迎大家指正。

接下来去解读_openfile(...)
闪了。
2013-01-26 16:01
c10121209
Rank: 1
等 级:新手上路
帖 子:18
专家分:5
注 册:2013-1-25
收藏
得分:0 
程序代码:
<_open.c>  

/***
*FILE *_openfile(filename, mode, shflag, stream) - 通过模式字符串和共享标志打开一个文件.
*
*目的:
*       解析字符串, 查看是否恰当的有 {rwa} 中的一个, 至多一个 '+',
*       至多一个 {tb}, 至多一个 {cn}, 至多一个 {SR}, 至多一个 'T', 至多一个 'D'.
*        通过shflag的结果.
*        如果权限允许的话以恰当的方式打开一个文件. 直到第一次io操作不分配缓冲区。
*        只建议在库内使用。
*
*进入:
*       char *filename - 要打开的文件
*       char *mode - 打开模式
*       int shflag - 文件的共享标志
*       FILE *stream - 使用文件的流
*
*退出:
*       配置流的参数, 引用系统文件的管理 通过流的调用。
*       返回 流 或 NULL
*
*Exceptions:
*
*******************************************************************************/

FILE * __cdecl __topenfile (
                            const _TSCHAR *filename,
                            REG3 const _TSCHAR *mode,
                            int shflag,
                            FILE *str
                            )
{
    int modeflag;
    int streamflag = _commode;                    //设置默认的流状态模式为未指派 int _commode = 0;
    int commodeset = 0;
    int scanset    = 0;
    int whileflag;
    int filedes;
    FILE *stream;
    BOOL encodingFlag = FALSE;

    _ASSERTE(filename != NULL);
    _ASSERTE(mode != NULL);
    _ASSERTE(str != NULL);                        //检验参数

    /*解析用户的打开模式配置下面两处的参数。
    (1) modeflag - 系统调用标志词
    (2) streamflag - 流句柄标志词. */

    /* 去掉前端的空格 */
    while (*mode == _T(' '))
    {
        ++mode;                                   //说明 fopen 第二个参数前面可以有空格出现。
    }

    /* 第一个字符必须是 'r', 'w', 或 'a'. */

    switch (*mode) {                                             //modeflag 的参数在 <fcntl.h> 中 , streamflag 的参数在 <stdio.h> 中
        case _T('r'):
            modeflag = _O_RDONLY;                                //文件只读
            streamflag |= _IOREAD;                               //流可读
            break;
        case _T('w'):
            modeflag = _O_WRONLY | _O_CREAT | _O_TRUNC;          //文件只写 | 文件可创建 | 文件清空 (根据后面的字符不同,含义可变)
            streamflag |= _IOWRT;                                //流可写
            break;
        case _T('a'):
            modeflag = _O_WRONLY | _O_CREAT | _O_APPEND;         //文件只写 | 文件可创建 | 文件末尾添加
            streamflag |= _IOWRT;                                //流可写
            break;
        default:
            _VALIDATE_RETURN(("Invalid file open mode",0), EINVAL, NULL);
    }

    /* 下面接可能有三组选项的模式字符:
    (1) A single '+' character,
    (2) One of 't' and 'b' and
    (3) One of 'c' and 'n'.
    */

    //标准C中只规定了 b 和 + 所以只考虑这两种情况。

    whileflag=1;                                                     //这个变量保证每个字符只能出现一次。

    while(*++mode && whileflag)
        switch(*mode) {

                case _T(' '):                                        //跳过空格
                    /* skip spaces */
                    break;

                case _T('+'):
                    if (modeflag & _O_RDWR)                          //如果包含可读可写的标志位,说明 + 已经出现过了,停止解析。
                        whileflag=0;
                    else {
                        modeflag |= _O_RDWR;                         //设置可读可写的标志位,清除读 或 写的标志位。
                        modeflag &= ~(_O_RDONLY | _O_WRONLY);
                        streamflag |= _IORW;
                        streamflag &= ~(_IOREAD | _IOWRT);
                    }
                    break;

                case _T('b'):                                        //设置二进制文件的标志位。
                    if (modeflag & (_O_TEXT | _O_BINARY))
                        whileflag=0;
                    else
                        modeflag |= _O_BINARY;
                    break;

                    //下面的忽略
                case _T('t'):
                    if (modeflag & (_O_TEXT | _O_BINARY))
                        whileflag=0;
                    else
                        modeflag |= _O_TEXT;
                    break;

                case _T('c'):
                    if (commodeset)
                        whileflag=0;
                    else {
                        commodeset = 1;
                        streamflag |= _IOCOMMIT;
                    }
                    break;

                case _T('n'):
                    if (commodeset)
                        whileflag=0;
                    else {
                        commodeset = 1;
                        streamflag &= ~_IOCOMMIT;
                    }
                    break;

                case _T('S'):
                    if (scanset)
                        whileflag=0;
                    else {
                        scanset = 1;
                        modeflag |= _O_SEQUENTIAL;
                    }
                    break;

                case _T('R'):
                    if (scanset)
                        whileflag=0;
                    else {
                        scanset = 1;
                        modeflag |= _O_RANDOM;
                    }
                    break;

                case _T('T'):
                    if (modeflag & _O_SHORT_LIVED)
                        whileflag=0;
                    else
                        modeflag |= _O_SHORT_LIVED;
                    break;

                case _T('D'):
                    if (modeflag & _O_TEMPORARY)
                        whileflag=0;
                    else
                        modeflag |= _O_TEMPORARY;
                    break;
                case _T('N'):
                    modeflag |= _O_NOINHERIT;
                    break;

                case _T(','):
                    encodingFlag = TRUE;
                    whileflag = 0;
                    break;


                default:
                    _VALIDATE_RETURN(("Invalid file open mode",0), EINVAL, NULL);
    }
    if (encodingFlag)
    {//这部分忽略
        static const _TSCHAR ccsField[] = _T("ccs");
        static const _TSCHAR utf8encoding[] = _T("UTF-8");
        static const _TSCHAR utf16encoding[] = _T("UTF-16LE");
        static const _TSCHAR unicodeencoding[] = _T("UNICODE");

        /* Skip spaces */
        while (*mode == _T(' '))
        {
            ++mode;
        }

        /*
        * The length that we want to compare is numbers of elements in
        * csField -1 since this number also contains NULL terminator
        */
        if (_tcsncmp(ccsField, mode, (_countof(ccsField))-1) != 0)
            _VALIDATE_RETURN(("Invalid file open mode",0), EINVAL, NULL);

        mode += _countof(ccsField)-1;

        /* Skip spaces */
        while (*mode == _T(' '))
        {
            ++mode;
        }

        /* Look for '=' */
        if (*mode != _T('='))
        {
            _VALIDATE_RETURN(("Invalid file open mode",0), EINVAL, NULL);
        }
        ++mode;

        /* Skip spaces */
        while (*mode == _T(' '))
        {
            ++mode;
        }

        if (_tcsnicmp(mode, utf8encoding, _countof(utf8encoding) - 1) == 0){
            mode += _countof(utf8encoding)-1;
            modeflag |= _O_U8TEXT;
        }
        else if (_tcsnicmp(mode, utf16encoding, _countof(utf16encoding) - 1) == 0) {
            mode += _countof(utf16encoding)-1;
            modeflag |= _O_U16TEXT;
        }
        else if (_tcsnicmp(mode, unicodeencoding, _countof(unicodeencoding) - 1) == 0) {
            mode += _countof(unicodeencoding)-1;
            modeflag |= _O_WTEXT;
        }
        else
            _VALIDATE_RETURN(("Invalid file open mode",0), EINVAL, NULL);

    }

    /* Skip trailing spaces */
    while (*mode == _T(' '))                                    //掉过空格
    {
        ++mode;
    }

    _VALIDATE_RETURN( (*mode == _T('\0')), EINVAL, NULL);       //保证 mode 中除了标准要求的没有其他别的字符了。

    /* 尝试打开文件.  注意如果没有b 或t, 就使用默认的文件模式. */

    if (_tsopen_s(&filedes, filename, modeflag, shflag, _S_IREAD | _S_IWRITE) != 0)    //打开文件的具体操作。成功返回0,失败返回-1.会返回文件描述符filedes
        return(NULL);

    /*配置流的基本信息 */
#ifndef CRTDLL
    _cflush++;  /* 促使 库 每次终止 程序(步骤) */                  //这个变量不清楚是干什么的,猜测和关闭程序时要关闭所有的文件这步操作有关。
#endif  /* CRTDLL */
    /* Init pointers */
    stream = str;                                                    //赋值给返回值

    stream->_flag = streamflag;                                      //设置流的状态标志参数
    stream->_cnt = 0;                                                //没弄清的参数
    stream->_tmpfname = stream->_base = stream->_ptr = NULL;         //一堆指针置为空

    stream->_file = filedes;                                         //文件的文件描述符

    return(stream);                                                  //返回
}
下面接着 _tsopen_s(...)

求指点 求意见 求笼罩。。。。
2013-01-26 17:38
c10121209
Rank: 1
等 级:新手上路
帖 子:18
专家分:5
注 册:2013-1-25
收藏
得分:0 
看了一下_tsopen_s(...)/* _sopen, _open */ 发现里面就是解析了打开文件模式等这些参数来配置调用CreateFile这个API的参数。
里面挺乱的,没怎么看全,就不贴出来了。

现在总结一下:

fopen(...) 先进入一个预先准备的表中去查找一个没有使用的流(FILE),若没有找到就使用一个用于扩充表的数据结构_FILEX来创建一个新的流,然后添加到表中。
在这个查表和对流的初始化的过程中要保证访问资源的所有权是唯一的。然后在解析打来模式等参数来配置CreateFile这个底层API的参数,调用它来打开或新建一个文件。
2013-01-27 12:00
快速回复:【求助】C标准库一书中的“原语”一词求解释【共同学习】
数据加载中...
 
   



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

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