九、文件io

文章详细介绍了文件的概念、操作,包括文件的唯一性、访问和操作的本质。在C语言中,讨论了fopen、fclose、fprintf等接口以及文件路径、追加写入、文件模式等。在操作系统层面,讲解了open、write、read、close系统调用,以及文件描述符的分配规则和重定向原理。最后,提到了缓冲区的作用和在FILE结构体中的位置。
摘要由CSDN通过智能技术生成

一、文件

(零)前置知识

1.文件内容+文件属性
答:文件属性也是数据->即便你创建一个空文件,也要占据磁盘空间!!!
2.文件操作=文件内容的操作+文件属性的操作
答:有可能在操作文件的过程中,既改变内容,又改变属性
3. 所谓的“打开”文件,究竟在干什么?
答:将文件的属性或内容加载到内存中! ——冯诺依曼体系决定:CPU只能从内存中对数据做读写!
4.是不是所有的文件,都会处于被打开的状态?
答:绝对不是!
没有被打开的文件,在哪里?
答:只在磁盘上静静的存储着!
5.对文件的宏观分类:
答:打开的文件(内存文件) 和 (未打开文件)磁盘文件
6.通常我们打开文件,访问文件,关闭文件,是谁在进行相关操作?——是进程在操作!
c语言接口fopen,fclose, fread, fwrite… -> 写出代码 -> 形成程序 -> 当我们的文件程序,运行起来的时候,才会执行对应的代码,然后才是真正的对文件进行相关的操作——是进程在操作!!!
7.学习文件操作:就是学习——进程和打开文件的关系! 内存级操作!!!

(一)概念

文件=文件内容+文件属性。

(二)操作

对文件操作
对属性操作
对文件和属性操作

(三)文件的唯一性

文件的路径和文件名决定了文件的唯一性。

(四)文件访问

一个文件要被访问,就要被打开。
所以我们的文件分为打开的文件和没有被打开的文件。

(五)文件操作的本质

进程和被打开文件的关系
进程可以打开多个文件 -> 系统中一定存在大量被打开的文件
-> 被打开的文件要OS管理 -> 如何管理 -> 先描述后组织
->操作系统为了管理对应的打开文件,必定要为文件创建对应的内核数据结构表示文件
->struct file() -> 包含了文件的大部分属性

(六)补充:

文件在磁盘,而磁盘是硬件,操作系统是硬件的管理者,所以无论谁想访问磁盘,都必须调用OS提供的接口,而OS也一定会提供文件级别的系统调用方法。

二、C语言文件操作复习

(一)接口

1.基本写入

int fprintf (FILE* stream, const char*format, [argument]);

fprintf(fp, “%s: %d\n”, msg, cnt++); 向fp中写入msg, cnt两个值、

#include<stdio.h>
#include<unistd.h>
int main()
{
  FILE *fp=fopen("log.txt","w");
  if(fp==NULL)
  {
    perror("fopen");
    return 1;
  }
  const char *msg="hello linux";
  int cnt=1;
  while(cnt<=5)
  {
    fprintf(fp,"%s:%d",msg,cnt++);
  }
 
  return 0;
}

2.当前路径

(当前路径)cwd——correct work director

当前路径定义:当前进程所处的工作路径!

默认的进程工作路径是在它所处的路径下,不过这个路径是可以改的

(1)chdir(“路径”):更改当前进程的工作路径
ls /proc/(pid) 查看文件进程;
ls /proc/(pid) -al 查看文件进程详细信息;

3.文件操作

在这里插入图片描述

(1)a: 追加写入,不断的往文件中新增内容->追加重定向!
(2)以w方式打开文件

准备写入的时候,其实文件已经先被清空!w:有文件就清空再从头写,没文件,就创建从头写。

(3)读取
利用格式./myfile log.txt ,读取log.txt文件的内容:

fgets(buffer, sizeof(buffer), fp)   //按行读取

把读取的内容放到buffer,fp流中一行读多少字节,从fp流读出数据;
读取失败返回NULL,成功返回buffer。

