【Linux】基础IO——文件描述符/缓冲区/重定向/文件系统

news2024/9/22 0:58:39

文章目录

  • 一、文件描述符
  • 二、缓冲区
  • 三、重定向的原理
  • 四、文件系统 (Linux Ext2)
    • 1 认识磁盘的结构
      • CHS
      • LBA
      • Block
    • 2 认识文件系统
      • 2.1 分区
      • 2.2 文件系统的结构
      • 2.3 剖析inode
      • 2.4 文件的操作
    • 3 软硬链接
      • 3.1 软链接
      • 3.2 硬链接


  • 📝 个人主页 :超人不会飞)
  • 📑 本文收录专栏:《Linux》
  • 💭 如果本文对您有帮助,不妨点赞、收藏、关注支持博主,我们一起进步,共同成长!

一、文件描述符

💭当一个进程打开一个文件时,有趣的事情就发生了。

  1. OS会跟踪进程打开的文件,并为其创建一个结构体(struct_file),该结构体中维护着打开文件的状态和信息,如:文件指针、文件在磁盘中所在位置、访问权限、inode元信息等。
  2. 为了建立进程与文件的联系,进程PCB维护着一个结构体(struct_files),该结构体中最重要的就是包含了一个指针数组,数组中每个指针指向进程打开的文件(内核中的结构体)。多个这样的结构体,组成了打开文件表,每一个结构体称为打开文件句柄
  3. 文件描述符(file descriptor)本质上就是该数组的下标,上面的过程,总结一下就是:进程每打开一个文件,都会为该文件分配一个文件描述符。因此,在OS层面,文件描述符是一个文件的唯一标识。

Linux的设计思想是,一切皆文件,当然也包括显示器和键盘这些设备。每个进程启动时,会默认打开三个文件流(内核中维护文件状态信息的结构体):标准输入、标准输出、标准错误。标准输入对应的是键盘,标准输出和标准错误对应的是显示器。(可以理解为,这些外设设备都有驱动程序,驱动程序一般是保存在磁盘中的文件,打开这些设备就是将对应的驱动程序文件打开到内存中)。

标准输入、标准输出、标准错误的文件描述符分别为0、1、2。

在这里插入图片描述

文件描述符的分配规则:最小未使用。

系统接口中的open,作用是打开一个文件,返回值就是文件描述符。这是系统调用打开文件的方法,各种语言中的打开文件接口底层都调用了open(如C语言的fopen)。

NAME
       open, creat - open and possibly create a file or device

SYNOPSIS
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>

       int open(const char *pathname, int flags);
       int open(const char *pathname, int flags, mode_t mode);
RETURN VALUE
       open() and creat() return the new file descriptor, or -1 if an error occurred (in which case, errno is set appropriately).

二、缓冲区

💭如果用户想要向一个文件中写入数据或读取数据,文件在磁盘中,进程维护在内存中,若每读写一次就要与磁盘进行一次IO,效率会十分低下。因此便有了缓冲区的概念。缓冲区将用户想要读写的数据先缓存起来,达到一定的条件时在与磁盘进行IO,而不是写一次IO一次,大大提高了读写效率。

缓冲区分为:用户级缓冲区和内核级缓冲区

用户级缓冲区: 用户级缓冲区一般是在进程的堆栈区中创建的,其存在的意义是减少系统调用次数,从而降低操作系统在用户态和内核态之间切换所耗费的时间。例如,在C语言中,FILE结构体用来维护文件的状态和信息,其中就维护了一个用户级的缓冲区,fwrite、fputs等接口都是将数据写入这个缓冲区里面,而不是直接写入文件。

内核级缓冲区: 每一个打开文件都会在内核中维护一个缓冲区,称为内核缓冲区。内核缓冲区存在的意义减少IO次数,提高向文件写数据的效率。当调用write时,会从用户级缓冲区的数据写入内核级缓冲区。调用read时,将数据从内核级缓冲区写入用户级缓冲区。调用write不一定会触发OS与磁盘的IO,当内核缓冲区中的数据达到一定数量,才会向磁盘中写入数据。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f9Kd1Ehd-1688355188463)(C:_data\博客\git_blog_pictures\基础IO\image-20230702223741187.png)]

🔎用户级缓冲区向内核级缓冲区刷新数据的策略:

  1. 对于显示器,行缓冲(遇到’\n’就刷新)
  2. 对于普通文件,全缓冲(缓冲区满再刷新)
  3. 无缓冲(有数据就刷新)

