美文网首页
Buffered I/O read & write

Buffered I/O read & write

作者: 无无吴 | 来源:发表于2019-08-13 10:42 被阅读0次

User-Buffered I/O

出于性能考虑,内核通过延迟写入、合并相邻的I/O请求和提前读取来在内部缓冲数据。通过不同的方式,用户缓冲也旨在改进性能。

Block Size

实际上,块的大小通常是512、1024、2048、4096或8192字节。
使用stat()系统调用或stat(1)命令可以很容易地计算出给定设备的块大小。
然而,通常不需要知道实际的块大小
最简单的选择是使用大缓冲区大小执行I/O,这是典型块大小的公共倍数。4096字节和8192字节都工作得很好。
但问题是程序很少用块来处理。程序处理字段、行和单个字符,而不是抽象如块。
User-Buffered I/O缩小了文件系统(以块形式)与应用程序(在其自身抽象)进行对话之间的差距。
可以在您自己的程序中手动实现用户缓冲。事实上,许多关键任务应用程序就是这样做的。然而,绝大多数程序都是standard I/O库(标准C库的一部分)或iostream库(标准C++库的一部分),它提供了一个健壮和有能力的用户缓冲解决方案。

Standard I/O

标准C库提供了标准的I/O库(通常简单地称为stdio)。

File Pointers

标准I/O例程不直接对文件描述符进行操作。相反,他们使用自己的唯一标识符,称为文件指针。在C库中,文件指针映射到文件描述符。文件指针由一个指向FILE的指针表示,该文件在<stdio.h>中定义。

Opening Files

#include <stdio.h>
FILE *fopen(const char *path, const char *mode);

Modes

mode参数描述如何打开给定文件。它是以下字符串之一:

  • r
    打开文件进行读取。流位于文件的开头。
  • r+
    打开文件进行读写。流位于文件的开头。
  • w
    打开文件进行写入。如果文件存在,则将其截断为零长度。如果文件不存在,则创建该文件。流位于文件的开头。
  • w+
    打开文件进行读写。如果文件存在,则将其截断为零长度。如果文件不存在,则创建该文件。流位于文件的开头。
  • a
    打开文件,以便以附加模式写入。如果文件不存在,则创建该文件。流位于文件的末尾。所有的写入都会附加到文件中。
  • a+
    以附加模式打开文件进行读取和写入。如果文件不存在,则会创建该文件。流位于文件末尾。所有写入都将附加到文件中。
    成功后,fopen()返回一个有效的文件指针。如果失败,则返回NULL并适当地设置errno。
FILE *stream;
stream = fopen("/etc/manifest", "r");
if(!stream)
      /*error*/

Opening a Stream via File Descriptor

#include <stdio.h>
FILE *fdopen(int fd, const char* mode);

mode与fopen()相同,并且必须与最初用于打开文件描述符的模式兼容。
模式w和w+是特殊的,它们不会导致截断。流定位在与文件描述符相关联的文件位置。一旦文件描述符转换为 steam,I/O不应再直接在文件描述符上执行。然而,这样做是合法的。请注意,文件描述符没有被复制,仅关联了一个新的stream。 关闭流也将关闭文件描述符。
在成功时,fdopen()返回一个有效的文件指针;如果失败,则返回NULL并适当地设置errno。

int testfdopen() {
    FILE *stream;
    int fd;

    fd = open("../FileIO/testfile.txt", O_RDWR);
    if(fd == -1){
        perror("open");
        return -1;
    }
    stream = fdopen(fd, "w+");
    if(!stream){
        perror("fdopen");
        return -2;
    }

    fclose(stream);
    return 0;
}

int main()
{
    testfdopen();
    return 0;
}
果然没有截断

Closing Stream

#include <stdio.h>
int fclose(FILE *stream);

首先刷新任何缓冲且尚未写入的数据。成功后,fclose()返回0。失败时,它返回EOF并相应地设置错误号。

Closing All Streams

函数关闭与当前进程关联的所有流,包括标准输入、标准输出和标准错误:

