Linux 基础IO


什么是IO:
IO" 是 “输入/输出” 的缩写,
在Linux I/O是操作系统中的一个重要概念,它涉及到将数据从一个地方移动到另一个地方。
比如你从磁盘中读取一个文件里面的内容,或创建一个文件保存到磁盘中。
一些常见的输入/输出设备和操作包括:

输入设备:

  • 键盘:用户通过键盘输入文本和命令。
  • 鼠标:用户通过鼠标进行图形用户界面的交互。
  • 扫描仪:将纸质文档或图像转换为数字格式。

输出设备:

  • 显示器:计算机通过显示器展示图形和文字信息。
  • 打印机:将数字信息转换为纸质输出。
  • 音频设备:播放声音,如扬声器和耳机。

文件IO:

  • 通过文件系统读写文件,包括从硬盘读取数据和将数据写入硬盘。

文件IO

文件I/O(Input/Output)是指计算机程序与文件系统进行数据交互的过程。这包括从文件中读取数据(输入)和将数据写入文件(输出)。在编程语言中(以C语言为例),语言会提供相应的库函数,在Liunx中,会提供一些系统调用接口。

C语言文件IO

在C语言中,文件I/O 主要涉及使用标准I/O库(stdio.h)提供的函数。以下是一些常见的C语言文件I/O 操作:

  • 打开文件(fopen):
    • FILE *fopen(const char *filename, const char *mode);
    • 打开文件,并返回一个文件指针
    • 参数1是打开的文件名
    • 参数2是文件打开方式
    • 常见的打开方式有 r(只读)、w(写入)、a(追加)、r+(读写)、w+(读写)、a+(读写)
#include <stdio.h>
int main()
{
   
    FILE *file = fopen("example.txt", "r");
    if (file == NULL) {
   
    // 处理文件打开失败的情况
    }
    return 0;
}
  • 关闭文件(fclose):
    • 用于关闭先前打开的文件。
    • 语法:int fclose(FILE *stream);
int main()
{
   
    FILE *file = fopen("example.txt", "r");
    if (file == NULL) {
   
    // 处理文件打开失败的情况
    }
    //操作文件
    fclose(file);//关闭文件
    return 0;
}
  • 写入文件(fwrite, fprintf):
    • 用于向文件中写入数据。
    • fwrite 用于二进制文件,fprintf 用于文本文件。
int main()
{
   
    FILE *file = fopen("example.txt", "r");
    if (file == NULL) {
   
    // 处理文件打开失败的情况
    }
    // 向打开的文件写入文本
    fprintf(file, "Hello, World!");

    fclose(file);//关闭文件
    return 0;
}
  • 读取文件(fread, fgets):
    • 用于从文件中读取数据。
    • fread 用于二进制文件,fgets 用于文本文件。
int main()
{
   
    FILE *file = fopen("example.txt", "r");
    if (file == NULL) {
   
    // 处理文件打开失败的情况
    }
   char buffer[100];
   // 向打开的文件读取一行文本存到buffer数组中
   fgets(buffer, sizeof(buffer), file);
   
    fclose(file);//关闭文件
    return 0;
}

对文件进行写入

示例:

#include <stdio.h>
#include <string.h>
int main()
{
   
    FILE *fp = fopen("myfile", "w");//w的方式打开,如果文件不存在,则创建文件
    if(!fp)
    {
   
        printf("fopen error!\n");
    }
    const char *msg = "hello World!\n";
    int count = 5;
    while(count--)
    {
   
        fputs(msg,fp);//向打开的文件写入五次hello World!
    }
    fclose(fp);
    return 0;
}

运行结果:

image.png

对文件进行读取

#include <stdio.h>
#include <string.h>
int main()
{
   
    FILE *fp = fopen("myfile", "r");
    if(!fp)
    {
   
        printf("fopen error!\n");
    }
    char buffer[64];
    for (int i = 0; i < 5; i++){
   
        //将读取的文件存放到数组中
		fgets(buffer, sizeof(buffer), fp);
		printf("%s",buffer);
	}
    fclose(fp);
    return 0;
}

运行结果:
成功从文件中读取到字符,打印到显示器中
image.png

默认打开的三个流