🔎内核级缓冲区向文件刷新数据的策略一般由OS决定,用户不可见。

  • C语言的fclose接口,会刷新用户缓冲区再关闭文件。进程结束时,OS会将FILE维护的缓冲区中的数据刷新到内核。

  • 有时候我们也需要强制刷新缓冲区,如C语言中用到了fflush函数,它实际上就是将FILE维护的用户级缓冲区中残余的数据刷新到内核缓冲区中。

  • 如若需要将数据从内核缓冲区刷新到文件中,则是调用系统调用接口void sync(void)(刷新全部缓冲区)或int syncfs(int fd)(刷新文件描述符为fd的文件的缓冲区)

NAME
       sync, syncfs - commit buffer cache to disk
SYNOPSIS
       #include <unistd.h>

       void sync(void);
       int syncfs(int fd);

下面是我基于对C语言FILE结构体和文件操作函数的理解,仿写其实现。感兴趣的小伙伴可以看看

myfile_gitee地址


三、重定向的原理

关于重定向,bash下的部分指令:

类型指令作用
输入重定向file1 < file2将file2的内容作为file1的输入
输出重定向file2 > file1将file2的输出结果拷贝到file1中(file1原数据先清除)
追加重定向file2 >> file1将file2的输出结果追加拷贝到file1中

所谓重定向,其原理是改变文件描述符表中指针的指向,使对一个文件的操作变成对另一个文件的操作。

🌰举例:若要把a.out程序的输出结果重定向到log.txt,即./a.out > log.txt

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dKkuxPOs-1688355188464)(C:_data\博客\git_blog_pictures\基础IO\image-20230703101912740.png)]

如图,将a.out进程维护的文件描述符表中指向标准输出的指针改为指向log.txt文件流,从而log.txt文件获得了文件描述符1。从语言层级,就拿C语言举例,若此时a.out中有printf函数,这是默认朝标准输出传递数据的,也就是文件描述符为1的文件,但此时OS层级已经修改了文件描述符1的指向,但是进程并不知道,所以就实现的输出重定向。

💭除了bash指令,实现重定向的方法

1️⃣先关闭再打开,根据文件描述符的分配规则即可分配到指定描述符。

int main()
{
   umask(0);
   close(1);
   int fd = open("log1.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);

   printf("hello printf\n");
   fprintf(stdout,"hello fprintf stdout\n");
   
   return 0;
}

2️⃣dup系统接口

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

常用的是dup2接口

**介绍:**在文件描述符表中,newfd对应指针成为oldfd对应指针的拷贝,最终留下oldfd,必要时先关闭newfd。若oldfd无效,则调用失败,newfd不会关闭;若oldfd有效,但oldfd与newfd相同,则不会发生任何事,并返回newfd。

**返回值:**成功返回newfd,失败返回-1。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m4M1weKp-1688355188464)(C:_data\博客\git_blog_pictures\基础IO\image-20230703104918291.png)]

💡一些补充知识的总结:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fFRttrLm-1688355188464)(C:_data\博客\git_blog_pictures\基础IO\image-20230703113042408.png)]

💬测试代码(同一个进程多次打开同一个文件)

int main()
{
    int fd1 = open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
    int fd2 = open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);


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

    const char* msg1 = "hello fd1\n";
    const char* msg2 = "hello fd2\n";
    
    //先向fd1写入msg1,再向fd2写入msg2
    //由于是不同打开文件句柄,文件偏移量不同,fd1写入msg1后文件偏移量改变,但fd2的文件偏移量不变
    //因此msg2会覆盖msg1
    write(fd1,msg1,strlen(msg1));
    write(fd2,msg2,strlen(msg2));
    //此时log.txt里的内容是"hello fd2\n"
    
    //再次向fd1写入msg1
    //由于fd1、fd2文件偏移量均已改变且相同,所以此时写入msg1不会发生覆盖
    write(fd1,msg1,strlen(msg1));
    //此时log.txt里的内容是:"hello fd2\nhello fd1\n"

    close(fd1);
    close(fd2);

    return 0;
}

⭕检验结果

[ckf@VM-8-3-centos lesson4_file]$ gcc test4.c
[ckf@VM-8-3-centos lesson4_file]$ ./a.out
fd1=3 fd2=4
[ckf@VM-8-3-centos lesson4_file]$ cat log.txt
hello fd2
hello fd1

四、文件系统 (Linux Ext2)

1 认识磁盘的结构