#include<stdio.h>
#include<unistd.h>

int main(int argc,char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s filename\n", argv[0]);
        return 1;
    }
    FILE* fp = fopen(argv[1],"r");

    if (fp == NULL) {
        perror("fopen");
        return 1;
    }   
    char buffer[64];
    while (fgets(buffer,sizeof(buffer),fp) != NULL)
     {
        printf("echo:%s", buffer);
    }

    return 0;
}

在这里插入图片描述

(二)回归理论

1.当我们向文件写入的时候,最终是不是向磁盘写入?——答:是

2.磁盘是硬件吗?——答:是硬件

3.只有谁有资格向硬件写入呢?——答:操作系统!

4.能绕开操作系统对文件直接写入吗?——答:不能,所有的上层访问文件的操作,都必须贯穿操作系统

5.操作系统是如何被上层使用的?——答:必须使用操作系统提供的相关系统调用!
封装了系统接口
1.如何理解printf
2.我们怎么从来没有见过?——因为所有的语言都对系统接口做了封装

为什么要封装?
1.直接使用原生系统接口,使用成本比较高!
2.语言不具备跨平台性!
封装是如何解决跨平台问题的呢?——穷举所有的底层接口+条件编译!

三、OS下文件操作

在这里插入图片描述

(一)利用比特位传递选项

一个int有32个比特位,通过比特位传递选项。

#include<stdio.h>    
    
#define ONE (1<<0)    
#define TWO (1<<1)    
#define THREE (1<<2)    
#define FOUR (1<<3)    
    
void show(int flags)    
{    
    if(flags & ONE) printf("one\n");    
    if(flags & TWO) printf("two\n");    
    if(flags & THREE) printf("three\n");    
    if(flags & FOUR) printf("four\n");    
}    
    
    
int main()    
{    
    show(ONE);    
    printf("------------------------\n");    
    
    show(TWO);    
    printf("------------------------\n");    
    show(ONE|TWO);    
    printf("------------------------\n");    
    show(ONE|TWO|THREE);    
    printf("------------------------\n");    
    show(ONE|TWO|THREE|FOUR);    
    printf("------------------------\n");    
    
    return 0;                                                                                                                                                                              
}  

(一)基本接口

可以通过man 2 open 打开文件手册学习open参数的含义和使用方法。

1.open:

函数原型:

int open(const char *pathname, int flags); //只能打开已有文件
int open(const char *pathname, int flags, mode_t mode);  // 打开曾经不存在的文件,可以初始化权限

头文件:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
(1)open的第一个参数

若pathname以路径的方式给出,则当需要创建该文件时,就在pathname路径下进行创建。
若pathname以文件名的方式给出,则当需要创建该文件时,默认在当前路径下进行创建。(注意当前路径的含义)

(2)open的第二个参数

open函数的第二个参数是flags,表示打开文件的方式。
在这里插入图片描述

打开文件时,可以传入多个参数选项,当有多个选项传入时,将这些选项用 “|” 运算符隔开。
例如,若想以只写的方式打开文件,但当目标文件不存在时自动创建文件,则第二个参数设置如下:

O_WRONLY | O_CREAT

系统接口open的第二个参数flags是整型,有32比特位,若将一个比特位作为一个标志位,则理论上flags可以传递32种不同的标志位。

#define O_RDONLY         00
#define O_WRONLY         01
#define O_RDWR           02
#define O_CREAT        0100

这些宏定义选项的共同点就是,它们的二进制序列当中有且只有一个比特位是1(O_RDONLY选项的二进制序列为全0,表示O_RDONLY选项为默认选项),且为1的比特位是各不相同的,这样一来,在open函数内部就可以通过使用“与”运算来判断是否设置了某一选项。

例如,O_RDONLY、O_WRONLY、O_RDWR和O_CREAT在系统当中的宏定义如下:

(3)mode 初始权限

