基本概念

文件的概念

文件本质上都是储存在计算机磁盘(储存设备)中二进制0/1的数据,只不过根据这些二进制数据作用的不同,意义的不同,人为地对着这些数据进行了管理、抽象出一个个不同的文件类型

“文件”—抽象的概念—表述磁盘内容的

在Linux系统语境下,文件(file)一般有两个基本含义:

  • 狭义:指普通的文本文件,或二进制文件。包括日常所见的源代码、word文档、压缩包、图片、视频文件等等。

  • 广义:除了狭义上的文件外,几乎所有可操作的设备或接口都可视为文件。包括键盘、鼠标、硬盘、串口、触摸屏、显示器等,也包括网络通讯端口、进程间通讯管道等抽象概念。

在Linux操作系统中,基于以上概念: 一切皆文件!

普通磁盘数据可以抽象为文件管理

Linux设备驱动也可以抽象为文件管理

Linux系统中文件的分类

在Linux中,文件总共被分成了7种,他们分别是:

  1. 普通文件 - :存在于外部存储器中,用于存储普通数据。
  2. 目录文件 d:用于存放目录项,是文件系统管理的重要文件类型。
  3. 管道文件 p:一种用于进程间通信的特殊文件,也称为有名管道FIFO。
  4. 套接字文件 s:一种用于网络间通信的特殊文件。
  5. 链接文件 l:用于间接访问另外一个目标文件,相当于Windows快捷方式–软链接。
  6. 字符设备文件 c:字符设备在应用层的访问接口。
  7. 块设备文件 b:块设备在应用层的访问接口。