CHS

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZrQJjn2G-1688355188465)(C:_data\博客\git_blog_pictures\基础IO\image-20230630095604116.png)]

  • 磁头(head)数最大为255 (用 8 个二进制位存储)。从0开始编号。

  • 柱面(cylinder)数最大为1023(用 10 个二进制位存储)。从0开始编号。

  • 扇区(sector)数最大数 63(用 6个二进制位存储)。从1开始编号。

磁盘上,盘片的表面涂有磁性物质,这些磁性物质用来记录二进制数据

磁头用于读取盘面上的数据,一个盘面对应一个磁头,因此用磁头编号来标识某一个盘面。

磁盘的单位是扇区,一个扇区的存储空间大小是512字节。

⭕根据磁盘的物理结构,可以用“第x个盘面(磁头)第y个柱面第z个扇区”来定位数据存储位置,这种方法称为CHS定址法。

LBA

观察盘面的结构可以发现,每个磁道的扇区数相等,但外磁道的扇区面积大于内磁道扇区面积,而扇区的存储量却相同。这会导致外磁道扇区的存储密度低于内磁道扇区的存储密度,造成空间浪费。因此,后来工程师为了平衡内外磁道存储密度,增加了外磁道的扇区数量。这样一来,CHS地址就无法唯一确定一个扇区了,于是又将磁盘空间逻辑抽象成一个线性空间,单位是扇区,如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1eN42BsZ-1688355188465)(C:_data\博客\git_blog_pictures\基础IO\image-20230630095133112.png)]

在这个线性空间中,每个扇区都有一个地址,这个地址称为LBA(逻辑块地址,Logic Block Address)。

LBA由磁盘控制器维护

LBA和CHS可以按照一定的规则相互转换

Block

综上所述,磁盘的最小读写单位是扇区(512KB)。如果每次读取都以此作为最小单位,那么效率会非常低,因此,文件系统将磁盘空间又分为一个个块(Block),每个块由多个扇区组成,每个块的大小在格式化文件系统时决定(ext2中,1K/2K/4K,一般是4K,即八个扇区)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QNlxrzVa-1688355188465)(C:_data\博客\git_blog_pictures\基础IO\image-20230630103459146.png)]

Block大小在格式化文件系统时就确定,不可修改,在ext2文件系统一般为4KB

一个block只能存放一个文件的数据,因此block大小不合适很可能会导致空间浪费。

每一个Block都有自己的编号,本质上可以把这个空间看作是数组,Block编号就是数组下标

自此我们有了三种地址:CHS、LBA、Block号


2 认识文件系统

2.1 分区

💭文件系统是计算机系统中用于组织和管理数据的一种机制或软件。它提供了一种结构化的方法,用于存储、访问、检索和维护计算机系统上的文件和目录。文件系统应用于磁盘空间的不同分区上,通常一个分区会使用一套文件系统来组织和管理数据,那么分区是什么?

下面这张图应该都不陌生。我们平时经常会提及给电脑“分盘”的概念,实际上就是磁盘空间的分区。事实上,计算机只有一个磁盘,但是存储了不同类型的数据(如:内核数据、用户数据等),为了方便管理,OS将磁盘从逻辑上区分为多个部分,这就是分区,每个分区可以独立进行格式化和管理。

在这里插入图片描述

每个分区都有自己的文件系统,用于在该分区上创建、存储和操作文件和目录。不同的分区使用不同的文件系统,以适应不同的需求和操作系统的要求。

2.2 文件系统的结构

在这里插入图片描述

Block Group: ext2文件系统会按照分区的大小,将其划分为多个组(group)。每个组的大小和结构都相同,管理方法也相同,所以确定了一组的管理方式,就能管理所有组。

BootBlock: 系统的引导块,一般用于计算机开机时的初始化,例如在磁盘上找到OS内核,加载到内存中,然后跳转到初始地址开始执行OS。在磁盘中的位置是固定的,一般CHS地址为0号磁头0号柱面1号扇区。

SuperBlock: 超级块,用于存储整个文件系统的相关信息,如:block和inode的总理;未使用的block和inode的数量;block与inode的大小(block为1/2/4K,inode为128byte),对于文件系统来说十分重要,SuperBlock的信息被破坏,可以说整个文件系统结构就被破坏了。因此一般同一个文件系统的每个group中都在开头设置了SuperBlock,目的是拷贝多份防止数据损坏。如果group1的SuperBlock数据损坏了,则可以group2的SuperBlock中拷贝数据过去,它们的内容是一样的。

