Linux:基础IO(二.缓冲区、模拟一下缓冲区、详细讲解文件系统)

上次介绍了:Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)


文章目录

  • 1.缓冲区
    • 1.1概念
    • 1.2作用与意义
  • 2.语言级别的缓冲区
    • 2.1刷新策略
    • 2.2具体在哪里
    • 2.3支持格式化
  • 3.自己来模拟一下缓冲区
    • 3.1项目文件规划
    • 3.2mystdio.h
    • 3.3mystdio.c
    • 3.4test.c
  • 4.文件系统
    • 4.1磁盘机械结构
    • 4.2磁盘的物理存储
    • 4.3磁盘的逻辑存储
    • 4.4文件系统
  • 5.文件名
    • 5.1再看文件的增删改查


1.缓冲区

1.1概念

在计算机中,内存被划分为不同的区域,其中一部分被用作缓冲区,用于临时存储数据

  1. 内存区域

    • 物理结构:计算机的内存是由许多存储单元组成的,每个存储单元都有一个唯一的地址。这些存储单元按照地址顺序排列,形成了一块连续的内存区域。
    • 访问方式:程序可以通过地址访问内存中的数据,读取或写入存储单元的内容。不同的内存区域有不同的用途,例如代码区、数据区、堆区和栈区等。
  2. 缓冲区

    • 定义:缓冲区是内存中的一块区域,用于临时存储数据。通常用于临时存储输入数据、输出数据或中间数据,以便程序能够有效地处理这些数据。
    • 特点:缓冲区是一种有限大小的内存区域,数据在缓冲区中暂时存储,等待被处理或传输到目标设备。
    • 用途:缓冲区在计算机程序设计中被广泛应用,例如用于输入输出操作、网络通信、文件读写等场景。
  3. 缓冲区的工作原理

    • 输入缓冲区:当数据被输入到程序中时,数据首先被存储在输入缓冲区中。程序可以逐个字符或一定量的数据从输入缓冲区中读取,进行处理。
    • 输出缓冲区:当程序输出数据时,数据首先被存储在输出缓冲区中。系统会根据缓冲区的策略或限制,将数据逐个字符或一定量的数据发送到目标设备。

缓冲区作为一块内存区域,提供了一个临时存储数据的空间,帮助程序高效地处理输入和输出

打开一个文件进行读取或写入时,文件内容并不是直接加载到整个内存中,而是加载到内存中的一个特定区域,即缓冲区(Buffer)。缓冲区是内存中的一个临时存储区域,用于存储从文件读取的数据或待写入文件的数据。

在读取文件时,操作系统会一次性从磁盘读取一定数量的数据块到缓冲区中,然后程序可以从这个缓冲区中读取数据,而不是每次都直接从磁盘读取。这样可以减少磁盘I/O操作的次数,提高读取效率。

在写入文件时,程序会将数据写入到缓冲区中,而不是直接写入到磁盘。当缓冲区满或者程序显式调用flush方法或关闭文件时,缓冲区中的数据才会被一次性写入到磁盘中。这种延迟写入的方式也可以提高写入效率,并减少磁盘操作的次数。

需要注意的是,缓冲区的大小是有限的,它不能无限地存储数据。因此,在处理大文件时,数据会分批次地加载到缓冲区中,并进行处理。同时,缓冲区的管理是由操作系统和文件系统来负责的,程序员通常不需要直接操作缓冲区,而是通过文件I/O函数或方法来间接地使用缓冲区。

1.2作用与意义

缓冲区在计算机系统中具有的意义和作用,主要体现在以下几个方面:

  1. 数据传输的效率:缓冲区可以暂时存储数据,使得数据传输过程中的速度更加稳定和高效。当数据产生和消费的速度不匹配时,缓冲区可以平衡数据的传输,避免数据丢失或传输阻塞。

  2. 数据处理的灵活性:缓冲区可以暂时存储数据,使得程序能够按照自己的速度处理数据,而不受外部数据产生或消费速度的限制。这种灵活性有助于提高程序的性能和稳定性。

  3. 数据交互的安全性:缓冲区可以对输入数据进行有效的验证和处理,防止恶意输入或缓冲区溢出等安全问题。通过合理设置缓冲区大小和数据校验机制,可以保护系统免受攻击。

  4. 系统资源的管理:缓冲区可以帮助管理系统资源的分配和使用,避免资源的浪费和不必要的阻塞。通过合理设计缓冲区的大小和处理策略,可以优化系统的性能和资源利用率。

