zoukankan      html  css  js  c++  java
  • Linux内核解析之标准I/O库

    当Linux创建一个进程时,会自动创建3个文件描述符0,1,2,分别对应标准输入,标准输出,错误输出。C库中与文件描述符对应的是文件指针。查看C库头文件stdio.h中的源码
    1. typedef struct _IO_FILE FILE; //文件流类型
    2. extern struct _IO_FILE *stdin; /* 标准输入流 */
    3. extern struct _IO_FILE *stdout; /* 标准输出流 */
    4. extern struct _IO_FILE *stderr; /* 错误流 */
    5. #ifdef __STDC__
    6. /* C89/C99 say they're macros. Make them happy. */
    7. #define stdin stdin
    8. #define stdout stdout
    9. #define stderr stderr
    10. #endif
    从上面源码看stdin,stdout,stderr是文件流指针,看看stdin,stdout,stderr是如何定义的
    1. _IO_FILE *stdin = (FILE *) &_IO_2_1_stdin_;
    2. _IO_FILE *stdout = (FILE *) &_IO_2_1_stdout_;
    3. _IO_FILE *stderr = (FILE *) &_IO_2_1_stderr_;
    继续查看_IO_2_1_stdin_的定义
    1. DEF_STDFILE(_IO_2_1_stdin_, 0, 0, _IO_NO_WRITES);
    2. DEF_STDFILE(_IO_2_1_stdout_, 1, &_IO_2_1_stdin_, _IO_NO_READS);
    3. DEF_STDFILE(_IO_2_1_stderr_, 2, &_IO_2_1_stdout_, _IO_NO_READS+_IO_UNBUFFERED);
    DEF_STDFILE是一个宏定义,用于初始化C库中的FILE结构,_IO_2_1_stdin__IO_2_1_stdout__IO_2_1_stderr_分别用于0,1,2的初始化,这样c库的文件指针跟系统的文件描述符关联起来了,另外注意后面的标志位,stdin是不可写,stdout不可读,stderr不可读不可写没缓冲
    通过上面分析可以得知stdin,stdout,stderr是file类的文件指针


    I/O缓冲引出的有趣问题。
        C库的I/O接口对文件I/O进行了封装为了提高性能,其引入了缓存机制,共有3种缓存机制:全缓存,行缓存及无缓存
        全缓存一般用于访问真正的磁盘文件。C库为文件访问申请一块内存,只有当文件内容将缓存填满或者执行flush时,C库才会将缓存内容写入到内核中。
        行缓存一般用于访问终端,当遇到一个换行符时,就会引发真正的I/O操作。
        无缓存那就不用多说了


    C库的fopen用于打开文件,其内部实现必然要使用open系统调用。那么fopen的各个标志位又对应open的哪些标志位呢?请看表2-1。
     
    1. _IO_FILE *
    2. _IO_new_file_fopen (fp, filename, mode, is32not64)
    3. _IO_FILE *fp;
    4. const char *filename;
    5. const char *mode;
    6. int is32not64;
    7. {
    8. int oflags = 0, omode;
    9. int read_write;
    10. int oprot = 0666;
    11. int i;
    12. _IO_FILE *result;
    13. #ifdef _LIBC
    14. const char *cs;
    15. const char *last_recognized;
    16. #endif
    17. if (_IO_file_is_open (fp))
    18. return 0;
    19. switch (*mode)
    20. {
    21. case 'r':
    22. omode = O_RDONLY;
    23. read_write = _IO_NO_WRITES;
    24. break;
    25. case 'w':
    26. omode = O_WRONLY;
    27. oflags = O_CREAT|O_TRUNC;
    28. read_write = _IO_NO_READS;
    29. break;
    30. case 'a':
    31. omode = O_WRONLY;
    32. oflags = O_CREAT|O_APPEND;
    33. read_write = _IO_NO_READS|_IO_IS_APPENDING;
    34. break;
    35. default:
    36. __set_errno (EINVAL);
    37. return NULL;
    38. }
    39. #ifdef _LIBC
    40. last_recognized = mode;
    41. #endif
    42. for (i = 1; i < 7; ++i)
    43. {
    44. switch (*++mode)
    45. {
    46. case '':
    47. break;
    48. case '+':
    49. omode = O_RDWR;
    50. read_write &= _IO_IS_APPENDING;
    51. #ifdef _LIBC
    52. last_recognized = mode;
    53. #endif
    54. continue;
    55. case 'x':
    56. oflags |= O_EXCL;
    57. #ifdef _LIBC
    58. last_recognized = mode;
    59. #endif
    60. continue;
    61. case 'b':
    62. #ifdef _LIBC
    63. last_recognized = mode;
    64. #endif
    65. continue;
    66. case 'm':
    67. fp->_flags2 |= _IO_FLAGS2_MMAP;
    68. continue;
    69. case 'c':
    70. fp->_flags2 |= _IO_FLAGS2_NOTCANCEL;
    71. continue;
    72. case 'e':
    73. #ifdef O_CLOEXEC
    74. oflags |= O_CLOEXEC;
    75. #endif
    76. fp->_flags2 |= _IO_FLAGS2_CLOEXEC;
    77. continue;
    78. default:
    79. /* Ignore. */
    80. continue;
    81. }
    82. break;
    83. }
    84. result = _IO_file_open (fp, filename, omode|oflags, oprot, read_write,
    85. is32not64);


    fdopen与fileno
    Linux提供了文件描述符,而C库又提供了文件流。在平时的工作中,有时候需要在两者之间进行切换,因此C库提供了两个API:
    1. #include <stdio.h>
    2. FILE *fdopen(int fd, const char *mode);
    3. int fileno(FILE *stream);
    fdopen用于从文件描述fd中生成一个file指针,而fileno则用于从文件指针中得到对应的文件描述符

    查看fdopen的实现,其基本工作是创建一个新的文件流FILE,并建立文件流FILE与描述符的对应关系。我们以fileno的简单实现,来了解文件流FILE与文件描述符fd的关系。——因为该函数代码较长,在此就不罗列C库的代码了。代码如下:
    1. int fileno (_IO_FILE* fp)
    2. {
    3. CHECK_FILE (fp, EOF);
    4. if (!(fp->_flags & _IO_IS_FILEBUF) || _IO_fileno (fp) < 0)
    5. {
    6. __set_errno (EBADF);
    7. return -1;
    8. }
    9. return _IO_fileno (fp);
    10. }
    11. #define _IO_fileno(FP) ((FP)->_fileno)
    从fileno的实现基本上就可以得知文件流与文件描述符的对应关系。文件流FILE保存了文件描述符的值。当从文件流转换到文件描述符时,可以直接通过当前FILE保存的值_fileno得到fd。而从文件描述符转换到文件流时,C库返回的都是一个重新申请的文件流FILE,且这个FILE的_fileno保存了文件描述符。
    因此无论是fdopen还是fileno,关闭文件时,都要使用fclose来关闭文件,而不是用close。因为只有采用此方式,fclose作为C库函数,才会释放文件流FILE占用的内存。












  • 相关阅读:
    python 接口自动化测试搭建钉钉环境
    接口测试流程梳理
    兼容性测试
    软件测试流程
    软件测试---黑盒测试的测试用例的设计方法
    软件基础之-----测试的方法
    基于Selenium2+Java的UI自动化(8)- 显式等待和隐式等待
    基于Selenium2+Java的UI自动化(6)-操作Alert、confirm、prompt弹出框
    基于Selenium2+Java的UI自动化(5)
    基于Selenium2+Java的UI自动化(4)
  • 原文地址:https://www.cnblogs.com/zengyiwen/p/5755187.html
Copyright © 2011-2022 走看看