#define _GNU_SOURCE
#include <stdio.h>
int fcloseall(void);

在关闭之前,所有的流都会被冲洗。函数总是返回0;它是特定于Linux的。

Reading from a Stream

Reading a Character at a Time

#include <stdio.h>
int  fgetc(FILE *stream);
int c;
c = fgetc(stream)
if(c == EOF)
    /* error */
else
    printf("c=%c\n", (char)c);

Putting the character back

#include <stdio.h>
int ungetc(int c, FILE *stream);
int testungetc() {
    FILE *stream = fopen("test.txt", "r");
    if(!stream){
        perror("fopen");
        return -1;
    }
    int ret;
    ret = fgetc(stream);
    if(ret == EOF){
        perror("fgetc");
        return -1;
    }
    printf("ret = %c\n", (char)ret);

    ret = fgetc(stream);
    if(ret == EOF){
        perror("fgetc");
        return -1;
    }
    printf("ret = %c\n", (char)ret);

    ret = ungetc(ret, stream);
    if(ret == EOF){
        perror("ungetc");
        return -1;
    }
    printf("ret = %c\n", (char)ret);

    ret = fgetc(stream);
    if(ret == EOF){
        perror("fgetc");
        return -1;
    }
    printf("ret = %c\n", (char)ret);


    fclose(stream);

    return 0;
}

int main()
{
    testungetc();
    return 0;
}
测试结果

Reading an Entire Line

#include <stdio.h>
char * fgets(char *str, int size, FILE *stream);
char buf[LINE_MAX];
if(!fgets(buf, LINE_MAX, stram))
  /*error*/

POSIX defines LINE_MAX in <limits.h>

int testfgets() {
    FILE *stream = fopen("aaaa.txt", "r");
    char buf[LINE_MAX] = {0};

    if(!fgets(buf, LINE_MAX, stream)){
        perror("fgets - 1");
        return -1;
    }
    printf("1 - %s\n", buf);

    if(!fgets(buf, LINE_MAX, stream)){
        perror("fgets - 2");
        return -1;
    }
    printf("2 - %s\n", buf);
    fclose(stream);
    return 0;
}

int main()
{
    testfgets();
    return 0;
}
测试结果
int testfgets() {
    FILE *stream = fopen("aaaa.txt", "r");
    while(1) {
        char buf[LINE_MAX] = {0};
        if (!fgets(buf, LINE_MAX, stream)) {
            perror("fgets - 1");
            break;
        }
        printf("%s", buf);
    }
    return 0;
}

int main()
{
    testfgets();
    return 0;
}
测试结果

上述测试表明:读取的最后一个字节后,将一个空字符(\0)存储在缓冲区中。因此printf打印室不用再加上\n,不然会多一个空行。

Reading arbitrary strings

char *s;
int c = 0; s = str;
while (--n > 0 && (c = fgetc (stream)) != EOF && (*s++ = c) != d) ;
if (c == d)
    *--s = '\0';
else
    *s = '\0';

将d设置为\n将提供类似于fget()的行为,减去在缓冲区中存储换行符。

Reading Binary Data

对于某些应用程序,读取单个字符或行是不够的。有时,开发人员希望读写复杂的二进制数据,例如C结构体。为此,standard I/O提供fread():

#include <stdio.h>
size_t fread(void *buf, size_t size, size_t nr, FILE *stream);

对fread()的调用将读取nr个数据的元素,每个大小size字节。
读取的元素数(而不是读取的字节数)会return。
该函数通过小于nr的返回值指示故障或EOF。不幸的是,如果不使用ferror()和feof(),就不可能知道这两种情况中的哪一种。

char buf[64];
size_t nr;
nr = fread (buf, 1, sizeof(buf), stream);
if (nr == 0)
    /* error */
int testfread() {
    FILE *stream = fopen("aaaa.txt", "r");
    char buf[64] = {0};
    size_t nr;
    nr = fread(buf, 1, sizeof(buf), stream);
    printf("nr: %d\n", nr);
    printf("%s", buf);
    fclose(stream);
    return 0;
}