最主要的是提高效率:聚集数据,一次拷贝

2.语言级别的缓冲区

file20

在文件I/O操作中,提到的缓冲区既可以是操作系统级别的,也可以是C语言标准库或特定编程语言库提供的。这两者在某些情况下是协同工作的,但它们的实现和用途有所不同。

操作系统级别的缓冲区

  • 当操作系统读取或写入文件时,它通常会使用内部缓冲区来优化磁盘操作。操作系统会根据需要,将一部分数据从磁盘读取到内存中的缓冲区,或者将缓冲区中的数据写入磁盘。这种缓冲区的管理对应用程序来说是透明的,应用程序不需要直接与之交互。

C语言标准库提供的缓冲区

  • 在C语言中,标准库函数(如freadfwritefgetcfputc等)也使用了缓冲区。这些函数在内部维护了一个缓冲区,用于存储从文件读取的数据或待写入文件的数据(通常被称为“用户空间缓冲区”或“标准I/O缓冲区”。)。当使用这些函数进行文件操作时,数据首先被读取或写入到这个内部缓冲区,然后再由库函数决定何时将数据从缓冲区传输到磁盘或从磁盘加载到缓冲区

  • 当你调用例如fwrite函数写入数据时,这些数据首先被写入到这个用户空间缓冲区中,而不是直接写入到操作系统或硬件的缓冲区。当缓冲区满或者显式调用fflush函数时,C语言标准库才会将缓冲区中的数据传递给操作系统,由操作系统进一步处理。

    操作系统也有自己的缓冲区,用于与硬件进行交互,例如磁盘I/O。操作系统的缓冲区通常被称为“内核缓冲区”或“系统缓冲区”。这些缓冲区用于在内核空间与用户空间之间传输数据,以及将数据从内存传输到磁盘或从磁盘加载到内存

    因此,可以说C语言的缓冲区(用户空间缓冲区)在将数据写入文件时,会先将数据存储在用户空间的缓冲区中,然后在适当的时候(如缓冲区满或显式调用fflush)将数据传递给操作系统的缓冲区(系统缓冲区),最后由操作系统负责将数据写入磁盘。

  1. 这里c语言级别的缓冲区能大大减少我们调用系统调用的次数。 (也是提高效率)
  • 没有缓冲区时,我们每有一个字符,就要调用一次系统接口(write()函数)

  • 此时有了缓冲区,我们可以每次放进缓冲区里,最后再进行调用系统接口。只要调用一次

    系统调用也是有成本的

  1. 有了缓冲区,能大大提高c语言中io函数的返回效率,减少使用时间。
  • 如printf函数只要写到缓冲区后,就能返回了

仔细一想:我们用户也只能通过调用OS提供的接口来让OS进行文件操作,那么c语言的缓冲区就是在调用系统接口上进行的优化

2.1刷新策略

缓冲区的刷新策略指的是何时将缓冲区中的数据写入到输出设备或从输入设备读取新数据的策略。在 C 语言中,通常有以下几种缓冲区刷新策略:

  1. 全缓冲:当缓冲区填满或者遇到换行符 \n 时,缓冲区会被刷新,数据被写入到输出设备或者从输入设备读取新数据。这种策略通常用于文件 I/O 操作,例如 stdio 中的 FILE 结构。
  2. 行缓冲:当遇到换行符 \n 时,缓冲区会被刷新,数据被写入到输出设备或者从输入设备读取新数据。这种策略通常用于标准输入/输出流,例如 stdoutstdin
  3. 无缓冲:数据不会被缓存,而是立即写入到输出设备或者从输入设备读取。这种策略通常用于特定的设备或者特殊的 I/O 操作。

当我们强制刷新时,或者进程结束。缓冲区也会刷新

在 C 语言中,可以使用 setbuf()setvbuf()fflush() 等函数来控制缓冲区的刷新策略。例如,fflush() 函数可以强制将缓冲区中的数据立即写入到输出设备,而不必等到缓冲区被填满或遇到换行符。

2.2具体在哪里

file21

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include<string.h>
int main()
{
	const char* s1 = "write\n";
	write(1, s1, strlen(s1));

	const char* s2 = "fprintf\n";
	fprintf(stdout, "%s", s2);

	const char* s3 = "fwrite\n";
	fwrite(s3, strlen(s3), 1, stdout);

	return 0;
}

file22

这里我们把结果重定向到test.txt。现在是正常的,但是下面我们做出一些改动。