例如,将mode设置为0666,则文件创建出来的权限为rw- rw- rw-
但实际上创建出来文件的权限值还会受到umask(文件默认掩码)的影响,实际创建出来文件的权限为:mode&(~umask)。umask的默认值一般为0002,当我们设置mode值为0666时实际创建出来文件的权限为0664。rw- rw- r–

若想创建出来文件的权限值不受umask的影响,则需要在创建文件前使用umask函数将文件默认掩码设置为0。

umask(0); //将文件默认掩码设置为0

注意: 当不需要创建文件时,open的第三个参数可以不必设置。

(4)open的返回值

open函数的返回值是新打开文件的文件描述符。

实际上这里所谓的文件描述符本质上是一个指针数组的下标,指针数组当中的每一个指针都指向一个被打开文件的文件信息,通过对应文件的文件描述符就可以找到对应的文件信息。
当使用open函数打开文件成功时数组当中的指针个数增加,然后将该指针在数组当中的下标进行返回,而当文件打开失败时直接返回-1,因此,成功打开多个文件时所获得的文件描述符就是连续且递增的。
而Linux进程默认情况下会有3个缺省打开的文件描述符,分别就是标准输入0、标准输出1、标准错误2,这就是为什么成功打开文件时所得到的文件描述符是从3开始进程分配的。

(5)宏标记位示例
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<string.h>

//每一个宏,对应的数值,只有一个比特位是1,彼此位置不重叠

#define ONE (1<<0)
#define TWO (1<<1)
#define THREE  (1<<2)
#define FOUR (1<<3)


void show(int flags) {

    if(flags & ONE) printf("one\n");
    if(flags & TWO) printf("two\n");
    if(flags & THREE) printf("three\n");
    if(flags & FOUR) printf("four\n");
}


 
int main()
{
   show(ONE);
   printf("-----------------------\n");
   show(TWO);
   printf("-----------------------\n");
   show(ONE | TWO);
   printf("-----------------------\n");
   show(ONE | TWO | THREE);
   printf("-----------------------\n");
   show(ONE | TWO | THREE | FOUR);
   printf("-----------------------\n");


 
    return 0;
}

在这里插入图片描述

(6)打开曾经不存在的文件用int open(const char *pathname, int flags, mode_t mode);

2.write:

系统接口中使用write函数向文件写入信息,write函数的函数原型如下:

ssize_t write(int fd, const void *buf, size_t count);

我们可以使用write函数,将buf位置开始向后count字节的数据写入文件描述符为fd的文件当中。

  • 如果数据写入成功,实际写入数据的字节个数被返回。
  • 如果数据写入失败,-1被返回。

3.read

系统接口中使用read函数从文件读取信息,read函数的函数原型如下:

ssize_t read(int fd, void *buf, size_t count);

我们可以使用read函数,从文件描述符为fd的文件读取count字节的数据到buf位置当中。

  • 如果数据读取成功,实际读取数据的字节个数被返回。
  • 如果数据读取失败,-1被返回。
#include<sys/types.h>
#include<stdio.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>

int main() {
    int fd = open("log.txt",O_RDONLY);

    if(fd < 0) {
        perror("error");
        return 1;
    }
    char ch;
    while (1) {
        ssize_t s = read(fd,&ch,1);
        if (s <= 0) {
            break;
        }
        write(1,&ch,1);
    }
    close(fd);
    return 0;
}

在这里插入图片描述

4.close

系统接口中使用close函数关闭文件,close函数的函数原型如下:

int close(int fd);

使用close函数时传入需要关闭文件的文件描述符即可,若关闭文件成功则返回0,若关闭文件失败则返回-1。

#include <stdio.h>    
#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <string.h>    
#include <unistd.h>    
    
    
int main()    
{    
    int fd = open("log.txt",O_WRONLY|O_CREAT,0666);    
    if(fd < 0)    
    {    
        perror("open fail");    
        return 1;    
    }    
    
    int cnt =5;    
    char outBuffer[64];    
    while(cnt)    
    {    
        sprintf(outBuffer,"%s:%d\n","hello world",cnt--);    
    
        write(fd,outBuffer,strlen(outBuffer));                                                                                                                                             
    }    
    
    
    close(fd);    
    
    return 0;    
}    

