首页 > 代码库 > APUE 学习笔记(四) 标准I/O库

APUE 学习笔记(四) 标准I/O库

1.流与FILE对象

unix I/O系统调用都是针对文件描述符的
标准C的I/O函数都是针对流(文件指针)的,我们使用一个流与一个文件相关联
 
2.缓冲
标准I/O库提供缓冲的目的就是尽可能减少read和write系统调用的使用次数
标准I/O提供三种类型的缓冲:
(1) 全缓冲:在填满标准I/O缓冲区后才进行实际I/O操作,磁盘上的文件通常是全缓冲,第一次I/O操作时调用malloc获得需要使用的缓冲区
(2)行缓冲:输入输出遇到换行符时,标准I/O库执行I/O操作。涉及终端时(标准输入和标准输出)通常使用行缓冲
                 标准I/O库用来收集每一行的缓冲区的长度是固定的,只要填满了缓冲区,就一定会进行I/O操作
(3)不带缓冲:标准出错流stderr通常是不带缓冲的,这使得出错信息可以尽快显示出来
setbuf  setvbuf
 
使用标准I/O的一个优点就是无需考虑缓冲以及最佳I/O长度的选择
 
3.二进制I/O
(1)读或写一个二进制数组,例如,将一个浮点数组的第2-5个元素写至一个文件
float data[10];
fwrite(&data[2], sizeof(data[0]), 4, fp);

(2)读或写一个结构体

struct {
    short count;
    long  total;
    char  name[NAMESIZE];
} item;

fwrite(&item, sizeof(item), 1, fp);

 

4.测试I/O性能

  #include <unistd.h>
  #include <fcntl.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <assert.h>
  
  #define BUFSIZE 1
  //#define BUFSIZE 8
  //#define BUFSIZE 64
  //#define BUFSIZE 256
  //#define BUFSIZE 1024
  //#define BUFSIZE 4096
  //#define BUFSIZE 9182
  
int main(int argc, char* argv[])
  {
    char buf[BUFSIZE];
    int nbytes = 0;
  /* unix read version */
      /*
      while ((nbytes = read(STDIN_FILENO, buf, BUFSIZE)) > 0) {
          if (write(STDOUT_FILENO, buf, nbytes) != nbytes) {
              fprintf(stderr, "write error\n");
          }
      }
      if (nbytes< 0) {
          fprintf(stderr, "read error\n");
      }
      */
  
      /* std c I/O version */
      int c;
      while ((c = fgetc(stdin)) != EOF) {
          if (fputc(c, stdout) == EOF) {
              fprintf(stderr, "fputc error\n");
          }
      }
      if (ferror(stdin)) {
          fprintf(stderr, "fgetc error\n");
      }
  
      return 0;
  }

  

我们查看程序的测试文本: stat test.txt

得知:该测试文件为 259,546,640Byte, I/O块大小为 4096字节

BUFSIZE 用户CPU时间 系统CPU时间 时钟时间 系统调用数
1 30.302 555.702 588.962 259,546,640
8 3.672 71.468 75.741 32,443,330
64 0.460 9.165 9.877 4,055,417
256 0.108 2.600 3.011 1,013,854
1024 0.052 1.020 1.889 253,464
4096 0.016 0.512 1.649 63,366
9182 0.000 0.596 1.735 31,683
fgetc fputc 7.604 0.380 8.001  
 从上表可以看出:

(1) BUFSIZE直接决定了 系统调用数,所以使用unix I/O的关键在于选取最合适的缓冲区长度

(2)当BUFSIZE等于 文件 I/O块大小时,系统CPU时间出现最小值,继续增加缓冲区长度对此时间几乎没有影响

(3)fgetc fputc标准I/O自行管理缓冲区,但是其时间都大于 unix I/O最佳缓冲区时的时间值,使用标准I/O库时,系统会自动选取一个最佳I/O长度

一般依据文件的I/O块大小值,但是因为标准I/O的内部缓冲 和 unix I/O最佳缓冲区长度相等时,标准I/O库中有一个 外存文件==> FILE内部缓冲 ==> 应用缓冲,

而Unix I/O 是直接 外存文件 ==> 应用缓冲, 两者虽然系统调用数相同,但是 标准I/O多了缓冲的操作,所以效率较低