在C语言中,有三个标准I/O流,它们是由标准库提供的默认打开的流。

  1. stdin(标准输入):
    • stdin 是标准输入流,通常关联于键盘。程序可以通过 scanffgets 等函数从 stdin 读取用户的输入。
  2. stdout(标准输出):
    • stdout 是标准输出流,通常关联于显示屏或终端。程序可以通过 printf 等函数向 stdout 输出信息。
  3. stderr(标准错误):
    • stderr 是标准错误输出流,也通常关联于显示屏或终端。与 stdout 类似,但主要用于输出错误信息。在程序出现错误时,可以使用 fprintf(stderr, ...) 将错误信息输出到 stderr

这三个流在程序启动时由操作系统自动打开,不需要用户手动打开或关闭。它们是标准库中预定义的 FILE 结构的指针。在头文件 stdio.h 中定义了它们。
c程序运行后,默认打开这三个流,我们才能使用printf,scanf 向显示器打印,从键盘读取输入等操作。

image.png
这三个流的返回值都是FILE*类型

Linux下一切皆文件,显示器也可以当做文件,fputs可以向磁盘文件进行写入,那么也可以向显示器文件进行写入。
示例

int main()
{
   
    fputs("Hello World!\n",stdout);
    return 0;
}

运行结果:成功在显示器上打印信息
image.png

系统文件IO

操作文件,除了C语言的接口可以,其他语言(C++/Java)都有自己的一套接口操作文件,语言级别的接口都是封装了系统调用接口来完成的。
Linux下常见对文件操作的系统调用接口

  • open
    • 打开或新建文件
    • int open(const char *pathname, int flags);
  • close
    • 用于关闭已打开的文件。
    • int close(int fd);
  • read
    • 用于从文件中读取数据。
    • ssize_t read(int fd, void *buf, size_t count);
  • write
    • 用于向文件中写入数据
    • ssize_t write(int fd, const void *buf, size_t count);

open

man 2 open认识open函数
image.png

  • 参数一pathname:要打开或新建的目标文件名。
  • 参数二 flags:: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
    • O_RDONLY: 只读打开
    • O_WRONLY:只写打开
    • O_RDWR:读写的方式打开。这三常量只能必须指定一个且只能指定一个
    • O_CREAT:若文件不存在,则创建它,flags使用这个参数,要加mode参数来指定新建文件的访问权限
    • O_APPEND:追加写
  • 参数3:当flags参数为O_CREAT时,添加参数3,指定文件的访问权限。当不需要创建文件时,open的第三个参数可以不必设置
  • 返回值:
    • 成功:新文件的文件描述符
    • 失败:-1

示例:以只读的方式新建文件

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
   
    int fd = open("log.txt",O_RDONLY | O_CREAT);//加O_CREAT才会新建文件,否则不会新建
    return 0;
}

新建的log.txt文件访问权限出错
image.png
设置第三个参数,只读新建文件的访问权限

int main()
{
   
    //创建出来文件的权限值不受umask的影响
    umask(0);
    int fd = open("log.txt",O_RDONLY | O_CREAT,0666);//0666对应-rw-rw-rw
    return 0;
}

运行结果

image.png
使用O_CREAT新建文件时,要指定文件的访问权限

close

open打开成功返回文件描述符,close通过文件描述符关闭文件

int main()
{
   
    //创建出来文件的权限值不受umask的影响
    umask(0);
    int fd = open("log.txt",O_RDONLY | O_CREAT,0666);//0666对应-rw-rw-rw
    close(fd);//关闭文件
    return 0;
}

read

从文件中读取信息
ssize_t read(int fd, void *buf, size_t count);

  • 描述符为fd的文件读取count字节的数据到buf位置当中。
  • 读取成功,返回sszie类型(实际读取数据的字节个数被返回)
  • 读取失败。返回-1
int main()
{
   
    int fd = open("log.txt",O_RDONLY);
    char buf[1024];
    ssize_t nums =read(fd,buf,sizeof(buf));//从文件中读取信息存放到buf数组中
    printf("%d\n",nums);
    printf("%s",buf);
    return 0;
}

运行结果:log.txt中的53个字节数据全部被读取到
image.png

write

向打开的文件中写入数据
ssize_t write(int fd, const void *buf, size_t count);

  • fd 是文件描述符,表示已打开文件或其他I/O对象。
  • buf 是指向要写入的数据的指针。
  • count 是要写入的字节数
int main()
{
   
    int fd = open("log.txt", O_WRONLY | O_APPEND);
    if(fd < 0 ){
   
        perror("open");
    }
    const char* str =  "Hello Wolrd!\n";
    //向log.txt文件写入Hello Wolrd!
    ssize_t nums = write(fd,str,strlen(str));
    printf("%d",nums);//实际写入字节数
    close(fd);
    return 0;
}