int main()
{
	const char* s1 = "write\n";
	write(1, s1, strlen(s1));

	const char* s2 = "fprintf\n";
	fprintf(stdout, "%s", s2);

	const char* s3 = "fwrite\n";
	fwrite(s3, strlen(s3), 1, stdout);

	fork();
	return 0;
}

file23

一开始我们都是向标准输出打印,采用行缓冲的刷新策略;但是我们使用重定向到test.txt 文件了,刷新策略变为全缓冲

  • fprintf与fwrite都是写入进stdout的缓冲区。缓冲区本身也是数据。

  • 进程结束时,会刷新缓冲区,刷新到操作系统。刷新的本质就是清空,也是修改数据,那就会发生写时拷贝

  • 父进程与子进程都刷新一次,一共两次

write接口是直接放到操作系统的缓冲区的,与进程没有关系

当调用 write 系统调用时,数据会被写入到操作系统的内核缓冲区中。内核缓冲区是操作系统用来暂存数据的内存区域,数据在这里等待被写入到文件中

2.3支持格式化

缓冲区在 C 语言中支持输入输出的格式化操作,使得程序员可以方便地对输入和输出的数据进行格式化处理。键盘和显示器都是字符设备,通过缓冲区的格式化操作,可以实现对键盘输入和显示器输出的数据进行格式化控制。

  1. 输入格式化操作:当用户从键盘输入数据时,这些数据首先会被存储在输入缓冲区中。程序员可以使用输入格式化函数如 scanf() 来从输入缓冲区中读取数据,并根据指定的格式进行解析和处理

  2. 输出格式化操作:当程序需要将数据输出到显示器时,这些数据会先被存储在输出缓冲区中。程序员可以使用输出格式化函数如 printf() 来将数据按照指定的格式输出到显示器上。通过格式化字符串中的格式控制符,可以指定输出的数据类型、宽度、精度等信息。


3.自己来模拟一下缓冲区

3.1项目文件规划

file23.5

  • mystdio.h:用来编写FILE结构体,和各种接口的声明
  • mystdio.c:用来具体实现各种接口
  • test.c:用来进行功能的测试

makefile内容一览:

filetest:mystdio.c test.c
	gcc -o $@ $^
.PHONY:clean
clean:
	rm filetest -f

3.2mystdio.h

#pragma once

#define SIZE 4096//缓冲区大小
#define NONE_FLUSH (1<<1) 
#define LINE_FLUSH (1<<2) 
#define FULL_FLUSH (1<<3) //三种刷新模式

typedef struct _myFILE
{
    //...
    char outbuffer[SIZE];//这是缓冲区
    int pos;//这是缓冲区下标
    int cap;//缓冲区容量
    int fileno;//文件的no编号
    int flush_mode;//缓冲区刷新模式
}myFILE;


myFILE* my_fopen(const char* pathname, const char* mode);

int my_fwrite(myFILE* fp, const char* s, int size);

void my_fflush(myFILE* fp);

void my_fclose(myFILE* fp);


3.3mystdio.c

#include<stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include"4.22.h"

myFILE* my_fopen(const char* pathname, const char* mode)
{
	int flag = 0;
	if (strcmp(mode, "r") == 0)
	{
		flag |= O_RDONLY;
	}
    else if (strcmp(mode, "w") == 0)
    {
        flag |= (O_CREAT | O_WRONLY | O_TRUNC);
    }
    else if (strcmp(mode, "a") == 0)
    {
        flag |= (O_CREAT | O_WRONLY | O_APPEND);
    }
    else
    {
        return NULL;
    }

    int fd = 0;
    if (flag & O_WRONLY)
    {
        fd = open(pathname, flag, 0666);
    }
    else
    {
        fd = open(pathname, flag);
    }
    if (fd < 0)
    {
        return NULL;
    }

    myFILE* myf = (myFILE*)malloc(sizeof(myFILE));
    myf->pos = 0;
    myf->cap = SIZE;
    myf->fileno = fd;
    myf->flush_mode = LINE_FLUSH;

    return myf;
}

void my_fflush(myFILE* fp)
{
    write(fp->fileno, fp->outbuffer, fp->pos);
    fp->pos = 0;
}

int my_fwrite(myFILE* fp, const char* s, int size)
{
    memcpy(fp->outbuffer+fp->pos, s, size);
    fp->pos += size;

    if (fp->flush_mode == LINE_FLUSH && fp->outbuffer[fp->pos - 1] == '\n')
    {
        my_fflush(fp);
    }
    else if (fp->flush_mode == LINE_FLUSH && fp->cap==fp->pos)
    {
        my_fflush(fp);
    }
}