注意到,每个文件信息的最左边一栏,是各种文件类型的缩写,从上到下依次是:

  • -(regular)普通文件
  • d(directory)目录文件
  • p(pipe)管道文件(命名管道)
  • s(socket)套接字文件(Unix域/本地域套接字)
  • l(link)链接文件(软链接
  • c(character)字符设备文件
  • b(block)块设备文件

系统IO与标准IO

对文件的操作,基本上就是输入输出,因此也一般称为IO接口

操作系统的层面上,这一组专门针对文件的IO接口就被称为系统IO (system calls);

标准库的层面上,这一组专门针对文件的IO接口就被称为标准IO(library calls),

如下图所示:

man  2  查询对象  //Linux内核直接提供的函数接口库
man  3  查询对象  //上层的C库 或第三方库

  • 系统IO:是众多系统调用当中专用于文件操作的一部分接口。
  • 标准IO:是众多标准函数当中专用于文件操作的一部分接口。

从图中还能看到,标准IO实际上是对系统IO的封装,系统IO是更接近底层的接口。如果把系统IO比喻为菜市场,提供各式肉蛋菜果蔬,那么标准IO就是对这些基本原来的进一步封装,是品类和功能更加丰富的各类酒庄饭店。

如何选择系统IO与标准IO

  • 系统IO

    • 由操作系统直接提供的函数接口,特点是简洁,功能单一
    • 没有提供缓冲区,因此对海量数据的操作效率较低
    • 套接字Socket、设备文件的访问只能使用系统IO
      • Linux平台下因为把设备也抽象为文件管理 —只能用系统IO
  • 标准IO

    • 由标准C库提供的函数接口,特点是功能丰富
    • 有提供缓冲区,因此对海量数据的操作效率高
    • 编程开发中尽量选择标准IO,但许多场合只能用系统IO

总的来讲,这两组函数接口在实际编程开发中都经常会用到,都是基本开发技能。

系统IO

  1. 文件的打开与关闭
  • 打开文件

  • 关键点:

    • open函数有两个版本,一个有两个参数,一个有三个参数。
    • 当打开一个已存在的文件时,指定两个参数即可。
    • 创建一个新文件时,需要用第三个参数指定新文件的权限,否则新文件的权限是随机值
    • 模式flags,可以使用位或的方式,来同时指定多个模式。
    • 模式flags中,O_NOCTTY主要用在后台精灵进程,阻止这些精灵进程拥有控制终端。
int main(void)
{int fd;// 以下三种打开方式,都要求文件已存在,否则失败返回fd = open("a.txt", O_RDWR);   // 以可读可写方式打开fd = open("a.txt", O_RDONLY); // 以只读方式打开fd = open("a.txt", O_WRONLY); // 以只写方式打开// 1. 如果文件不存在,则创建该文件,并设置其权限为0644// 2. 如果文件已存在,则失败返回fd = open("a.txt", O_RDWR|O_CREAT|O_EXCL, 0644);   // 以可读可写方式创建文件fd = open("a.txt", O_RDONLY|O_CREAT|O_EXCL, 0644); // 以只读方式创建文件fd = open("a.txt", O_WRONLY|O_CREAT|O_EXCL, 0644); // 以只写方式创建文件// 1. 如果文件不存在,则创建该文件,并设置其权限为0644// 2. 如果文件已存在,则清空该文件的原有内容fd = open("a.txt", O_RDWR|O_CREAT|O_TRUNC, 0644);   // 以可读可写方式打开并清空文件fd = open("a.txt", O_RDONLY|O_CREAT|O_TRUNC, 0644); // 以只读方式打开并清空文件fd = open("a.txt", O_WRONLY|O_CREAT|O_TRUNC, 0644); // 以只写方式打开并清空文件// 以下三种打开方式,都要求文件已存在,否则失败返回--默认打开文件后文件光标在末尾fd = open("a.txt", O_RDWR|O_APPEND, 0644);   // 以可读可写方式追加文件内容fd = open("a.txt", O_WRONLY|O_APPEND, 0644); // 以只写方式追加文件内容
}

问题:

①使用open函数打开创建文件时,你指定权限是0777 但实际上生成的文件变为了0775

原因:存在权限掩码

umask
输出:0002

被创建的新文件的权限=(mode & ~umask)

mode == 0777

umask == 0002

777 &(~002) = 777 & 775 = 775

umask命令修改系统默认权限掩码

umask  新的掩码

同一个文件是可以以不同模式被多次打开的;

默认情况下,进程最多可以一次性打开1024个文件(包括stdin,stdout,stderr),文件描述符为0~1023,其中012是默认分给stdinstdoutstderr

  • 关闭文件

  • 关键点

    • 当不再使用一个文件时,应当关闭该文件,防止系统资源浪费。
    • 对同一文件重复执行关闭操作会失败返回,不会有其他副作用。
  1. 标准库函数的错误处理

在所有的库函数中,如果调用过程出错了,那么该函数除了会返回一个特定的数据来告诉用户调用失效之外,还都会去修改一个大家共同的全局错误码errno,我们可以通过这个错误码,来进一步确认究竟是什么错误。

例如:

#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h> // 全局错误码声明所在的头文件
int main()
{    int fd = open("a.txt", O_RDWR);    if(fd == -1)    {        // 以下两条语句效果完全一致:输出函数出错的原因        perror("打开a.txt失败");        printf("打开a.txt失败:%s\n", strerror(errno));    }    return 0;
}
  • 关键点:

    • 如果库函数、系统调用出错了,全局错误码 errno 会随之改变
    • 如果库函数、系统调用没出错,全局错误码 errno 不会改变
    • 一个库函数、系统调用出错后,若未及时处理错误码,则错误码可能会被随后的其他函数修改

提取错误码信息的两种办法:

1. 使用perror(),直接输出用户信息和错误信息:
if(open("a.txt", O_RDWR) == -1)
{    perror("打开a.txt失败");
} 2. 使用strerror(),返回错误信息交给用户自行处理:
if(open("a.txt", O_RDWR) == -1)
{    printf("打开a.txt失败:%s\n", strerror(errno));
}以上代码输出的结果是完全一样的:./a.out //运行程序
打开a.txt失败: No such file or directory //返回结果
打开a.txt失败: No such file or directory//返回结果一般而言,perror()用起来更加方便,但有时候需要使用strerror()来输出一些更加灵活的信息,比如以上代码,如果打开文件的名字不是固定a.txt,而是取决于外部参数,那么就应该写成:if(open("a.txt", O_RDWR) == -1)
{    printf("打开%s失败:%s\n", argv[1], strerror(errno));
}以上代码输出的结果是:
./a.out a.txt   //运行程序
打开a.txt失败: No such file or directory  //返回结果./a.out ELF-V4.tar.gz //运行程序
打开ELF-V4.tar.gz失败: No such file or directory //返回结果
  1. 文件描述符本质

函数 open() 的返回值,是一个整型 int 数据。这个整型数据,实际上是内核中的一个称为 fd_array数组的下标

task_struct{}是进程控制块PCB

文件描述符的本质内涵

打开文件时,内核产生一个指向 file{} 的指针,并将该指针放入一个位于 file_struct{} 中的数组 fd_array[] 中,而该指针所在数组的下标,就被 open() 返回给用户,用户把这个数组下标称为文件描述符,如上图所示。

结论:

  • 文件描述符从0开始,每打开一个文件,就产生一个新的文件描述符。
  • 可以重复打开同一个文件,每次打开文件都会使内核产生系列结构体,并得到不同的文件描述符
  • 由于系统在每个进程开始运行时,都默认打开了一次键盘两次屏幕,因此0、1、2描述符分别代表标准输入(stdin)、**标准输出(stdout)标准出错(stderr)**三个文件(两个硬件)。

0,1,2是一开始就默认打开的输入输出文件。

  1. 文件的读写操作

  • 关键点:

    • 参数count是读写字节数的愿望值,实际读写成功的字节数由返回值决定

    • 读取普通文件时,如果刚好读到了文件尾,已经没有数据可读时,read()会返回0

    • 读取管道文件时,如果管道中没有数据read()默认会阻塞

    • 读取文件内容:

      1. 将文件 a.txt 中的内容读出来,并显示到屏幕上
      int fd = open("a.txt", O_RDWR);
      char buf[100];
      int n;
      while(1)
      {    bzero(buf, 100);    n = read(fd, buf, 100); //直接获取100字节,除非遇到文件末尾,不会因为遇到换行符而结束读取// 每次最多读取100个字节     if(n == 0) // 读完退出        break;     printf("%s", buf);
      }
      close(fd);
      
      • 写入文件内容:
  • 2. 将键盘输入的内容,写入文件 a.txt
    int fd = open("a.txt", O_RDWR);char buf[100];
    bzero(buf, 100);// 从键盘输入数据
    fgets(buf, 100, stdin);// 将输入的数据写入文件
    write(fd, buf, strlen(buf));
    close(fd);
    
    1. 文件的读写位置
  • 当我们对文件进行读写操作时,系统会为我们记录操作的位置,以便于下次继续进行读写操作的时候,从记录的地方开始。

  • 有几点需要注意:

    • 每当open一个文件,系统就会维护一套包括文件操作位置在内的相关信息。
    • 同一个文件描述符进行读写操作时,使用的同一套文件信息,影响的是同一个位置参数
    • 同一个文件的多个不同的文件描述符进行读写操作时,使用的是不同的文件信息,影响的是不同的位置参数,彼此互相之间独立,这往往会导致文件信息的错乱。
  • 示例代码1:读写作用于同一个文件位置

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <strings.h>
int main(int argc, char **argv) // ./main a.txt
{int fd = open(argv[1], O_RDWR);char buf[100];// 读出1个字节,读完后读写操作位置是第2个字节bzero(buf, 100);read(fd, buf, 1);printf("%s\n", buf);// 写入2个字节,写完后读写操作位置是第4个字节bzero(buf, 100);write(fd, "xx", 2);// 读出3个字节,读完后读写操作位置是第7个字节bzero(buf, 100);read(fd, buf, 3);printf("%s\n", buf);return 0;
}

示例代码2:多次打开得到不同的文件描述符,各自读写操作位置独立

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <strings.h>int main(int argc, char **argv) // ./main a.txt
{// 假设文件中的原始内容是:abcdefghijkint fd1 = open(argv[1], O_RDWR);int fd2 = open(argv[1], O_RDWR);char buf[100];// 读出1个字节,读完后读写操作位置是第2个字节// 此时影响的是fd1,对fd2没有影响bzero(buf, 100);read(fd1, buf, 1);printf("%s\n", buf); // 输出a// 写入2个字节,读完后读写操作位置是第3个字节// 与上述读操作没有关系bzero(buf, 100);write(fd2, "xy", 2); // ab被覆盖,原文件变成xycdefghijk// 读出3个字节,读完后读写操作位置是第5个字节// 此时影响的是fd1,对fd2没有影响bzero(buf, 100);read(fd1, buf, 3);printf("%s\n", buf); // 输出ycdreturn 0;
}
  1. 读写位置的设置

对文件进行常规的读写操作的时候,系统会自动调整读写位置,以便于让我们顺利地顺序读写文件,但如果有需要,文件的读写位置是可以任意调整的,调整函数接口如下:

  • 关键点:

    1. lseek函数可以将文件位置调整到任意的位置,可以是已有数据的地方,也可以是未有数据的地方,假设调整到文件末尾之后的某个地方,那么文件将会形成所谓“空洞”。
    2. lseek函数只能对普通文件调整文件位置,不能对管道文件调整
    3. lseek函数的返回值是调整后的文件位置距离文件开头的偏移量,单位是字节。可以用来获取文件大小。

示例代码:

int main(void)
{// 假设文件 a.txt 只有一行// 内容如下:// 1234567890abcde int fd = open("a.txt", O_RDWR);// 读取前面10个阿拉伯数字:char buf[10];read(fd, buf, 10);// 将文件位置调整到'c'lseek(fd, 2, SEEK_CUR);// 将文件位置调整到'1'lseek(fd, 0, SEEK_SET);// 将文件位置调整到'a'lseek(fd, -5, SEEK_END);
}

文件空洞有什么用?

作用1:

提前告诉文件系统 ,我大概需要多大空间 ,提前占位 ,实际磁盘还未占用

百度网盘下载

迅雷下载

作用2:

共享文件/内存

不同程序打开同一个文件,文件描述符不一样,但指向的都是同一个文件同一块磁盘空间

事先使用文件空洞操作可以使共享文件空间占位更大,之后你的多个共享这个空间的程序

可以根据自己的拓展再实际的增加共享磁盘空间的 磁盘占用量

使用文件空洞操作可以让一个文件占位长度灵活变化,这样设置的共享文件空间就可以灵活的拓展

  1. dup() 与 dup2()
  • dup 是英语单词 duplicate 的缩写,意即“复制”。
  • 这两个函数功能类似,都是用来**“复制”文件描述符**,接口规范如下:

注意:文件描述符只是一个整数,当使用dup或dup2函数产生一个新的文件描述符时,它们是指向同一套资源的。这个新的文件描述符就类似旧的文件描述符的别名,当新描述符的位置指针变化时,旧文件描述符的位置指针也会变化;能达到一个文件共享的效果。

示例代码:

int main()
{// 打开文件 a.txt ,获得其文件描述符 fd1// 此处 fd1 就代表了这个文件及其配套的系统资源int fd1 = open("a.txt", O_RDWR);int fd2 = 0;int fd3 = 100;int fd4 = 0;// 复制文件描述符 fd1,默认得到最小未用的文件描述符fd2 = dup(fd1);//返回新的文件描述符,fd2为未被使用的最小文件描述符// 复制文件描述符 fd1,并指派为 100fd4 = dup2(fd1, fd3);//返回新的文件描述符,fd4 = fd3
}
//注意
使用dup或dup2时,获取的文件描述符是共用一套资源的。
  • 解析:

使用dup函数时,会自动分配当前未用的最小的文件描述符,如上述代码,由于进程默认打开了0、1、2作为标准输入输出,于是 fd1 就是3,新产生的文件描述符就是4,而 dup2 函数可以任意指定文件描述符的数值,如果指定的文件描述符已经有所指代,那么原先指代关系将会被替换。这种情况被称为“重定向”。

  1. mmap()

该函数全称是 memory map,意为内存映射,即将某个文件与某块内存关联起来,达到通过操作这块内存来间接操作其所对应的文件的效果

页地址:256或512 byte

  • 关键点:
    • mmap函数的flags参数是有很多的,上表只罗列了最简单的几个,详细信息请使用 man 手册进行查阅。
    • mmap函数理论上可以对任意文件进行内存映射,但通常用来映射一些比较特别的设备文件,比如液晶屏LCD。

注意:

在较旧的Linux内核(如2.6内核)中,可以直接使用mmap()来给LCD设备映射内存,但在较新Linux内核(如4.4内核)中,则需要经由DRM统一管理,不可直接mmap映射显存。

示例代码1,映射普通文件:

int main()
{// 以只读方式打开一个文件int fd = open("a.txt", O_RDWR);// 申请一块大小为1024字节的映射内存,并将之与文件fd相关联char *p = mmap(NULL, 1024, PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0);// 将该映射内存的内容打印出来(即其相关联文件fd的内容)printf("%s\n", p);// 通过操作内存,间接修改了文件内容p[0] = 'x';printf("%s\n", p);// 解除映射munmap(p, 1024);return 0;
}

注意几点:

  1. 使用 mmap 映射内存时,指定的读写权限必须是打开模式的子集
  2. 映射模式为 MAP_SHARED 时,修改的内容会影响文件
  3. 映射模式为 MAP_PRIVATE 时,修改的内容不会影响文件

标准IO函数

  1. 文件的打开与关闭

不管用系统IO函数还是标准IO函数,操作文件的第一步,都是“打开(open/fopen)”文件,需要注意:

  • 系统IO:打开文件得到的是一个整数 int ,称为文件描述符
  • 标准IO:打开文件得到的是一个*指针 FILE ,称为文件流指针

文件指针指向结构体 FILE,该结构体内部包含了文件描述符,如下图:

标准IO打开文件:

注意:

  • 总共有6中打开模式,不能自创别的模式,比如 “rw” 是非法的。

r: 文件必须存在

w:文件不存在则创建 存在则清空

a:文件不存在则创建 存在则将文件光标移到文件末尾

加上‘+’成为读写模式,其它方面不变

FILE *fp;
fp = fopen("./1.txt","r+");//fopen内部有使用malloc分配了一个FILE类型结构体的空间
...
fclose(fp);//里面有对fp指向的堆内存结构体空间做free()
//当重复关闭同一份文件时,会报重复free错误;而使用系统IO的close重复关闭文件时不会报错
char *p = malloc()

标准IO关闭文件:

注意:

close关闭文件是属于内核中的动作,可以重复关闭文件(具体为什么我也不清楚);

而fclose关闭文件还涉及到用户层的内存空间的释放,是不可以重复释放内存空间,所以不可以重复调用fclose关闭文件

  1. 文件内容的读写操作

标准IO跟系统IO不同的地方在于,标准IO的读写操作提供了很多不同的函数接口,以满足不同的需求:

(一)按单字节读写文本文件:

  • 关键点:

    • fgetc()与getc()功能完全一样,区别是fgetc是函数,而getc是宏
    • fputc()与putc()功能完全一样,区别是fputc是函数,而putc是宏
    • getchar()和putchar()只能针对键盘输入和屏幕输出,不能指定别的文件。

以上单字节读写函数中可以指定文件的情况

①指定标准输入文件stdin

输入缓冲区还没有内容 fgetc() getc() 会阻塞 等待输入缓冲区有内容

②指定的是一个普通文件

FILE * fp = fopen("./1.txt",“w+”);

fgetc(fp);//不论文件是否为空 直接返回 不阻塞 如果到文件末尾,直接返回EOF -1

③getchar()默认从输入缓冲区获取一个字符

(二)按行读写文本文件

feof(fp):判断是否到文件末尾

缺省就是默认的意思;

注意:这里的读取成功返回的是缓冲区的指针而不是获取或者写入的字节数

这里的写入函数成功返回非负整数,失败返回EOF;与常见的情况不同。

fgets()

①从输入缓冲区拿去数据 有两个结束阻塞的条件

  • 缓冲区内遇到了**’\n’换行符(这里空格**也会被读取,与scanf不同)

  • 获取到指定的size字节后,还没遇到’\n’也会退出

②fgets() 操作windows的文本文件和Linux文本文件时

  • windows的换行是**\r\n** 读取出来后只有\n 去掉了\r

  • Linux换行默认是只有**\n** 但是当读取windows的文本文件时,会把\r 看成普通字符一并

读取而不会去掉

gets()

gets() 默认从stdin文件获取的 这个函数不太安全

gets()和fgets()都能从缓冲区获取最后回车键按下的换行 ‘\n’,不过gets会将它丢掉,而fgets()会将它放到容器中所以此时的缓冲区已经没有换行符了

注意:

不论时fgets()还是gets() 从指定文件读取了行字符串后,都会在容器末尾加上’\0


  • 关键点:

    • 对于读操作而言,返回 EOF 意味着读操作失败,这有两种情况:
      1. 如果 feof(fp) 为真,此时意味着读到了文件末尾,没有数据可读了。
      2. 如果 ferror(fp) 为真,此时意味着遇到了错误。
    • 读操作函数接口的返回值是 int ,而不是 char ,原因是当读操作失败是返回的 EOF 的数值一般是 -1,而 char 型数据可能无法表达 -1。

fgets() 和 gets() 都是按行读取文件数据,他们的区别是:

  • fgets() 可以读取指定的任意文件,而 gets() 只能从键盘读取。
  • fgets() 有内存边界判断(即size,指定获取大小),而 gets() 没有(只有遇到了‘\n’才会结束本次获取,不然就一直读下去;这样容易导致获取的大小超过了用来储存的缓冲区),因此后者是不安全的,不建议使用。
  • fgets() 在任何情形下都按原样读取数据,但 gets() 会自动去除数据末尾的 ‘\n’

fputs() 和 puts() 都是按行将数据写入文件,他们的区别是:

  • fputs() 可以将数据写入指定的任意文件,而 puts() 只能将数据输出到屏幕。
  • fputs() 在任何情形下都按原样写入数据,但 puts() 会自动给写入数据的末尾加上 ‘\n

(三)按指定格式读写文本文件

关于sscanf() 格式化匹配内容

对于%s格式化获取一串数据中的字符串,会导致后续的文本格式化获取出现问题

sscaf("24,hello\0,a","%d,%s,%c",&buff,buff1,&buff2);
//只能匹配到前面%d和%s  第三个匹配不到
sscanf()和fscanf()两个格式化匹配获取数据函数,在使用%s格式获取数据时,要注意\0字符是%s获取的结束或是遇到\n结束,所以以后使用%s格式和其他的格式从文件或自定义缓冲区获取数据,要把%s放最后, 或单独使用%s获取
  • 注意:
  1. fprintf( )不仅可以像printf( )一样向标准输出设备输出信息,也可以向由stream指定的任何有相应权限的文件写入数据。
  2. sprintf()和snprintf()都是向一块自定义缓冲区写入数据,不同的是后者第二个参数提供了这块缓冲区的大小,避免缓冲区溢出,因此应尽量使用后者,放弃使用前者。
  3. fscanf( )不仅可以像scanf( )一样从标准输入设备读入信息,也可以从由stream指定的任何有相应权限的文件读入数据。
  4. sscanf( )从一块由s指定的自定义缓冲区中读入数据。
  5. 这些函数的读写都是带格式的,格式由下表规定:

(四)按数据块读写文件(能读写各种格式文件)

注意:

  • 以上两个函数既能处理文本,也能处理二进制文件
  • 返回值均是成功读写的“块”数,而非字节数
  1. 文件位置的获取与设定

与系统IO接口类似,标准IO也可以设置文件的读写位置、获取读写位置,只不过对于标准IO而言,这两个功能是分开实现的:

关键点:

  • 设置和获取的文件位置都是long类型。
  • 可以将文件位置设置到已有数据范围之外,形成“空洞”。
  • 在系统IO中可以使用lseek函数来获取文件大小,在标准IO中可以使用ftell函数获取文件大小。

标准IO缓冲区

标准IO实际上是系统IO的封装,这种封装体现如下图所示,fopen()函数将调用open()得到的文件描述符填入结构体 FILE 中,并为文件分配缓冲区、设置缓冲区类型,最后给用户返回指向 FILE 的指针 fp,称为文件指针:

如上图所示,每当使用标准IO的写操作函数,试图将数据写入文件 a.txt 时,数据都会流过缓冲区,然后再在适当的时刻冲洗(或称刷新,flush)到内核,最后才真正写入设备文件。

按照缓冲区什么时候冲洗数据到内核,可以将缓冲区分成以下三类:

  • 不缓冲类型

    • 一旦有数据,立刻将数据冲洗到文件
  • 全缓冲类型:

    • 一旦填满缓冲区,立刻将数据冲洗到文件
    • 程序正常退出时,立刻将数据冲洗到文件
    • 遇到 fflush() 强制冲洗时,立刻将数据冲洗到文件
    • 关闭文件时,立刻将数据冲洗到文件
    • 读取文件内容时,立刻将数据冲洗到文件
    • 改变缓冲区类型时,立刻将数据冲洗到文件
  • 行缓冲类型:

    • 同全缓冲类型
    • 一旦遇到**‘\n’**时,立刻将数据冲洗到文件

关键点:

  1. 缓冲(buffer)都是针对写操作而言的,缓冲的存在是为了提高写效率
  2. 对于标准输出而言,默认是行缓冲的;

对于标准出错而言,默认是不缓冲的;

对于普通文件而言,默认都是全缓冲类型

  1. 滞留在缓冲区中的数据有时被称为脏数据(dirty data),脏数据的存在代表程序操作的结果与文件真实状态不一致,若未正常冲洗这些脏数据就退出程序则有可能会造成数据丢失

  2. 这三种缓冲类型,可以通过函数 setbuf()/setvbuf() 来修改。

系统文件IO与标准文件IO相关推荐

  1. 网络IO和磁盘IO详解

    网络IO和磁盘IO详解 1. 缓存IO 缓存I/O又被称作标准I/O,大多数文件系统的默认I/O操作都是缓存I/O.在Linux的缓存I/O机制中,数据先从磁盘复制到内核空间的缓冲区,然后从内核空间缓 ...

  2. 【网络编程】同步IO、异步IO、阻塞IO、非阻塞IO

    IO分两阶段: 1.数据准备阶段:在该阶段,根据是否等待数据准备,将IO分成阻塞和非阻塞: 2.内核空间复制回用户进程缓冲区阶段:在该阶段,只要程序需要等待复制完成,才能往下运行(尽管这个时间很短), ...

  3. IO模式和IO多路复用

    前言 前天看redis相关的博文里面提到了epoll,就搜了一下,发现这篇文章 Linux IO模式及 select.poll.epoll详解,讲的很好,收获很大.这里根据自己的理解总结一下. IO模 ...

  4. 磁盘IO:缓存IO、直接IO、内存映射

    磁盘IO的几种访问方式如下: 缓存IO 缓存I/O又被称作标准I/O,大多数文件系统的默认I/O操作都是缓存I/O.在Linux的缓存I/O机制中,数据先从磁盘复制到内核空间的缓冲区,然后从内核空间缓 ...

  5. 【408预推免复习】操作系统之IO层次结构和IO控制方式

    [408&预推免复习]操作系统之IO层次结构和IO控制方式 文章目录 1 I/O设备 2 I/O系统的层次结构 3 I/O控制方式 4 IO设备分配与回收 文章目录 1 I/O设备 2 I/O ...

  6. Socket.IO 客户端 API IO

    IO 创建方式 <script src="/socket.io/socket.io.js"></script> <script>const so ...

  7. mysql io depth_MySQL读写IO的操作过程解析

    数据库作为存储系统,所有业务访问数据的操作都会转化为底层数据库系统的IO行为(缓存系统也可以当做是key-value的数据库),本文主要介绍访问MySQL数据库的IO流程以及IO相关的参数. 一.My ...

  8. 【多线程】0.理解一下5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO

    5种IO模型.阻塞IO和非阻塞IO.同步IO和异步IO 看了一些文章,发现有很多不同的理解,可能是因为大家入切的角度.环境不一样.所以,我们先说明基本的IO操作及环境. 本文是在<UNIX网络编 ...

  9. 【linux开发】IO端口和IO内存的区别及分别使用的函数接口

    IO端口和IO内存的区别及分别使用的函数接口 每个外设都是通过读写其寄存器来控制的.外设寄存器也称为I/O端口,通常包括:控制寄存器.状态寄存器和数据寄存器三大类.根据访问外设寄存器的不同方式,可以把 ...

  10. 重叠IO模型-异步IO

    重叠IO模型-异步IO http://laiba.tianya.cn/laiba/CommMsgs?cmm=959&tid=2701316824681802728 说到重叠模型首先还是提一下异 ...

最新文章

  1. PHP区域联动后端接口与数据表设计
  2. Codeforces Round #593 (Div. 2) D. Alice and the Doll 暴力 + 二分
  3. 在Java中给出的时间
  4. LwIP应用开发笔记之二:LwIP无操作系统UDP服务器
  5. http://blog.csdn.net/x86android/article/details/16980967
  6. QTdesigner使用--待更新
  7. Open-Unmix - A Reference Implementation for Music Source Separation
  8. 2009国家公务员面试过关点点通
  9. 精读《如何阅读一本书》(附全书思维导图)
  10. bash: yum:未找到命令
  11. 【Linux修炼】开篇
  12. arm指令bne.w改成b,即无条件跳转
  13. oracle新书 罗敏_Oracle Acs资深顾问罗敏 老罗技术核心感悟:分表还是分区?
  14. s一般怎么称呼自己的m_教师节到了,聊一聊该怎么称呼尊敬的老师
  15. 计算机用户与权限如何设置密码,如何设置电脑用户权限_如何设置电脑使用时间...
  16. 沟通的艺术:简介及内容导航
  17. java 像素矩阵_JAVA eclipse 中,已知灰度图像的像素矩阵怎么输出这个图像
  18. mongodb 创建只读用户
  19. 快速学会如何在linux上用户环境变量和系统环境变量
  20. 汉明码(计算机组成原理)没听课也能懂

热门文章

  1. 团队的英文翻译缩写_有感情的一起游戏的团队英文缩写
  2. Xilinx UCF约束语法一
  3. 小写转大写金额 php,php小写金额转大写
  4. 图像处理-相关知识点
  5. [总结]IOS开发工程师之路
  6. linux学习记录(二)
  7. 修正牛顿法及其matlab实现
  8. 关于win10重新安装应用商店(Microsoft Store)的解决方案
  9. Hudi-通过Hive查询hudi表数据
  10. Handler到底是一个什么东东