运行结果:

image.png

文件描述符

进程要访问文件,必须要先打开文件,文件要被访问,前提是加载到内存中,才能被访问。因为冯诺依曼体系结构规定CPU不能直接访问外设(磁盘),必须先将数据加载到内存中,让CPU访问内存。所以文件要被访问,前提是加载到内存中。

一个进程可以打开多个文件,当系统中存在大量的被打开的文件,操作系统要将这些打开的文件管理起来(用struct描述起来,再用特定的数据结构管理起来)

操作系统会为每个已经打开的文件创建各自的struct file结构体,然后将这些结构体以双链表的形式连接起来,之后操作系统对文件的管理也就变成了对这张双链表的增删查改等操作。

当程序运行起来,操作系统会该进程创建对应的PCB、页表、进程地址空间(mm_struct)等
image.png

文件要被进程访问,就要建立进程和文件之间的关系。用来区分哪些进程访问哪些打开的文件。

image.png
文件描述符fd在Linux内核中,本质就是一个数组的下标

磁盘文件 && 内存文件

当文件存储在磁盘当中时,我们将其称之为磁盘文件,而当磁盘文件被加载到内存当中后,我们将加载到内存当中的文件称之为内存文件。磁盘文件和内存文件之间的关系就像程序和进程的关系一样,当程序运行起来后便成了进程,而当磁盘文件加载到内存后便成了内存文件。

磁盘文件由两部分构成,分别是文件内容和文件属性。文件内容就是文件当中存储的数据,文件属性就是文件的一些基本信息,例如文件名、文件大小以及文件创建时间等信息都是文件属性,文件属性又被称为元信息。
文件加载到内存时,一般先加载文件的属性信息,当需要对文件内容进行读取、输入或输出等操作时,再延后式的加载文件数据。

文件描述符的分配规则

写代码观察一些open的返回值,

int main()
{
   
    //创建出来文件的权限值不受umask的影响
    umask(0);
    int fd1 = open("log1.txt",O_RDONLY | O_CREAT,0666);//0666对应-rw-rw-rw
    int fd2 = open("log2.txt",O_RDONLY | O_CREAT,0666);
    int fd3 = open("log3.txt",O_RDONLY | O_CREAT,0666);
    int fd4 = open("log.4txt",O_RDONLY | O_CREAT,0666);
    //打开一个不存在的文件
    int fd5 = open("xxx",O_RDONLY);

    printf("fd1 = %d\n",fd1);
    printf("fd2 = %d\n",fd2);
    printf("fd3 = %d\n",fd3);
    printf("fd4 = %d\n",fd4);
    printf("fd5 = %d\n",fd5);
    return 0;
}

运行结果:

image.png

  • 成功的文件描述符是从3开始递增的
  • 失败的是-1

c程序运行时 默认打开三个流,当进程创建时就默认打开了标准输入流、标准输出流和标准错误流,也就是说数组当中下标为0、1、2的位置已经被占用了,所以只能从3开始进行分配。

当关闭0号fd后再打开文件,新的fd是怎么样的呢?

int main()
{
   
  umask(0);
  close(0);//关闭stdin
  int fd1 = open("log1.txt",O_CREAT | O_RDONLY,0666);
  int fd2 = open("log2.txt",O_CREAT | O_RDONLY,0666);
  int fd3 = open("log3.txt",O_CREAT | O_RDONLY,0666);

  printf("fd1 = %d\n",fd1);
  printf("fd2 = %d\n",fd2);
  printf("fd3 = %d\n",fd3);

  return 0;
}

运行结果:

image.png

关闭0号文件时,新建的文件log1.txt会从0开始,后面新建的继续从3开始

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

重定向

了解了文件描述符和文件描述符的分配规则之后,在理解重定向就很简单了。

重定向的本质就是在操作系统内部,更改fd对应内容的指向。
常见的重定向有:

  • >输出重定向
  • >>追加输出重定向
  • < 输入重定向

输出重定向

小例子:
关闭1号文件,再向显示器打印信息

int main()
{
   
  close(1);
  int fd = open("log.txt",O_WRONLY | O_CREAT,0666);
  if(fd < 0 ){
   
    perror("open fail");
    return -1;
  }

  printf("Hello World!\n");
  printf("Hello World!\n");
  close(fd);
  return 0;
}