void my_fclose(myFILE* fp)
{
    my_fflush(fp);
    close(fp->fileno);
    free(fp);
    fp = NULL;
}

3.4test.c

#include"mystdio.h"
#include<stdio.h>
#include<string.h>

const char* filename="./log.txt";

int main()
{
    myFILE* fp=my_fopen(filename,"w");
    if(fp==NULL) return 1;

    const char* buffer="hellow,there are myfile";
    my_fwrite(fp, buffer, strlen(buffer));

    my_fclose(fp);
    return 0;
}

file23.6


4.文件系统

4.1磁盘机械结构

file24

硬盘驱动器是计算机中常见的数据存储设备,它由多个组件组成,其中包括盘片、磁头、主轴、马达等:

  1. 盘片(Platter):硬盘驱动器通常包含多个盘片,每个盘片都是一个圆形的磁性介质,通常由金属或玻璃制成,表面被覆盖上磁性材料。数据存储在盘片的表面上,每个盘片都有内侧和外侧两面,数据存储在不同的磁道上。

  2. 磁头(Head):硬盘驱动器中的磁头负责读取和写入数据。磁头通过在盘片表面上移动来访问不同位置上的数据,它可以在盘片上创建磁场来表示数据的0和1。通常,硬盘驱动器会有多个磁头,每个磁头对应一个盘面,可以同时读写多个盘片上的数据。

    磁头的来回是为了定位磁道的,盘片的旋转是为了定位(寻址)制定磁道上的扇区的

  3. 主轴(Spindle):主轴是硬盘驱动器中的一个旋转部件,它负责旋转盘片。盘片被安装在主轴上,主轴通过马达驱动盘片旋转,通常转速在几千转每分钟到一万转每分钟之间,不同硬盘驱动器的转速可能有所不同。

  4. 马达(Motor):硬盘驱动器中的马达负责驱动主轴旋转、移动磁头和控制盘片的位置。马达通常包括主轴电机、磁头马达和定位马达等部件,它们协同工作以确保盘片的旋转、磁头的移动和数据的读写。

  5. 控制器(Controller):硬盘驱动器中的控制器负责管理数据的读写、磁头的移动、盘片的旋转等操作。控制器通常包括逻辑控制器和驱动器电路板,它们与计算机系统进行通信,控制硬盘驱动器的各项功能。

我们之前说的0与1,实际上只是一种抽象的概念。在不同的设备和存储介质中,0和1的表示方式可能会有所不同,这取决于具体的存储技术和物理原理

盘片上的数据存储是通过磁性材料来实现的,磁性材料可以在不同的磁场方向上表示0和1

  1. 磁性材料:盘片表面被覆盖上一层磁性材料,通常是氧化铁或类似的磁性材料。这种磁性材料可以在外加磁场的作用下保持磁性,并且可以在不同方向上表示不同的磁极性,从而实现数据的存储。
  2. 磁场的方向:硬盘驱动器中的磁头可以在盘片表面上创建磁场,通过改变磁场的方向来表示数据的0和1。当磁场方向朝向盘片表面时,表示为1;当磁场方向远离盘片表面时,表示为0。通过这种方式,磁头可以在盘片上创建一系列的磁场来表示数据的二进制形式。

4.2磁盘的物理存储

在这里插入图片描述

硬盘的物理储存结构主要包括磁道、扇区和柱面,这些是硬盘上数据存储的基本单位。下面我将简要解释它们的含义和作用:

  1. 磁道:硬盘的盘片表面被划分成多个同心圆环,每个环称为一个磁道,每一个都有自己的编号。磁道是硬盘上的存储单位之一,数据在磁道上被存储和组织。硬盘的磁头可以沿着磁道移动,读取或写入数据。
  2. 扇区:每个磁道被划分成若干个扇区,扇区是硬盘上存储数据的最小单位(基本单位)。通常,一个扇区的大小是512字节或4KB。当数据被写入硬盘时,它会被分割成适当大小的扇区,并存储在硬盘的不同扇区中。
  3. 柱面:硬盘上的每个盘片都有多个磁道,而所有盘片上相同位置的磁道组成一个柱面。磁头可以同时读取或写入同一柱面上的多个磁道,这有助于提高数据的读写效率。
  4. 盘面:硬盘的盘片表面被划分成多个盘面,每个盘面都可以存储数据。通常,硬盘有多个盘片叠在一起,每个盘片都有两个盘面(正面和背面),每一个也有自己的编号

