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

news2024/12/23 5:36:58

上次介绍了: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.coloradmin.cn/o/1829308.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据库编程-SQLite3(一)】sqlite3数据库在Windows下的配置及测试

学习分析 1、资源准备2、环境配置2.1、将资源包下载解压缩保存。2.2、在QT中创建工程,配置环境 3、测试配置3.1、 sqlite3_open函数3.2、sqlite3_close函数3.3、代码测试 1、资源准备 资源包 2、环境配置 2.1、将资源包下载解压缩保存。 解压缩得到以下文件 2.2、在QT中创建…

JavaFX VBox

VBox布局将子节点堆叠在垂直列中。新添加的子节点被放置在上一个子节点的下面。默认情况下&#xff0c;VBox尊重子节点的首选宽度和高度。 当父节点不可调整大小时&#xff0c;例如Group节点&#xff0c;最大垂直列的宽度基于具有最大优选宽度的节点。 默认情况下&#xff0c;…

逢3必过报数游戏-第13届蓝桥杯省赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第84讲。 逢3必过报数游戏&…

guli商城业务逻辑-基础篇笔记

这里写目录标题 0.1 viscode设置用户代码片段1.实现多级菜单接口1.1 对接前端菜单1.2 对接网关接口解决跨域问题&#xff0c;如果不解决跨域&#xff0c;浏览器还是访问不了api1.3 把商品服务添加网关1.4 修改前端显示分类菜单1.5 给菜单添加删除修改功能1.5.1 删除功能的后端业…

安卓在Fragment控制状态栏显示隐藏

废话不多上效果 隐藏 显示 核心代码 首先是Framgrent package com.zx.tab;import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button;impor…

分布式系统中的经典思想实验——两将军问题和拜占庭将军问题

文章目录 一、两将军问题1.1 问题描述1.2 深入理解两将军问题1.3 实验结论 二、拜占庭将军问题2.1 问题描述2.2 深入理解拜占庭将军问题2.3 解决方案 三、两将军和拜占庭问题的关系3.1 区别和联系3.2 应用与现实意义 参考资料 一、两将军问题 1.1 问题描述 两将军问题描述的是…

使用了代理IP怎么还会被封?代理IP到底有没有效果

代理IP作为一种网络工具&#xff0c;被广泛应用于各种场景&#xff0c;例如网络爬虫、海外购物、规避地区限制等。然而&#xff0c;很多用户在使用代理IP的过程中却发现自己的账号被封禁&#xff0c;这让他们不禁产生疑问&#xff1a;使用了代理IP怎么还会被封&#xff1f;代理…

基于粒子群优化算法的的微电网多目标优化调度----算法改进

前言&#xff1a; 当阅读过前一篇我的博客之后&#xff0c;并且认真去读懂了那篇文章末尾的代码&#xff0c;那么&#xff0c;后续的算法改进对于你来说应当是很容易的了。前文中提及过&#xff0c;粒子群在进行迭代时&#xff0c;每迭代一次&#xff0c;都会根据自己个体最优值…

4 最简单的 C 程序设计—顺序程序设计-4.6 顺序结构程序设计举例

【例 4.14】输入三角形的三边长&#xff0c;求三角形面积。 #include <stdio.h> #include <math.h> // 包含数学函数头文件main() {/* 【例 4.14】输入三角形的三边长&#xff0c;求三角形面积。已知三角形的三边长 a,b,c&#xff0c;则该三角形的面积公式为&…

【Java04】引用变量数组初始化的内存机制

引用类型数组指向的元素也是引用。其本质是&#xff1a; 由一个在栈上的引用数组变量指向一块堆内存&#xff1b;这块堆内存里存储的元素是引用&#xff0c;又分别指向其他堆内存。 class Person // Person是一个自定义的类 {public int age;puiblic double height;public vo…

mybatis-plus使用拦截器实现sql完整打印

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen 在使用mybatis-plus&#xff08;mybatis&#xff09;的时候&#xff0c;往往需要…

DDPM公式推导(四)

3 Diffusion models and denoising autoencoders 扩散模型可能看起来是一类受限制的潜在变量模型&#xff0c;但它们在实现中允许很大的自由度。必须选择正向过程的方差 β t \beta_t βt​以及逆向过程的模型架构和高斯分布参数化。为了指导我们的选择&#xff0c;我们在扩散…

解决必剪电脑版导出视频缺斤少两的办法

背景 前几天将电脑重置了&#xff0c;今天想要剪辑一下视频&#xff0c;于是下载了必剪&#xff0c;将视频、音频都调整好&#xff0c;导出&#xff0c;结果15分钟的视频只能导出很短的时长&#xff0c;调整参数最多也只能导出10分钟&#xff0c;My God&#xff01; 解决 首…

Win10“始终使用此应用打开”不见了怎么办?

问题背景 真是服了&#xff0c;昨天家里停电把我电脑系统盘固态烧掉了&#xff0c;于是换了个新的固态给电脑装上新系统。结果这个版本的Win10系统居然无法修改默认应用。具体问题见下面两个图&#xff0c;以py文件为例。 图一&#xff1a;“选择打开方式时没有始终使用此应用…

深入理解指针(四)

目录 1. 回调函数是什么? ​2. qsort使用举例 2.1冒泡排序 2.2使用qsort函数排序整型数据 ​2.3 使用qsort排序结构数据(名字) 2.4 使用qsort排序结构数据(年龄) 3. qsort函数的模拟实现 1. 回调函数是什么? 回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数…

计算机组成原理之定点加法与减法运算

文章目录 补码的加减法算法的流程与逻辑实现溢出判断溢出原因单符号位判断双符号位&#xff08;变形补码&#xff09; 基本的加法/减法器舍入方法习题 补码的加减法 数用补码表示&#xff0c;符号位参与运算 考虑几个问题&#xff1f; 1.实际操作能否只取决于操作码&#xff1f…

基于CentOS Stream 9平台 安装/卸载 Redis7.0.15

已更正systemctl管理Redis服务问题 1. 官方下载地址 https://redis.io/downloads/#redis-downloads 1.1 下载或上传到/opt/coisini目录下&#xff1a; mkdir /opt/coisini cd /opt/coisini wget https://download.redis.io/releases/redis-7.0.15.tar.gz2. 解压 tar -zxvf re…

docker拉取镜像太慢解决方案

前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 创建daemon.json文件,输入以下信息 vim /etc/docker/daemon.json{"registry-mirrors": ["https://9cpn8tt6.mirror…

搭建WWW服务

1.实验环境的配置 【1】设置windows虚拟机server和test网络属性 打开虚拟机的【开始】菜单->【控制面板】->【网络连接】窗口。 1. 选中【本地连接】右击鼠标&#xff0c;选中【属性】&#xff0c;打开【本地连接属性】窗口。 2. 选择【网络】页签。 3. 在【此连接使…

vue3实现表格的分页以及确认消息弹窗

表格的分页实例展示 效果1:表格按照每行10条数据分页,且编号也会随之分页自增 实现按照页码分页效果 第二页 展示编号根据分页自动增长 固定表格高度 这边设置了滚动条,同时表格高度实现自适应滚动条高度 template部分 表格代码 编号是按照页码条数进行循环并根据索引自增…