运行结果:

image.png
运行程序,什么也没打印,这是因为printf会向1号文件输出信息,但是1号文件默认是显示器,现在已经关闭了1号文件。1号文件变为了我们自己的文件。这种本应该向显示器输出信息,却输出到了别的文件,这就叫做输出重定向(fd = 1)。

image.png

理论上log.txt文件会存放Hello World! Hello World! 的信息,但是当我们打开log.txt文件的时候,里面仍然是空的
image.png
这是因为缓冲区的问题,printf并不会直接把数据写入到内存中,而是先写入到缓存区中,当printf写入完后,使用fflush刷新一下缓冲区就可以了。

int main()
{
   
  close(1);
  int fd = open("log.txt",O_WRONLY | O_CREAT,0666);
  if(fd < 0 ){
   
    perror("open fail");
    return -1;
  }

  printf("Hello World!\n");
  printf("Hello World!\n");
  fflush(stdout);
  close(fd);//刷新缓冲区
  return 0;
}

重新编译,运行程序,查看文件,就能看见信息了

image.png

输入重定向

输入重定向就是从一个文件读取数据,现在重定向为另一个文件。
其本质就是改变fd(数组下标)指向的内容

image.png

例子:
将从键盘读取的数据,重定向到指定文件中读取

int main()
{
   
  close(0);
  int fd = open("log.txt",O_RDONLY);
  char str[80];
  while(scanf("%s",str) != EOF)
  {
   
    printf("%s\n",str);
  }
  close(fd);
  return 0;
}

运行结果:读取到log.txt文件中的数据
image.png

追加输出重定向

追加输出重定向的区别:
输出重定向会覆盖原有的内容重写。而追加输出据基础上继续新增

//追加重定向
int main()
{
   
  close(1);
  int fd = open("log.txt",O_WRONLY | O_APPEND | O_CREAT , 0666);
  if(fd < 0){
   
    perror ("open fail");
    return -1;
  }

  printf("Hello Linux!\n");
  printf("Hello Linux!\n");
  printf("Hello Linux!\n");
  fflush(stdout);
  close(fd);
  return 0;
}

运行结果:
image.png
删除open的O_APPEND选项后 运行结果:

image.png

stdout和stderr 区别
stdout 和 stderr 对应的默认都是显示器文件,但是区别就在于:重定向的是文件描述符是1的标准输出流,而并不会对文件描述符是2的标准错误流进行重定向。

重定向函数dup2

dup2函数是Linux提供的系统调用接口
函数原型:
image.png
它的作用是将 oldfd 复制到 newfd,并且如果 newfd 已经打开,则会先关闭它。这可以用于重定向文件描述符。

int main()
{
   
  int fd = open("log.txt",O_WRONLY);
  dup2(fd,1);//将fd更改为1 
  printf("Hello Linux!!!!!!!!!!\n");
  printf("Hello Linux!!!!!!!!!!\n");
  printf("Hello Linux!!!!!!!!!!\n");
  fflush(stdout);
  close(1);
  return 0;
}

image.png

FILE

FILE 是一个在C语言中定义的结构体,用于处理文件操作。FILE 结构是由标准C库提供的,用于在程序中表示文件流。它包含了有关文件的信息,如文件描述符、缓冲区等。

不论是语言级别的库函数还是系统调用级别的函数,访问文件的本质就是通过文件fd进行访问的
C库中的FILE结构体也一定封装了fd

可以在Linux中 /usr/include/stdio.h路径下查看stdio.h中的源码

typedef struct _IO_FILE FILE;//源码中的描述

FILE实际上就是struct _IO_FILE的一个别名

了解了FILE之后,当我们使用fopen打开一个文件之后,
fopen首先创建FILE结构体变量,并且返回该结构体的地址(FILE*),然后通过系统调用 open接口打开对应的文件,得到对应的文件描述符。将对应的文件描述符填入到FILE结构体中。
然后fprintf,fread,fwrite等函数通过传入对应的FILE* 指针找到对应的FILE结构体,从FILE中拿到对应的文件描述符,最后通过文件描述符对文件进行操作。

FILE当中的缓冲区

观察下面代码

#include <stdio.h>
#include <unistd.h>
int main()
{
   
    printf("Hello printf\n");
    fputs("Hello fputs\n",stdout);

    write(1,"Hello write\n",12);//这里不能用stdout  stdout是c语言的 系统不认识
    fork();//创建子进程
    return 0;
}