如果我们想要定位一个扇区:

  1. 定位到柱面
  2. 确定是柱面里的哪一个磁头
  3. 找到目标扇区

上述方法称之为:CHS(柱面、磁头、扇区)定位法。系统会通过柱面号、磁头号和扇区号来唯一地确定硬盘上的一个位置。通过这种方式,系统可以精确地定位到目标扇区,以读取或写入文件的数据。

任何文件就是由多个扇区的数据构成的,系统在读取或写入文件时会逐个扇区地进行操作

4.3磁盘的逻辑存储

file26

那么如何通过下标来确定实际位置呢?(下标如何转变CHS)

  1. 确定盘片:首先将数组下标除以单盘的大小(一个盘的扇区数量),得到该数组元素所在的盘片号。

  2. 计算在盘片内的偏移量:将数组下标取模单盘大小,得到在盘片内的偏移量

  3. 确定磁道:将在盘片内的偏移量除以一个磁道的扇区个数,得到该数组元素所在的磁道号。

  4. 确定扇区:将在盘片内的偏移量取模一个磁道的扇区个数,得到该数组元素所在的扇区号。

  5. 最终的物理地址即为盘片号、磁道号和扇区号的组合。

最后我们把一个800G的磁盘管理,经过分区分组。转变为对10G的区域进行管理,那具体怎么管理呢?

我们先来看看Linux下的文件特性

  • 文件=内容+属性

  • 内容的大小不确定,可能很大,可能很小

  • 属性的大小是固定的:属性的类别是一样的,但是每个类别里的内容不一样。文件名不属于属性

    struct inode//文件的属性集
    {
    	//类型
    	//大小
    	//权限
    	//时间
    	//...
    	int inode_num;//inode的编号
    };
    

    大小是固定的128字节,4kb/128b=32(一个数据块block能存32个文件的属性)。 系统中标识一个文件使用的是inode

    inode编号,在一个分区里是唯一的