int main()
{
    testfread();
    return 0;
}
测试结果1
int testfread() {
    FILE *stream = fopen("aaaa.txt", "r");
    char buf[64] = {0};
    size_t nr;
    nr = fread(buf, 3, sizeof(buf), stream);
    printf("nr: %d\n", nr);
    printf("%s", buf);
    fclose(stream);
    return 0;
}

int main()
{
    testfread();
    return 0;
}
测试结果2

从上面两个测试可以发现 改变size的大小,返回的nr也会对应改变,这个size就是指每次读取的自己长度。

Writing to a Stream

Writing a Single Character

#include <stdio.h>
int fputc (int c, FILE *stream);

成功完成后,函数返回c。否则,返回EOF,并适当地设置errno。

if (fputc ('p', stream) == EOF)
  /* error */

Writing s String of Characters

#include <stdio.h>
int fputs (const char *str, FILE *stream);

在成功时,fpust()返回一个非负数。如果失败,则返回EOF。

FILE *stream;
stream = fopen ("journal.txt", "a");
if (!stream)
    /* error */
if (fputs ("The ship is made of wood.\n", stream) == EOF)
    /* error */
if (fclose (stream) == EOF)
    /* error */

Writing Binary Data

#include <stdio.h>
size_t fwrite (void *buf, size_t size, size_t nr, FILE *stream);

Buffered I/0 样例

int testBufferedIO() {
    FILE *in, *out;
    struct pirate{
        char name[100];
        unsigned long booty;
        unsigned long beard_len;
    }p, blackbeard = {"Edward Teach", 950, 48};

    out = fopen("data", "w");
    if(!out){
        perror("fopen");
        return 1;
    }
    if(!fwrite(&blackbeard,
            1, sizeof(blackbeard), out)){
        perror("fwrite");
        return 1;
    }

    if (fclose (out)) {
        perror ("fclose");
        return 1; }
    in = fopen ("data", "r");
    if (!in) {
        perror ("fopen");
        return 1; }
    if (!fread (&p, 1, sizeof(struct pirate), in)) {
        perror ("fread");
        return 1;
    }
    if (fclose (in)) {
        perror ("fclose");
        return 1;
    }
    printf ("name=\"%s\" booty=%lu beard_len=%u\n", p.name, p.booty, p.beard_len);
    return 0;
    
}

int main()
{
    testBufferedIO();
    return 0;
}
测试结果

相关文章

  • Buffered I/O read & write

    User-Buffered I/O 出于性能考虑,内核通过延迟写入、合并相邻的I/O请求和提前读取来在内部缓冲数据...

  • File I/O read() and write()

    通过read()来读取 读取成功,返回写入BUF的字节数。读取失败,返回-1并且设置errno。如果fd表示的对象...

  • 聊聊面试中的IO

    [toc] I/O模型 阻塞I/O 最常见的I/O模型,默认情况下通过read或者write等系统调用读写文件或者...

  • 标准io和文件io,套接字高级io

    概念 文件I/O称之为不带缓存的IO(unbuffered I/O)。不带缓存指的是每个read,write都调用...

  • I/O 性能问题定位与优化

    I/O问题定位 I/O性能优化 应用程序优化 使用 mmap 代替 read/write,减少内存拷贝次数 需要同...

  • I/O多路复用

    背景 阻塞IO操作通常IO操作(比如read和write)都是阻塞I/O的,当调用read时,如果没有数据收到,线...

  • Linux系统编程11:I/O复用

    0. 背景 阻塞IO操作通常IO操作(比如read和write)都是阻塞I/O的,当调用read时,如果没有数据收...

  • linux - C 中的头文件

    ①unistd.h 类Unix中系统调用的封装,如fork,pipe,以及各种I/O read,write,clo...

  • 1.文件IO

    文件IO Unix系统中的文件I/O通常只用到5个函数:open、read、write、lseek和close。这...

  • I love this realistic world

    Why I write this article is because I read a book written...

网友评论

      本文标题:Buffered I/O read & write

      本文链接:https://www.haomeiwen.com/subject/hubmjctx.html