特别注意:
我们在线文件中写入string 的时候以、0作为字符串的结尾,它是由C语言规定的,但是这里是文件写入是,结束时是与\0无关的。所以在strlen()不需要+1;

通过上面测试我们不难发现C语言中的库函数接口是通过封装了系统调用接口实现的

在这里插入图片描述
系统调用接口和库函数的关系,一目了然。

所以,可以认为,f#系列的函数,都是对系统调用的封装,方便二次开发。请添加图片描述

四、文件描述符fd

(一)文件描述符是什么

文件是由进程运行时打开的,一个进程可以打开多个文件,而系统当中又存在大量进程,也就是说,在系统中任何时刻都可能存在大量已经打开的文件。
因此,操作系统务必要对这些已经打开的文件进行管理,操作系统会为每个已经打开的文件创建各自的struct file结构体,然后将这些结构体以双链表的形式连接起来,之后操作系统对文件的管理也就变成了对这张双链表的增删查改等操作。
而为了区分已经打开的文件哪些属于特定的某一个进程,我们就还需要建立进程和文件之间的对应关系。

文件描述符就是一个小整数。

  • Linux进程默认情况下会有3个缺省打开的文件描述符。
  • 分别是标准输入0, 标准输出1, 标准错误2.
  • 0,1,2对应的物理设备一般是:键盘,显示器,显示器
    在这里插入图片描述
  • 而现在知道,文件描述符就是从0开始的小整数。
  • 当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。
  • 而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!
  • 所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件

因此,我们只要有某一文件的文件描述符,就可以找到与该文件相关的文件信息,进而对文件进行一系列输入输出操作。
注意: 向文件写入数据时,是先将数据写入到对应文件的缓冲区当中,然后定期将缓冲区数据刷新到磁盘当中。

(二)文件描述符的分配规则

文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
在这里插入图片描述

1.什么叫做进程创建的时候会默认打开0、1、2?

0就是标准输入流,对应键盘;1就是标准输出流,对应显示器;2就是标准错误流,也是对应显示器。
而键盘和显示器都属于硬件,属于硬件就意味着操作系统能够识别到,当某一进程创建时,操作系统就会根据键盘、显示器、显示器形成各自的struct file,将这3个struct file连入文件双链表当中,并将这3个struct file的地址分别填入fd_array数组下标为0、1、2的位置,至此就默认打开了标准输入流、标准输出流和标准错误流。

2.磁盘文件 VS 内存文件?

当文件存储在磁盘当中时,我们将其称之为磁盘文件,而当磁盘文件被加载到内存当中后,我们将加载到内存当中的文件称之为内存文件。磁盘文件和内存文件之间的关系就像程序和进程的关系一样,当程序运行起来后便成了进程,而当磁盘文件加载到内存后便成了内存文件。
磁盘文件由两部分构成,**分别是文件内容和文件属性。**文件内容就是文件当中存储的数据,文件属性就是文件的一些基本信息,例如文件名、文件大小以及文件创建时间等信息都是文件属性,文件属性又被称为元信息。
文件加载到内存时,一般先加载文件的属性信息,当需要对文件内容进行读取、输入或输出等操作时,再延后式的加载文件数据。

结论: 文件描述符是从最小但是没有被使用的fd_array数组下标开始进行分配的。

3.重定向

在明确了文件描述符的概念及其分配规则后,现在我们已经具备理解重定向原理的能力了。看完下面三个例子后,你会发现重定向的本质就是修改文件描述符下标对应的struct file*的内容。

(1)输出重定向原理:

输出重定向就是,将我们本应该输出到一个文件的数据重定向输出到另一个文件中。
在这里插入图片描述
例如,如果我们想让本应该输出到“显示器文件”的数据输出到log.txt文件当中,那么我们可以在打开log.txt文件之前将文件描述符为1的文件关闭,也就是将“显示器文件”关闭,这样一来,当我们后续打开log.txt文件时所分配到的文件描述符就是1。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
	close(1);
	int fd = open("log.txt", O_WRONLY|O_APPEND|O_CREAT, 0666);
	if(fd < 0){
		perror("open");
		return 1;
	}
	printf("hello Linux\n");
	printf("hello Linux\n");
	printf("hello Linux\n");
	printf("hello Linux\n");
	printf("hello Linux\n");
	fflush(stdout);
	close(fd);
	return 0;
}

此时,我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中,其中,fd=1。这种现象叫做输出
重定向。常见的重定向有:>, >>, <

在这里插入图片描述

在这里插入图片描述

(2)输入重定向原理

输入重定向就是,将我们本应该从一个文件读取数据,现在重定向为从另一个文件读取数据。
在这里插入图片描述
例如,如果我们想让本应该从“键盘文件”读取数据的scanf函数,改为从log.txt文件当中读取数据,那么我们可以在打开log.txt文件之前将文件描述符为0的文件关闭,也就是将“键盘文件”关闭,这样一来,当我们后续打开log.txt文件时所分配到的文件描述符就是0。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
	close(0);
	int fd = open("log.txt", O_RDONLY | O_CREAT, 0666);
	if (fd < 0){
		perror("open");
		return 1;
	}
	char str[40];
	while (scanf("%s", str) != EOF){
		printf("%s\n", str);
	}
	close(fd);
	return 0;
}

说明一下:
scanf函数是默认从stdin读取数据的,而stdin指向的FILE结构体中存储的文件描述符是0,因此scanf实际上就是向文件描述符为0的文件读取数据。

标准输出流和标准错误流对应的都是显示器,它们有什么区别?

#include <stdio.h>
int main()
{
	printf("hello printf\n"); //stdout
	perror("perror"); //stderr

	fprintf(stdout, "stdout:hello fprintf\n"); //stdout
	fprintf(stderr, "stderr:hello fprintf\n"); //stderr
	return 0;
}

在这里插入图片描述

这样看起来标准输出流和标准错误流并没有区别,都是向显示器输出数据。但我们若是将程序运行结果重定向输出到文件log.txt当中,我们会发现log.txt文件当中只有向标准输出流输出的两行字符串,而向标准错误流输出的两行数据并没有重定向到文件当中,而是仍然输出到了显示器上。

实际上我们使用重定向时,重定向的是文件描述符是1的标准输出流,而并不会对文件描述符是2的标准错误流进行重定向。

(二)使用 dup2 系统调用

要完成重定向我们只需进行fd_array数组当中元素的拷贝即可。例如,我们若是将fd_array[3]当中的内容拷贝到fd_array[1]当中,因为C语言当中的stdout就是向文件描述符为1文件输出数据,那么此时我们就将输出重定向到了文件log.txt。
在这里插入图片描述

在Linux操作系统中提供了系统接口dup2,我们可以使用该函数完成重定向。dup2的函数原型如下:

1.函数原型

#include <unistd.h>
int dup2(int oldfd, int newfd);

函数功能: dup2会将fd_array[oldfd]的内容拷贝到fd_array[newfd]当中,如果有必要的话我们需要先使用关闭文件描述符为newfd的文件。

函数返回值: dup2如果调用成功,返回newfd,否则返回-1。
使用dup2时,我们需要注意以下两点:

如果oldfd不是有效的文件描述符,则dup2调用失败,并且此时文件描述符为newfd的文件没有被关闭。
如果oldfd是一个有效的文件描述符,但是newfd和oldfd具有相同的值,则dup2不做任何操作,并返回newfd。

例如,我们将打开文件log.txt时获取到的文件描述符和1传入dup2函数,那么dup2将会把fd_arrya[fd]的内容拷贝到fd_array[1]中,在代码中我们向stdout输出数据,而stdout是向文件描述符为1的文件输出数据,因此,本应该输出到显示器的数据就会重定向输出到log.txt文件当中。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
	int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);
	if (fd < 0){
		perror("open");
		return 1;
	}
	close(1);
	dup2(fd, 1);
	printf("hello printf\n");
	fprintf(stdout, "hello fprintf\n");
	return 0;
}