newnewfile27

  1. i节点表(inode Table):存放文件属性如文件大小,所有者,最近修改时间等。里面存的是一个个inode

    表里面那么多inode,我们如何知道哪个被使用,哪个没有使用

  2. inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用

    比特位的位置:代表第几个inode

    比特位的位置内容:代表这个inode是否被使用

  3. 数据区(Data blocks):存放文件内容(这是一个非常非常大的区,里面有很多4kb的小数据块

file28

下面我们可以看到,最多对应15个block,那文件内容太大,不够用怎么办?

  • 下标[0,11]就正常存使用的block,保存文件的内容
  • 下标[12,13]指向的block里存的其他被使用的block的编号
  • 下标[14]指向的block里存的也是其他block的编号,这些block里存的又是被使用的block的编号
  1. 块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用

    比特位的位置:代表第几个4kb的数据块

    比特位的位置内容:代表这个数据块是否被使用

    struct inode
    {
    	//...
    	int block[15];//里面对应一个个使用的数据块的编号
    	//...
    };
    

    那么现在:我们有了文件的inode编号后,就能在inode Table里找到inode,然后能进一步找到block(该文件对应的数据块)和其他属性

  2. GDT(Group Descriptor Table):块组描述符,描述块组属性信息(一个块的宏观使用情况)

  3. 超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了(不是所有的block group里都有super block,个别才有)

    多个super block是为了进行数据备份

    不是全都有是因为每次一变动,每个都要更新。降低了效率

    Super Block 的备份通常是为了提高文件系统的容错性和可靠性。每个 Block Group 并不一定都有 Super Block,而是选择性地放置。这样做可以减少每次写操作时需要更新的 Super Block 数量,从而提高文件系统的性能

新建一个文件都干了什么呢?

  1. 在inode位图中找到一个未被使用的inode编号

  2. 填写inode结构体:根据新的inode编号在inode表里找到,填写inode结构体,包括文件的属性信息(如文件类型、权限、大小、创建时间等)和指向数据块的指针

  3. 更新inode表:将填写好的inode结构体写入inode表中,以便后续查找和访问。同时,更新inode位图中对应inode的状态为已占用。

  4. 分配数据块:根据文件大小和文件系统的块大小,确定需要分配的数据块数量,并在块位图中找到未被使用的数据块。将这些数据块分配给新文件,并更新块位图中对应数据块的状态为已占用。

  5. 将文件内容写入数据块:将文件的内容写入分配的数据块中,以完成文件的创建。文件的内容可以是文本、图像、音频等任意类型的数据。

删除一个文件呢?

  1. 找到文件的inode:首先,通过文件名在目录结构中查找到文件对应的inode号码。
  2. 释放数据块:根据文件的inode结构体中记录的数据块指针,找到文件所占用的数据块,并将这些数据块标记为未使用状态,以便后续被其他文件使用。
  3. 更新inode位图和块位图:将文件的inode和数据块对应的位图中的相应位标记为未使用状态,以释放这些资源。

这里我们看到不对数据块里的内容进行清理,只是标志为未使用(这样效率更快)。数据块内容会在后续被其他文件写入时覆盖,或者由文件系统的垃圾回收机制在需要时清理

找一个文件,就是通过inode编号找,前提:我们怎么知道文件在哪一个分组里面

file29

4.4文件系统

我们上面讲的那个区域里的文件系统称为Ext* 。是一系列的 Linux 文件系统,最常见的是 Ext2、Ext3 和 Ext4

每一个分区有自己的文件系统。我们对分区初始化本质是向指定分区,写入全新的文件系统

Ext2(Second Extended File System)

  • 特点: Ext2 是 Linux 中最早的可用文件系统之一,它是 Ext 文件系统的改进版本,引入了许多新的特性,如索引节点 (inode) 和快速文件系统 (Fast File System)。
  • 优点: Ext2 文件系统简单、可靠,并且在 Linux 社区得到了广泛的支持和应用。它的设计目标是提供一个高性能的文件系统,同时保持数据的稳定性和一致性。
  • 缺点: Ext2 文件系统不支持日志功能,因此在系统崩溃或意外断电时可能会导致数据丢失或损坏。同时,Ext2 文件系统没有提供数据的回收和压缩功能,会导致存储空间的浪费。

5.文件名

前面我们的起点都是inode编号,那我们一开始怎么得到inode呢?

  • 目录也是文件。目录=inode+目录的内容
  • 目录的内容都是——目录里的文件名与inode编号的映射关系
  • 内容实际上就是一个映射表,将文件名映射到对应的inode编号。当你查看一个目录的内容时,你实际上是在查看这个映射表。这就是为什么目录本身也是一个文件,并且也有它自己的inode

我们之前讲过,对于目录没有w权限,就不能添加、删除文件。因为这些涉及对目录文件内容的修改

对于目录没有r权限,那么你将无法查看目录中的文件列表。因为我们无法读文件内容,不能查看文件名与inode编号的映射关系

5.1再看文件的增删改查

对一个文件进行的增删查改操作,都与该文件所处的目录有密切关系。这是因为文件系统中的文件和目录是以树形结构组织的,每个文件都通过其所在的目录进行定位和管理

那么,我们想要知道目录的inode,那就要知道目录所处目录的内容,才能知道目录对应的inode。以此类推

想要找到一个文件,我们要有路径,首先进行路径解析,从根目录往下进行。系统能够最终定位到指定路径的文件或目录,并获取其inode编号

  1. 增(创建文件)

    1. 在一个目录中创建一个新的文件,需要为该文件分配一个新的inode号。
    2. 在文件系统的inode表中查找一个未被使用的inode,分配给新文件。
    3. 在分组中分配数据块给新文件存储数据,并将数据写入这些数据块中。
    4. 在当前目录中,将新文件的名称与分配的inode号建立映射关系,这样就完成了新增文件的操作
  2. 删(删除文件)

    1. 根据文件名称查找到对应的inode编号,进而确定文件所在的分组。
    2. 修改inode bitmap内容,将该inode对应的位标记为未使用,表示该inode已经被释放。文件内容使用的数据块,并将这些数据块标记为未使用状态
    3. 在当前目录文件中删除该文件的名称与inode编号的映射关系,完成文件的删除操作
  3. 查(查找文件)

    1. 根据文件名称查找到对应的inode编号,确定文件所在的分组。
    2. 根据inode编号找到文件的数据块信息,获取文件的内容。
  4. 改(修改文件)

    1. 根据文件名称查找到对应的inode编号,确定文件所在的分组。
    2. 根据inode编号找到文件的数据块,对数据块中的内容进行修改。

inode编号只在一个分区里是唯一的,那我们怎么知道是在哪个分区

在Linux系统中,被写入文件系统的分区在被使用之前需要进行“挂载”操作。挂载的主要目的是将文件系统的分区与目录树中的某个目录关联起来,从而使得用户可以通过该目录访问和操作分区中的文件

在Linux系统中,每个分区都会挂载到文件系统的某个挂载点(mount point)下。通过查看文件或目录的路径,我们可以确定它所属的挂载点,进而确定它所在的分区(能直接根据路径看在哪个分区里,进行路径前缀匹配)

当在执行一些命令时,如果没有指定完整的文件路径,系统会根据当前进程的执行路径来查找文件。这是因为在Linux系统中,有一个环境变量叫做PATH,它包含了一系列目录路径,系统会根据这些路径来搜索可执行文件。

类似的,对于一些命令,如find等,如果您没有指定完整的路径,系统会从当前目录开始搜索文件。这是因为这些命令会使用当前进程的工作目录作为搜索起点

因此,当您在执行命令时只提供文件名而没有路径时,系统会首先在当前目录下搜索这个文件名,然后再根据PATH环境变量或当前目录来查找文件。

系统会按照以下步骤来查找可执行文件:

  1. 首先,系统会检查当前进程的工作目录(也就是执行命令时所处的目录)下是否存在与提供的文件名相匹配的文件。如果找到了,系统会执行该文件。

  2. 如果在当前工作目录下没有找到匹配的文件,系统会继续在PATH环境变量指定的目录列表中逐个查找。PATH环境变量包含了一系列目录路径,系统会按照这些路径的顺序来搜索可执行文件。

  3. 如果在PATH指定的所有目录中都没有找到匹配的文件,系统会提示找不到文件的错误。


好啦,这次内容到这里了。感谢大家支持!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/714778.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Burp Suite Professional 2024.5 (macOS, Linux, Windows) - Web 应用安全、测试和扫描

Burp Suite Professional 2024.5 (macOS, Linux, Windows) - Web 应用安全、测试和扫描 Burp Suite Professional, Test, find, and exploit vulnerabilities. 请访问原文链接&#xff1a;Burp Suite Professional 2024.5 (macOS, Linux, Windows) - Web 应用安全、测试和扫描…

JWT令牌、过滤器Filter、拦截器Interceptor

目录 JWT令牌 简介 JWT生成 解析JWT 登陆后下发令牌 过滤器(Filter) Filter快速入门 Filter拦截路径 过滤器链 登录校验Filter-流程 拦截器(Interceptor) Interceptor 快速入门 拦截路径 登录校验流程 JWT令牌 简介 全称:JSON Web Token(https://iwt.io/) …

springboot与flowable(12):网关服务(包容网关)

一、绘制流程图 包容网关可以看作是排他网关和并行网关的结合体。和排他网关一样&#xff0c;可以在外出顺序流上定义条件&#xff0c;包容网关会解析它们。但是主要的区别是包容网关可以选择多余一条顺序流&#xff0c;这和并行网关一样。包容网关的功能是基于进入和外出顺序流…

axure使用中继器画柱状图

源文件在顶部。 在axure通过读取中继器中的数据来画柱状图&#xff0c;如下图&#xff1a; 1&#xff09;创建一个中继器&#xff0c;在里面创建两列&#xff1a;1列是柱状图底部的名称、2列是柱的高度&#xff0c;如下图&#xff1a; 2&#xff09;双击中继器&#xff0c;画一…

华为OD机试 - 多段线数据压缩(Java 2024 D卷 100分)

华为OD机试 2024D卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;D卷C卷A卷B卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测…

ord版本升级(0.15升级到0.18.5)

1、升级rust ~# rustup update stable ~# rustc --versionrustc 1.79.0 (129f3b996 2024-06-10)2、拉取0.18.5代码 ~# wget https://github.com/ordinals/ord/archive/refs/tags/0.18.5.tar.gz ~# tar -xf 0.18.5.tar.gz ~# cd ord-0.18.5 ~# cargo build --release3、启动se…

PDFFactoryFinePrint软件安装包下载+详细安装教程

简介&#xff1a; pdfFactory Pro(虚拟打印机)是一个无须 Acrobat 创建 Adobe PDF 文件的打印机驱动程序。 pdffactory pro虚拟打印机提供了比其他程序提供得更简单、更有效率和更少的花费的创建 PDF 文件的解决方案。用于需要安全的 PDF(法律文档、公司信息等)和其他高级功能…

网络通信架构

BS架构/CS架构 使用协议分别对应&#xff1a; TCP / HTTP 在计算机网络和软件开发中&#xff0c;CS架构&#xff08;Client-Server Architecture&#xff0c;客户端-服务器架构&#xff09;和BS架构&#xff08;Browser-Server Architecture&#xff0c;浏览器-服务器架构&am…

【云岚到家】-day04-2-索引同步-搜索接口

【云岚到家】-day04-2-索引同步-搜索接口 1 索引同步1.1 编写同步程序1.1.1 创建索引结构1.1.2 编写同步程序1.1.2.1 添加依赖1.1.2.2 配置连接ES1.1.2.3 编写同步程序 1.1.3 测试1.1.4 小结1.1.4.1 如何保证CanalMQ同步消息的顺序性&#xff1f;1.1.4.2 如何保证只有一个消费者…

【Linux】进程_6

文章目录 五、进程8. 进程地址空间 未完待续 五、进程 8. 进程地址空间 上图可能很多人都看过了&#xff0c;这里再来验证一下&#xff1a; 验证位置&#xff1a; 验证堆栈的生长方向&#xff1a; 在上面的空间布局图中&#xff0c;有一个疑问&#xff0c;画的空间是 内存…

el-pagination 切换分页条数,会出现两次请求

文章目录 前言一、问题展示二、源码展示 前言 继上一次发现el-pagination在删除的时候pageNum不更新的问题。这次又发现了&#xff0c;切换分页条数&#xff0c;会出现两次请求。网上有很多解决方案&#xff0c;我就不多说了&#xff0c;我就简单记一下为啥会出现两次请求的问…

决策树算法:揭示数据背后的决策逻辑

目录 一 决策树算法原理 特征选择 信息增益 信息增益比 基尼指数 树的构建 树的剪枝 预剪枝 后剪枝 二 决策树算法实现 一 使用决策树进行分类 数据预处理 构建决策树模型 二 使用决策树进行回归 数据预处理 构建决策树回归模型 三 决策树算法的优缺点 优点 …

【Java】已解决java.lang.NoClassDefFoundError异常

文章目录 一、问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决java.lang.NoClassDefFoundError异常 一、问题背景 java.lang.NoClassDefFoundError 是 Java 运行时环境&#xff08;JRE&#xff09;在尝试加载某个类时&#xff0c;但没有找到…

《web应用技术》第十一次作业

1、验证过滤器进行权限验证的原理。 代码展示&#xff1a; Slf4j WebFilter(urlPatterns "/*") public class LoginCheckFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) thro…

轻松实现H5页面下拉刷新:滑动触发、高度提示与数据刷新全攻略

前段时间在做小程序到H5的迁移&#xff0c;其中小程序中下拉刷新的功能引起了产品的注意。他说到&#xff0c;哎&#xff0c;我们迁移后的H5页面怎么没有下拉刷新&#xff0c;于是乎&#xff0c;我就急忙将这部分的内容给填上。 本来是计划使用成熟的组件库来实现&#xff0c;…

手把手教你如何在Windows11下安装Docker容器

文章的主要要点&#xff1a; 为什么使用Docker&#xff1a;Docker可以简化部署过程&#xff0c;特别适合新手或在学习新技能&#xff08;如Redis、MySQL、消息队列、Nginx等&#xff09;时使用。 安装前的准备&#xff1a;在安装Docker之前&#xff0c;需要在Windows中开启一些…

python13 元组类型

元组用 () 声明&#xff0c;注意如果只有一个元素时要在元素后面加个 逗号, 否则不是类型就不是元组了。 声明方式2内置函数声明 data tuple(helloworld); 元组是不可变列表&#xff0c; 元组可以使用序列的所有功能。具体可以看我以前序列的文章 元组里的元素可以是多种数据类…

zynq qemu模拟器环境搭建

qemu是硬件模拟器&#xff0c;方便有些同学没得开发板想验证一些driver是否在指定板卡上可以测试&#xff0c;qemu就实现了该功能&#xff0c;选择qemu模拟器最好是选择cpu厂商指定qemu源码&#xff0c;这样测试结果更加逼真。本文章主要介绍如何搭建zynq平台qemu模拟器环境。 …

​单级高频谐振小放

目录 高频交流等效电路 质量指标 增益 通频带 选择性 高频交流等效电路 质量指标 增益 YL撇是怎么来的。 通频带 选择性

12 款 Android 照片恢复应用程序列表

丢失难忘的照片总是令人痛苦的。如果软件崩溃或意外删除&#xff0c;Android 设备上的照片也可能会丢失。这时照片恢复应用程序就派上用场了。查看我们为 Android 收集的顶级照片恢复应用程序。 但是&#xff0c;您不会想为自己选择任何照片恢复应用程序。因此&#xff0c;我们…