GDT,Group Descriptor Table:块组描述符,描述当前区段(block group)开始和结束的block号码,以及说明每个区段(inodemap、blockmap、inode table)分别介于哪些block号码之间。

Inode Table:

先介绍inode:inode(index node)是文件的索引节点,记录文件的各种信息,如:inode编号;文件属性、权限;硬链接引用计数;文件大小;文件数据所在块block的编号…

而Inode Table用于存储这些inode节点,本质就是一个数组

关于inode:一个文件系统中,每个文件的inode编号都是唯一的。每一个分组都有一个特定的inode编号范围,文件的inode编号 = 所属分组inode编号范围起始值 + inode节点在分组内Inode Table中的下标

Block BitMap: 标识Data Block中每一个数据块是否被占用的位图

Inode BitMap: 标识Inode Table中每一个位置是否被占用的位图

2.3 剖析inode

当用户访问一个文件时,必须通过该文件的inode才能找到对应的数据(实际上文件数据存储在block)。一个inode节点(语言层面可以理解为一个结构体)内部大致划分为两个区域,一个是存储文件权限、属性等其它信息,另一个则是一个记录文件数据所在的block编号的表。

而由于inode的大小只有128字节(ext2),如果一个文件太大,其占用的block数量可能会超过inode可记录的数量。为此,inode记录block号码的区域被设计为12个直接、一个间接、一个双间接、一个三间接记录区。所谓间接,就是拿一个block当作其它block的编号记录区,只有最后一个间接才会真正用来记录block号码,其他的间接层,都只是依次引用。

一个block = 4KB

block编号是一个整型,因此:一个编号的大小 = sizeof(int) = 4byte

故一个block能存储的编号数量 = 4KB / 4byte = 1024

在这里插入图片描述

💭所以在当前文件系统(blocksize = 4KB)下,一个inode能索引的最大数据量(即单文件最大容量)为:
12 × 4 + 1024 × 4 + 1024 × 1024 × 4 + 1024 × 1024 × 1024 × 4 = 4 T B 12 \times 4 + 1024\times4 + 1024\times1024\times4 + 1024\times1024\times1024\times4 = 4TB 12×4+1024×4+1024×1024×4+1024×1024×1024×4=4TB

🔎查看inode编号的bash指令:ls加上-i选项即可查看

在这里插入图片描述

2.4 文件的操作

💭文件的操作有访问文件、创建文件、删除文件。这些操作在底层是怎样实现的呢?要解决这个问题,先要弄懂目录的存储。

要想对一个文件进行操作,必须先找到他。知道了文件数据的存储在数据块中,那我们怎么找到文件?要知道,文件一定在目录中,而目录也是个文件。目录是一个特殊文件,那么同理他必然也会有对应的inode节点和block数据库,而**目录的block中存放的是目录中文件的文件名和inode的映射关系。**因此,知道文件名就能在目录中找到对应的inode,自然也能找到文件了。

因此可以得出一个结论:文件名不是文件的属性,不存储在文件inode中,而是存储在文件所在目录的数据块中,发挥与inode映射的作用。

例:假设有一个目录mydir,下面是其包含文件和block中存储数据的示意图

在这里插入图片描述

在这里插入图片描述

🔎了解目录的存储后,就能从底层摸清访问文件、创建文件、删除文件的过程了。

假设我们要对一个名为"log.txt"的文件进行操作

  • 访问文件:
    (访问文件就是要找到文件,是操作文件最重要的一步,分为以下几步)
    1. 找到当前目录的block数据块(通过内存中打开的目录对象)
    2. 根据文件名log.txt,找到文件名与文件inode号的映射关系
    3. 在当前文件系统中,先通过inode号确定文件所在分组,再确定文件在组内Inode Table的下标,最终找到inode
    4. 通过文件的inode与数块的映射关系,找到其block
    5. 最后将block中的数据load到内存中,成功访问log.txt文件

💨总过程:

文件名 → \rightarrow inode号 → \rightarrow inode → \rightarrow block

  • 创建文件:

    1. 在当前目录所在分区中,找到某个group中空闲的Inode Table位置(即扫描每个Inode BitMap,0即是空)。得到一个inode号。
    2. 在找到的Inode Table空位上新建一个inode,并写入文件属性(必须写入已经得到的node号,并且注意此时新文件(非目录)为空,没有数据块)
    3. 在当前目录的数据块中,写入新文件的文件名"log.txt"与inode号的映射关系。
  • 向文件写入数据:

    1. 按照访问文件的步骤先找到文件的inode编号和inode
    2. 扫描文件所在group的Block BitMap,寻找空闲block,开始向这些block写入数据
    3. 写入完毕后,将这些block号对应Block BitMap位置中的0置为1,即标识这些block已被占用
    4. 建立文件inode与占用block的映射关系,并修改inode中的文件属性(如文件大小)
  • 删除文件:

    1. 按照访问文件的步骤先找到文件的inode编号和inode

    2. 改位图:Inode BitMap对应位置1$\rightarrow 0 ; B l o c k B i t M a p 对应位置 1 0;Block BitMap对应位置 1 0BlockBitMap对应位置1\rightarrow$0;

    注意:删除文件并没有直接清除block上的数据,而是在Inode BitMap和Block BitMap位图层级上取消占用。这也为恢复已删除文件提供了可能,只要已删文件曾用过的block未被其它文件写入数据,数据未被破坏,就有恢复的可能。

💭补充两个细节:

  1. 为了方便管理,一个目录和该目录下的文件会存储在同一个分区中;(这也能解释为何一个目录下不能有同名文件了,因为一个文件系统中,每个文件的inode编号都是唯一的,如果一个目录下出现同名文件,就无法建立文件名和inode一一对应的映射关系了)
  2. 为了提高访问速度,减少磁盘IO次数,OS把曾经使用过的和当前使用的目录文件缓存到内存中,形成一个个目录项(一个内存级的数据结构),目录项将文件名和对应的inode关联起来(每个目录项对应一个文件或子目录)。这样文件系统就能通过内存中的目录项,快速找到文件名和对应inode的映射关系,而不用每次都到磁盘中将目录的数据load到内存。

3 软硬链接

有时候我们希望对文件起别名,以达到某种效果。那么在Linux中,可以使用软链接(Symbolic Link)和硬链接(Hard Link)来实现。

3.1 软链接

概念:

软链接的本质是一个新文件,它与链接文件不是同一个文件,拥有独立的inode。软链接中存储的是链接文件的路径,所以访问软链接时,相当于访问另一个文件。

  1. 创建hello程序的软链接(ln -s 文件名 软链接名)

    [ckf@VM-8-3-centos linkTest]$ ll
    total 16
    -rwxrwxr-x 1 ckf ckf 8360 Jul  2 10:45 hello
    -rw-rw-r-- 1 ckf ckf   77 Jul  2 10:45 hello.c
    [ckf@VM-8-3-centos linkTest]$ ./hello
    hello link
    [ckf@VM-8-3-centos linkTest]$ ln -s hello hello-soft
    
  2. 查看当前目录,发现创建了一个新文件hello-soft与hello链接,且二者inode编号不同

    [ckf@VM-8-3-centos linkTest]$ ll -i
    total 16
     926892 -rwxrwxr-x 1 ckf ckf 8360 Jul  2 10:45 hello
     926891 -rw-rw-r-- 1 ckf ckf   77 Jul  2 10:45 hello.c
    1058748 lrwxrwxrwx 1 ckf ckf    5 Jul  2 11:11 hello-soft -> hello
    
  3. 运行hello-soft,发现结果与hello一致

    [ckf@VM-8-3-centos linkTest]$ ./hello-soft 
    hello link
    

由于软链接是独立的文件,因此,删除其链接的文件,软链接依然存在,只是找不到里面的文件了。

在这里插入图片描述

📝软链接示意图

在这里插入图片描述

应用场景:

对于一些路径较深,难以按路径访问的文件,可以用软链接作为快捷方式访问。

[ckf@VM-8-3-centos linkTest]$ ll
total 20
drwxrwxr-x 3 ckf ckf 4096 Jul  2 11:22 a
-rwxrwxr-x 1 ckf ckf 8360 Jul  2 10:45 hello
-rw-rw-r-- 1 ckf ckf   77 Jul  2 10:45 hello.c
[ckf@VM-8-3-centos linkTest]$ tree a
a
`-- b
    `-- c
        `-- d
            `-- hello

3 directories, 1 file
[ckf@VM-8-3-centos linkTest]$ ln -s a/b/c/d/hello hello-shortcut
[ckf@VM-8-3-centos linkTest]$ ll
total 20
drwxrwxr-x 3 ckf ckf 4096 Jul  2 11:22 a
-rwxrwxr-x 1 ckf ckf 8360 Jul  2 10:45 hello
-rw-rw-r-- 1 ckf ckf   77 Jul  2 10:45 hello.c
lrwxrwxrwx 1 ckf ckf   13 Jul  2 11:24 hello-shortcut -> a/b/c/d/hello
[ckf@VM-8-3-centos linkTest]$ ./a/b/c/d/hello
hello link
[ckf@VM-8-3-centos linkTest]$ ./hello-shortcut
hello link