在这里插入图片描述

六、缓冲区

(一)本质

缓冲区的本质:就是一段内存!

(二)意义

  1. 解放使用缓冲区的进程的时间。
  2. 缓冲区的存在可以集中处理数据刷新,减少IO的次数,从而达到提高整机的效率的目的。

送书的例子:你在西安,需要把书送到北京的朋友那里:你(进程)把书(数据)交给顺丰快递(缓冲区),此时进程返回并继续执行自己后续的代码,缓冲区积累够一堆数据以后才开始刷新,就好比你送快递,不可能给你一个人送,这样浪费时间浪费资源,而是累计够了数据,一起送过去!

(三)FILE当中的缓冲区

#include <stdio.h>
#include <unistd.h>
int main()
{
	//c
	printf("hello printf\n");
	fputs("hello fputs\n", stdout);
	//system
	write(1, "hello write\n", 12);
	fork();
	return 0;
}

在这里插入图片描述

但是,当我们将程序的结果重定向到log.txt文件当中后,我们发现文件当中的内容与我们直接打印输出到显示器的内容是不一样的。

在这里插入图片描述

那为什么C库函数打印的内容重定向到文件后就变成了两份,而系统接口打印的内容还是原来的一份呢?

首先我们应该知道的是,缓冲的方式有以下三种:

  • 无缓冲。
  • 行缓冲。(常见的对显示器进行刷新数据)
  • 全缓冲。(常见的对磁盘文件写入数据)

当我们直接执行可执行程序,将数据打印到显示器时所采用的就是行缓冲,因为代码当中每句话后面都有\n,所以当我们执行完对应代码后就立即将数据刷新到了显示器上。

而当我们将运行结果重定向到log.txt文件时,数据的刷新策略就变为了全缓冲,此时我们使用printf和fputs函数打印的数据都打印到了C语言自带的缓冲区当中,之后当我们使用fork函数创建子进程时,由于进程间具有独立性,而之后当父进程或是子进程对要刷新缓冲区内容时,本质就是对父子进程共享的数据进行了修改,此时就需要对数据进行写时拷贝,至此缓冲区当中的数据就变成了两份,一份父进程的,一份子进程的,所以重定向到log.txt文件当中printf和puts函数打印的数据就有两份。但由于write函数是系统接口,我们可以将write函数看作是没有缓冲区的,因此write函数打印的数据就只打印了一份。

这个缓冲区是谁提供的?

实际上这个缓冲区是C语言自带的,如果说这个缓冲区是操作系统提供的,那么printf、fputs和write函数打印的数据重定向到文件后都应该打印两次。

(四)位置

这个缓冲区在哪里?

我们常说printf是将数据打印到stdout里面,而stdout就是一个FILE*的指针,在FILE结构体当中还有一大部分成员是用于记录缓冲区相关的信息的。

//缓冲区相关
/* The following pointers correspond to the C++ streambuf protocol. */
/* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr;   /* Current read pointer */
char* _IO_read_end;   /* End of get area. */
char* _IO_read_base;  /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr;  /* Current put pointer. */
char* _IO_write_end;  /* End of put area. */
char* _IO_buf_base;   /* Start of reserve area. */
char* _IO_buf_end;    /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base;  /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */

也就是说,这里的缓冲区是由C语言提供,在FILE结构体当中进行维护的,FILE结构体当中不仅保存了对应文件的文件描述符还保存了用户缓冲区的相关信息。

操作系统有缓冲区吗?

操作系统实际上也是有缓冲区的,当我们刷新用户缓冲区的数据时,并不是直接将用户缓冲区的数据刷新到磁盘或是显示器上,而是先将数据刷新到操作系统缓冲区,然后再由操作系统将数据刷新到磁盘或是显示器上。(操作系统有自己的刷新机制,我们不必关系操作系统缓冲区的刷新规则)

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值