分别用语言级别的函数和系统调用级别的函数向显示器打印信息
运行结果:成功向显示器打印信息
image.png
但是 如果将运行结果重定向到一个文件中,结果变为如下情况:

image.png
这是为什么呢?
首先了解一下缓冲区的刷新策略
一般分为以下几点

  • 行刷新 (遇到\n就刷新)
  • 立即刷新
  • 满刷新(当缓冲区满了才会刷新)

还有两种特殊情况:

  • 强制刷新(使用fflush进行刷新)
  • 进程退出

而显示器文件的刷新策略是行刷新。磁盘文件的刷新策略是满刷新(这样为了更少的IO操作,更少的外设访问,从而提高效率所有的外设更倾向于满刷新)

当执行上面的程序时,向显示器打印就是采用行刷新的策略,每个打印信息后面都有\n进行刷新缓冲区。当执行完代码就立即打印到显示器上了。
然而当输出重定向到log.txt(磁盘文件中时,缓冲区的刷新策略变为了满刷新)不会立即输出到文件中。使用printf函数和fouts函数打印的数据都存到了语言自带的缓冲区了。之后在用fork创建子进程,当子进程执行结束时,进程退出,要刷新缓冲区的内容。向文件进行写入。由于进程之间具有独立性,此时就需要对数据进行写时拷贝,缓冲区的数据就会变为父子进各自一份。父进程执行完毕,进程退出,刷新缓冲区,又向文件进行写入。所以printf和fputs输出两遍。而wiret只执行了一次,证明没有所谓的缓冲区。

缓冲区由谁提供

实验证明这个缓冲区是C标准库提供的,如果说这个缓冲区是操作系统提供的,那么printf、fputs和write函数打印的数据重定向到文件后都应该打印两次。

缓冲区在哪

缓存区就是一段内存空间,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. */

为社么要有缓冲区

就是为了更少的IO操作,更少的外设访问,从而提高效率

操作系统也有缓冲区

当我们刷新用户缓冲区的数据时,并不是直接将用户缓冲区的数据刷新到磁盘或是显示器上,而是先将数据刷新到操作系统缓冲区,然后再由操作系统将数据刷新到磁盘或是显示器上。因为操作系统才是软硬件资源的管理者。

文件系统

文件不仅仅有内容,还有其属性(文件大小,类型,拥有者等)。文件被存放到磁盘当中。现在基本都是固态硬盘了。这里以磁盘为例说明。

image.png

磁盘是一种存储介质,在计算机中,磁盘是唯一的机械设备。

磁盘分区

磁盘通常被称为块设备,一般以扇区为单位,一个扇区的大小通常为512字节。比如一个512GB的磁盘,扇区大概有10亿多个。
image.png
这和数组很像,为了方便理解,可简单的理解为:
将数据存储到磁盘中转换为将数据存储到数组中。
找到磁盘特定的扇区变为找到特定数组的下标。
对磁盘的管理变为了对数组的管理。

操作系统为了更好的管理磁盘,对磁盘进行了分区操作。(这个原理就和一个国家划分省一样)。

在windows系统当中,经常会对磁盘进行分区,分为C盘,D盘等。就是在原有的基础上进一步划分区域(和一个省份继续划分市级单位一样)。

image.png
对磁盘的管理变成了对小分区的管理。
在linux中 也可以查看分区信息。
使用ls /dev/vda* -l命令查看

image.png

区域划分完成了,下一步就要对其进行管理了。对磁盘的第一个操作就是我们平时所熟悉的格式化。

磁盘格式化:就是对磁盘中的分区进行初始化的一种操作。磁盘格式化就是对分区后的区域写入对应的管理信息。(就相当于为每个市分配一个市长)

写入的管理信息具体是什么,这个由不同的操作系统决定,常见的文件系统包括FAT32、NTFS、exFAT(在Windows系统中常见)、EXT2(在Linux系统中常见)、HFS+(在Mac OS中常见)。

EXT2文件系统

EXT2(第二扩展文件系统)是Linux操作系统中使用的一种文件系统。它是EXT文件系统家族的一部分,后来被EXT3和EXT4所取代。基本原理都一样。EXT4加入了更多的功能。这里以EXT2为例。

操作系统为了更好的管理磁盘,对磁盘进行了分区。分区完对其格式化,写入文件系统。