3.2 硬链接

概念:

硬链接是在目录中创建一对新的文件名与inode映射关系,文件名新起,inode是另一个文件的inode。硬链接和链接文件共享一个inode,相当于链接文件的别名。一个文件可以有多个硬链接。事实上,inode节点中有一个引用计数,新建一个硬链接时,引用计数自增1,删除时自减1,只有当引用计数清零时,文件才会被删除。

  1. 创建hello程序的硬链接hello-hard(ln 文件名 硬链接名),发现hello和hello-hard的inode编号相同

    [ckf@VM-8-3-centos linkTest]$ ln hello hello-hard
    [ckf@VM-8-3-centos linkTest]$ ll -i
    total 32
    1058749 drwxrwxr-x 3 ckf ckf 4096 Jul  2 11:22 a
     926892 -rwxrwxr-x 2 ckf ckf 8360 Jul  2 10:45 hello
     926892 -rwxrwxr-x 2 ckf ckf 8360 Jul  2 10:45 hello-hard
     926891 -rw-rw-r-- 1 ckf ckf   77 Jul  2 10:45 hello.c
    1058748 lrwxrwxrwx 1 ckf ckf   13 Jul  2 11:24 hello-shortcut -> a/b/c/d/hello
    
  2. 文件属性一行的第三个数字是该文件的硬链接数(inode引用计数),若我们再给hello创建一个硬链接,可发现此数字会发生变化

    [ckf@VM-8-3-centos linkTest]$ ln hello hello-hard2
    [ckf@VM-8-3-centos linkTest]$ ll -i
    total 44
    1058749 drwxrwxr-x 3 ckf ckf 4096 Jul  2 11:22 a
     926892 -rwxrwxr-x 3 ckf ckf 8360 Jul  2 10:45 hello
     926892 -rwxrwxr-x 3 ckf ckf 8360 Jul  2 10:45 hello-hard
     926892 -rwxrwxr-x 3 ckf ckf 8360 Jul  2 10:45 hello-hard2
     926891 -rw-rw-r-- 1 ckf ckf   77 Jul  2 10:45 hello.c
    1058748 lrwxrwxrwx 1 ckf ckf   13 Jul  2 11:24 hello-shortcut -> a/b/c/d/hello
    
  3. 运行硬链接,发现结果一致

    [ckf@VM-8-3-centos linkTest]$ ./hello
    hello link
    [ckf@VM-8-3-centos linkTest]$ ./hello-hard
    hello link
    [ckf@VM-8-3-centos linkTest]$ ./hello-hard2
    hello link
    

📝硬链接示意图

在这里插入图片描述

应用场景:

每个文件夹中,都至少会有 . ..两个目录,分别代表当前目录和上级目录,它们的本质就是相应目录的硬链接

.

[ckf@VM-8-3-centos linkTest]$ ll -a
total 24
drwxrwxr-x 2 ckf ckf 4096 Jul  2 15:42 .
drwxrwxr-x 5 ckf ckf 4096 Jul  2 10:49 ..
-rwxrwxr-x 1 ckf ckf 8360 Jul  2 10:45 hello
-rw-rw-r-- 1 ckf ckf   77 Jul  2 10:45 hello.c
[ckf@VM-8-3-centos linkTest]$ pwd
/home/ckf/NewBeginning/lesson4_file/linkTest
[ckf@VM-8-3-centos linkTest]$ ls -di .
1058747 .
[ckf@VM-8-3-centos linkTest]$ ls -di /home/ckf/NewBeginning/lesson4_file/linkTest
1058747 /home/ckf/NewBeginning/lesson4_file/linkTest

//第10和12行表明,.和/home/ckf/NewBeginning/lesson4_file/linkTest的inode号相同,.是硬链接

..

一个父目录下,每个子目录都有父目录的硬链接..,父目录除了自己的绝对路径和.硬链接,其它硬链接就是子目录的..,因此:
父目录下的子目录数量 = 父目录的硬链接数 − 2 父目录下的子目录数量 = 父目录的硬链接数 - 2 父目录下的子目录数量=父目录的硬链接数2

利用硬链接,Linux的文件系统呈现出如下的树状结构,每个节点之间都存在一条路径