文件系统通常是每个分区都有的。分区的头部会包括一个启动块(Boot Block),对于该分区的其余区域,EXT2文件系统会根据分区的大小将其划分为一个个的块组(Block Group)。

image.png
注意:D盘也有同样的文件系统。
启动块的大小是确定的,而块组的大小是由格式化的时候确定的,并且不可以更改。

EXT2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。每个组块都由超级块(Super Block)、块组描述符表(Group Descriptor Table)、块位图(Block Bitmap)、inode位图(inode Bitmap)、inode表(inode Table)以及数据表(Data Block)组成。如下图

image.png

  • Super Block: 存放文件系统本身的结构信息。记录的信息主要有:Data Block 和 inode的总量, 未使用的Data Block和inode的数量,一个Data Block和inode的大小,最近一次挂载的时间,最近一次写入数据的 时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个 文件系统结构就被破坏了
  • Group Descriptor Table:GDT(块组描述符)描述该分区当中块组的属性信息。
  • Block Bitmap:Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没 有被占用
  • inode Bitmap:每个bit表示一个inode是否空闲可用。假设有10000+个inode结点,就有10000+个比特位。比特位和特定的inode是一一对应的。inode bitmap中1 表示被占用,0表示可用。
  • inode Table:存放文件属性,即每个文件的inode结构。
  • Data Block:存放文件内容

inode

通过上面对文件系统的了解,可以看出文件的属性和内容是分开存放的。当系统中存在大量的文件的时候,需要给每个文件的属性集起一个唯一的编号,即inode号。也就是说,inode是一个文件的属性集合,Linux中几乎每个文件都有一个inode。

inode(Index Node的缩写)是操作系统中用于存储文件属性的数据结构。每个文件或目录都有一个唯一的inode,通过inode可以查找和管理文件的相关信息,而不是直接使用文件名。inode 可以通过文件系统的 inode表 中确定。

使用ll -i可以查看文件的inode

image.png

当我们新建一个文件并对其写入内容时 主要分为以下四步

  1. 存储属性
    • 遍历inode位图,找到一个空闲的inode
    • 在inode表中找到inode,将文件的属性填入其中
  2. 存储数据
    • 确定要写入数据所需数据块个数,假设需要三个数据块,内核通过Block Bitmap确定了三块空数据块,将内核缓冲区的数据块依次写入到指定的三块数据块中
  3. 记录分配情况
    • 文件内容分别存放到了三块数据块中,内核在inode表中记录了这三块数据块。
  4. 添加文件名到目录
    • 将该文件的文件名 和 inode 添加到目录文件数据块中(每个文件都有对应的目录,新建的文件属于目录文件的内容)
    • 文件名和inode之间的对应关系,将文件名和文件的内容及属性连接起来。

删除文件

删除文件并不会像我们想象的那样,将磁盘中的数据抹除,而是将

  • 该文件对应的inode在inode位图中从1变为0(表示该inode可用)
  • 将该文件使用的数据块在Block Bitmap中从1变为0(表示该数据块可用)
    等待后面继续写入数据时,就会从bitmap和inodemap中重新申请该inode和数据块进行覆盖写入。(删除的数据如果没有进行写入操作,是可以找回的)

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-01-25 05:56:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-25 05:56:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-25 05:56:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-25 05:56:03       20 阅读

热门阅读

  1. OpenCV处理视频文件

    2024-01-25 05:56:03       37 阅读
  2. 算法训练营Day49(动态规划10)

    2024-01-25 05:56:03       40 阅读
  3. 开源计算机视觉库OpenCV详解

    2024-01-25 05:56:03       39 阅读
  4. Kong工作原理 - 负载均衡 - 高级负载均衡

    2024-01-25 05:56:03       31 阅读
  5. Kong工作原理 - 负载均衡 - 负载均衡算法

    2024-01-25 05:56:03       35 阅读
  6. 设计模式-中介模式

    2024-01-25 05:56:03       34 阅读
  7. 探索人工智能的发展与影响

    2024-01-25 05:56:03       31 阅读
  8. mysql 负向条件查询不能使用索引

    2024-01-25 05:56:03       35 阅读
  9. 面试题目,你对前端工程化的了解

    2024-01-25 05:56:03       33 阅读
  10. 在Python中如何实现单例模式?

    2024-01-25 05:56:03       31 阅读
  11. [go] Stringer 类型

    2024-01-25 05:56:03       36 阅读