⭕值得注意的是,OS只允许用户对普通文件做硬链接,不允许对目录进行硬链接,否则可能会导致文件系统树状结构出现环路,结构一旦遭到破坏,对文件的访问就会出问题(如:一个文件有两个绝对路径)。

[ckf@VM-8-3-centos linkTest]$ ll
total 20
drwxrwxr-x 2 ckf ckf 4096 Jul  2 15:53 emptyDir
-rwxrwxr-x 1 ckf ckf 8360 Jul  2 10:45 hello
-rw-rw-r-- 1 ckf ckf   77 Jul  2 10:45 hello.c
[ckf@VM-8-3-centos linkTest]$ ln emptyDir hard-link
ln: ‘emptyDir’: hard link not allowed for directory

在这里插入图片描述


Ending

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

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

相关文章

低代码平台的价格范围及购买成本分析

Zoho Creator是一款强大而灵活的低代码应用程序开发平台&#xff0c;可帮助企业快速、高效地创建各种应用程序。但是&#xff0c;很多人可能会担心它的价格问题。在这篇文章中&#xff0c;我们将深入探讨Zoho Creator的定价策略和计划&#xff0c;以帮助您更好地理解其价格结构…

如何保证消息队列的高可用?

RabbitMQ的高可用性 RabbitMQ 是比较有代表性的&#xff0c;因为是基于主从&#xff08;非分布式&#xff09;做高可用性的&#xff0c;我们就以 RabbitMQ 为例子讲解第一种 MQ 的高可用性怎么实现。 RabbitMQ 有三种模式&#xff1a;单机模式、普通集群模式、镜像集群模式。…

【裸机开发】I2C 通信接口(一)—— I2C 通信时序协议及通信流程

目录 一、I2C 简介 二、I2C 的整体通信流程 三、主要通信时序和协议 3.1 开始 / 停止条件 3.2 地址传送 3.3 数据传输 3.4 应答条件 3.5 重复开始条件 四、总线仲裁机制&#xff08;SDA&#xff09; 1、什么是总线仲裁 2、总线仲裁的过程 五、时钟同步&#xff08;…

QT学习笔记:TCP客户端的实现

QT一般用来做客户端&#xff0c;我这里就简单讲一下怎么开发基于QT的TCP客户端。 1、用QtCreator创建项目 2、界面 3、.pro文件添加network QT core gui network 4、mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include &l…

关于Unity动画卡在第一帧的处理方法

今天在制作人物的死亡动画时出现了题目所说的问题&#xff0c;以下是动画的状态机 因为任何状态都可能死亡&#xff0c;所以是从anyState进入的死亡动画 进入条件为isDead是true&#xff0c;当角色死亡时这个条件就会设置成true 结果出现了卡在这个动画的问题 经过检查发现&a…

Java连接数据库——JDBC使用步骤

步骤: 1.注册驱动 ——导入相应平台厂商的驱动jar包(zip为源码) 驱动版本 8&#xff1a;com.mysql.cj.jdbc.Driver 驱动版本 5&#xff1a;com.mysql.jdbc.Driver DriverManger.registerDriver( new Driver() ); 2.创建链接 —— connection Java程序连接数据库要调用方法,方…

网络推广技术和算法不断更新

网络推广技术和算法不断更新&#xff0c;以下是一些关于网络推广技术和算法的讨论。 1. 定向广告投放&#xff1a;定向广告投放是一种网络推广技术&#xff0c;它利用用户数据和行为分析&#xff0c;将广告投放给特定的受众群体。通过分析用户的兴趣、行为、地理位置等信息&am…

Go环境搭建[win10]

下载 https://golang.google.cn/dl/https://golang.google.cn/dl/go1.20.5.windows-amd64.msiGo环境变量配置 系统变量 GOROOT D:\Dev\Env\Go [Go语言安装目录] GOPROXY https://goproxy.io,direct [配置代理] GOPATH D:\Dev\PROJECTS_GO [Go语言工作目录] PATH …

Java Web JavaScript (1)23.7.1

JavaScript 1&#xff0c;JavaScript简介 JavaScript 是一门跨平台、面向对象的脚本语言&#xff0c;而Java语言也是跨平台的、面向对象的语言&#xff0c;只不过Java是编译语言&#xff0c;是需要编译成字节码文件才能运行的&#xff1b;JavaScript是脚本语言&#xff0c;不…

如何自己开发软件测试工具?一篇文章教会你

目录 序言&#xff1a; 一、自动化测试工具浅析 二、如何快速开发一个自动化测试工具 总结&#xff1a; 序言&#xff1a; 一说到自动化测试工具&#xff0c;大家很多人都会想到的是QTP、LR或者selenium之类的工具&#xff0c;要大家一开始设计一个这样的工具&#xff0c;其…

DevOps实现自动化发布实操

DevOps实现自动化发布流程 本篇文章来自 B站视频&#xff08;部分步骤与视频存在差异&#xff09; 流程图及原理 本地编写代码提交至远程仓库Jenkins&#xff08;基于Docker&#xff09;通过内置Git获取提交的代码&#xff0c;通过Maven进行打包&#xff0c;形成可执行文件&a…

数字信号处理实验:IIR数字滤波器设计及软件实现

目录 一、实验目的 二、实验原理 三、实验设备 四、实验内容及步骤 五、实验结果及分析 六、实验主程序框图及程序清单 七、实验总结 一、实验目的 熟悉用双线性变换法设计IIR数字滤波器的原理与方法&#xff1b;学会调用MATLAB信号处理工具箱中滤波器设计函数&#xff…

还在手动下载github项目?想要自动化下载github项目?基于python开发项目自动下载模块帮你实现自动下载存储

GitHub是一个基于Web的代码托管平台和开发者社区。它允许开发者存储、管理和分享他们的代码&#xff0c;并进行版本控制。开发者可以在GitHub上创建仓库来存储项目代码&#xff0c;并使用Git来跟踪和管理代码的变更历史。GitHub提供了一系列协作工具&#xff0c;如问题追踪、Pu…

一波三折|药学博士终获CSC资助赴瑞典隆德大学从事2年博士后研究

我们先为W博士获得美国哈佛大学布列根和妇女医院的邀请函&#xff0c;助其成功获得CSC公派资助&#xff0c;但后被哈佛大学国际中心以可能拒签为由&#xff0c;不予办理DS-2019表格。幸亏我们未雨绸缪先期又申请到瑞典隆德大学的2年博士后邀请函&#xff0c;最终顺利得到CSC改派…

CAS + 自旋 锁底层

多线程安全问题 为什么会出现多线程安全问题? 在多线程并发下, 假设有 A,B 两个线程同时操作 count 0 这个公共变量, 在A线程中count, 在B线程中count, 正常来说结果应该是 count 2, 可是同时在A, B两个线程中拿到 count 0 , 并且都执行count赋值, 结果就变成了 count 1…

【Java可执行命令】(十一)Java 密钥库和证书管理工具keytool:玩转密钥库和证书管理,深入解析keytool工具的应用与技巧~

Java可执行命令之keytool 1️⃣ 概念2️⃣ 优势和缺点3️⃣ 使用3.1 语法格式3.2 生成证书请求&#xff1a;keytool -certreq3.3 导出证书&#xff1a;keytool -exportcert3.4 生成密钥对&#xff1a;keytool -genkeypair3.5 导入证书或证书链&#xff1a;keytool -importcert3…

基于STM32的直流电机调速系统

目录 基于STM32的直流电机调速系统一、原理图二、部分代码三、视频演示 基于STM32的直流电机调速系统 功能&#xff1a; 1.通过LCD屏幕显示实时两个电机的占空比 2.通过按键调整电机1和2的加减速 3.通过L298N驱动两个直流电机完成调速 一、原理图 二、部分代码 #include &qu…

基于Spring Boot + Vue社区管理系统的设计与实现

1、项目介绍 Spring Boot 是一个用于构建 Java 应用程序的开源框架&#xff0c;它使得开发者可以轻松地创建独立的、生产级别的 Spring 应用程序。Vue.js 是一个流行的 JavaScript 框架&#xff0c;用于构建现代化的、响应式的社区管理系统是一个用于管理社区活动、用户信息和…

Vue发布新版本,强制更新代码的方式

public下新建version.json文件定义版本 {"version":"1.1.0" } util下新建updateVersion.js import axios from axios; import { Loading } from element-ui; var t1; var t2; export async function isNewVersion() {var randomNumberMath.random() co…

[软件工具]左键连发工具左键连点工具使用教程

左键连发软件是一个可以点击一下自动左键连续点击指定次数的软件&#xff0c;比如你设置20次&#xff0c;当你点击一次松开鼠标后&#xff0c;会自动左键连续点击20次。具体使用教程为&#xff0c;我们打开软件 我们可以设置连发次数&#xff0c;默认15次&#xff0c;你可以设置…