『 Linux 』目录与软硬链接 (万字详解)


如何理解目录

请添加图片描述

目录是一个文件存在其对应独立的Inode;

$ stat dir
  File: ‘dir’
  Size: 4096        Blocks: 8          IO Block: 4096   directory
Device: fd01h/64769d    Inode: 2360193     Links: 2
Access: (0775/drwxrwxr-x)  Uid: ( 1002/    _XXX)   Gid: ( 1002/    _XXX)
Access: 2024-06-03 19:07:42.139437045 +0800
Modify: 2024-06-03 19:07:40.931391471 +0800
Change: 2024-06-03 19:07:40.931391471 +0800
 Birth: -

文件的实质是内容与属性的结合;

struct inode {
    uint16_t i_mode;        // 文件类型和权限
    uint16_t i_uid;         // 文件所有者的用户ID
    uint32_t i_size;        // 文件大小(字节)
  	/* ... */
    uint32_t i_flags;       // 文件标志
    uint32_t i_block[15];   // 数据块指针
    // 其他文件系统特定的信息
};

当访问文件时需要通过Inode中的数据块指针数组字段中的元素直接或间接访问数据块中的数据;

目录文件的Inode中的数据块指针指向的内容称为目录项;


目录项

请添加图片描述

目录项是目录文件的Inode中的数据块指针数组直接或间接指向的内容;

是属于目录文件的内容;

目录项中包含了当前目录下的所有文件名以及Inode之间的关系;

struct ext2_dir_entry {
    uint32_t inode;      // inode编号
    uint16_t rec_len;    // 目录项长度
    uint8_t name_len;    // 文件名长度
    uint8_t file_type;   // 文件类型
    char name[];         // 文件名
};

/// 例: 目录项1 文件名"file.txt"
/*
	{
        inode: 10,
    	rec_len: 16,
   		name_len: 9,
    	file_type: 1, // 普通文件
    	name: "file1.txt"
	}
*/

以先描述后组织的关系保存当前目录下所有文件的基本信息;

  • 目录项的作用

    • 文件名到Inode的映射

      目录想主要作用是将文件名映射到对应的Inode编号;

      虽然是以目录项的形式存在,但是以抽象来看的话是一个KeyValue结构;

      通过目录项使得文件系统能够通过文件名找到对应文件的Inode,从而访问文件的元数据和内容;

    • 目录结构的组织

      目录想使得文件系统能够组织和管理目录结构;

      通过目录项用户可以浏览和访问目录中的文件和该目录文件中的其他子目录文件;

假设需要访问一个目录文件为/aa/bb/cc/dd.txt,需要进行以下操作:

  • 获取根目录(/)的inode:
    • 从inode表中获取根目录的inode;
  • 访问根目录的数据块:
    • 通过根目录的inode指针,找到根目录的数据块;
    • 在根目录的数据块中查找名为aa的目录项,获取其对应的inode编号;
  • 访问aa目录:
    • 通过aa目录的inode编号,找到aa目录的inode;
    • 读取aa目录的inode,获取其指向的数据块指针;
    • 通过aa目录的inode指针,找到aa目录的数据块;
    • aa目录的数据块中查找名为bb的目录项,获取其对应的inode编号;
  • 访问bb目录:
    • 通过bb目录的inode编号,找到bb目录的inode;
    • 读取bb目录的inode,获取其指向的数据块指针;
    • 通过bb目录的inode指针,找到bb目录的数据块;
    • bb目录的数据块中查找名为cc的目录项,获取其对应的inode编号;
  • 访问cc目录:
    • 通过cc目录的inode编号,找到cc目录的inode;
    • 读取cc目录的inode,获取其指向的数据块指针;
    • 通过cc目录的inode指针,找到cc目录的数据块;
    • cc目录的数据块中查找名为dd.txt的目录项,获取其对应的inode编号;
  • 访问dd.txt文件:
    • 通过dd.txt文件的inode编号,找到dd.txt文件的inode;
    • 读取dd.txt文件的inode,获取其指向的数据块指针;
    • 通过dd.txt文件的inode指针,找到文件的实际数据块,读取文件内容;

目录中的权限问题

请添加图片描述

  • 为什么同一个目录下不能有同名文件?

    目录是文件,其Inode中的指针数组中数组中指针指向的数据块中存放的数据为目录项;

    目录项为当前目录下所有文件的文件名与Inode编号的映射关系(以结构体方式存在);

    这个映射关系为类似一个KeyValue的形式;

    若是Key值存在重复则无法有效找到对应文件的Inode从而不能正确找到需要访问的文件;

文件将通过自身的文件属性控制不同用户对该文件的操作权限;

而目录文件也是文件,其在文件系统中有独立的Inode,所以目录同样存在权限;

$ ll
total 4
drwxrwxr-x 2 _XXX _XXX 4096 Jun  3 19:07 dir
-rw-rw-r-- 1 _XXX _XXX    0 Jun  3 19:07 log.txt
  • 权限类型

    每个文件和目录都有三个权限类型:读( r ),写( w )和执行( x );

    这些权限分别适用于文件所有者(user),文件所属组(group)和其他用户(others);

  • 权限表示

    权限通常用一个9位的字符串表示,如上图为例的rwxrwxr-xrw-rw-r--(第一个字符表示文件类型,d表示目录文件);

    前三位表示文件所有者的权限;

    中间三位表示文件所属组的权限;

    最后三位表示其他用户的权限;

对于普通文件而言其文件权限如下:

  • 读权限( r )

    允许用户读取文件的内容;

    若是没有读权限用户但有写与执行权限时可以对文件进行修改与执行但无法利用类似的cat命令将文件内容进行展示;

    $ cat log.txt 
    cat: log.txt: Permission denied
    
  • 写权限( w )

    允许用户修改文件的内容;

    若是没有写权限但是有读权限与执行权限时可以利用类似cat的命令读取文件内容与执行文件但是无法对文件内容进行增删改等写入操作;

    $ chmod 550 log.txt 
    $ cat log.txt 
    hello
    $ echo "world\n" > log.txt 
    bash: log.txt: Permission denied
    
  • 执行权限( x )

    允许用户执行文件(如果文件是可执行程序或是脚本);

    若是没有执行权限则无法运行文件;

    $ ll
    total 8
    d-wx-wx-wx 2 _XXX _XXX 4096 Jun  3 19:07 dir
    -rw-rw-rw- 1 _XXX _XXX    6 Jun  4 10:55 log
    $ ./log
    bash: ./log: Permission denied
    

对于目录文件如下:

  • 读权限( r )

    允许用户查看目录中的文件列表 (包括目录文件) ;

    若是没有读权限但有写权限和执行权限时可以进入目录并在目录中进行增删改等操作但无法使用ls等命令读取该目录中的文件列表;

    $ chmod 330 dir
    $ ls dir/
    ls: cannot open directory dir/: Permission denied
    
  • 写权限( w )

    允许用户在目录中创建,删除和重命名文件和子目录;

    若是没有写权限则但有读和执行权限时可以进入目录并使用ls等命令展示当前目录的内容但无法对目录中的文件进行增删改查等操作;

    $ chmod 550 dir
    $ touch dir/test.txt
    touch: cannot touch ‘dir/test.txt’: Permission denied
    
  • 执行权限( x )

    允许用户进入该目录 ;

    若是没有执行权限时即使拥有其他权限也无法对目录进行任何操作(目录文件中执行权限x是所有操作的前提);

    $ chmod 660 dir
    $ cd dir
    bash: cd: dir: Permission denied
    $ ll dir
    ls: cannot access dir/dir: Permission denied
    ls: cannot access dir/log.txt: Permission denied
    ls: cannot access dir/hello.txt: Permission denied
    ls: cannot access dir/test.txt: Permission denied
    total 0
    d????????? ? ? ? ?            ? dir
    -????????? ? ? ? ?            ? hello.txt
    -????????? ? ? ? ?            ? log.txt
    -????????? ? ? ? ?            ? test.txt
    $ touch dir/makefile
    touch: cannot touch ‘dir/makefile’: Permission denied
    

根目录

请添加图片描述

在Linux中,由根目录至其他的所有的目录文件的整体构造被称为操作系统目录树;

  • 操作系统目录树

    操作系统目录树是文件系统的核心结构之一,以属性结构组织和管理文件和目录;

    使得所有的文件和目录都已层次化的方式进行组织,且每个目录都可以包含子目录和文件;

  • 根目录

    根目录是整个操作系统目录树的起点也是最高顶点,通常为/;

    通常操作系统目录树的根目录/所在的文件系统被称为根文件系统;

    每个文件系统被创建时将默认存在一个根目录/;

  • 根文件系统

    挂载至操作系统目录树中根目录/的文件系统被称为根文件系统;

当存在多个分区(文件系统)时,分区必须以挂载的形式挂载至操作系统目录树之中;

在进行分区时将会把分区格式化为一个文件系统,以便于存储文件和目录;

根目录也是一个目录,也是一个文件,在文件系统中有自己的Inode编号和属性,通常在创建一个文件系统时会创建对应的根目录,并在其InodeTable表中维护根目录的Inode信息;

而为了方便用户的操作,实际上每当进行一次分区(创建出一个文件系统)过后操作系统内核都会默认为其生成一个根目录/;

但在创建一个新的分区后不能立即使用,必须以挂载的形式将其挂载至操作系统目录树之中;

一但挂载成功其文件系统的根目录将会变成挂载时所设置的名称,在文件系统中其算是一个根目录而在整体的操作系统目录树中并不是根目录;

文件系统的创建通常如下:

  • 分区

    采用分区工具将磁盘划分为一个或多个分区;

  • 格式化分区

    利用文件系统工具将分区格式化为一个特定的文件系统类型,其中文件系统的根目录的创建也属于格式化中的一环;

  • 创建挂载点

    在根文件系统中创建一个目录为挂载点;

  • 挂载分区

    将分区挂载至挂载点;

所以在一整个体系中只有一个根目录(根文件系统所挂载的目录);

对于各个文件系统的根目录而言,操作系统内核将选择一个文件系统作为根文件系统;

而将根文件系统挂载至/的操作一般可以理解为告诉操作系统该目录将作为整个操作系统目录树的最高顶点(起点);


Dentry缓存

请添加图片描述

访问或操作任何一个文件/程序都要带路径并通过路径访问其目录项获得对应的Inode逐级进行访问;

这是一个效率过低的操作,无论进行任何操作都要从根目录开始寻找;

为了提高效率操作系统将进行一个Dentry缓存;

Dentry缓存是内核中的一个内存缓存,用于存储最近访问过的目录项,通常以结构体的形式存在;

每个Dentry结构体表示文件系统中的一个目录项,包含文件名和对应的Inode信息;

其结构信息可以参照如下:

struct dentry {
    atomic_t d_count;                  // 引用计数
    unsigned int d_flags;              // 标志
    spinlock_t d_lock;                 // 自旋锁
    int d_mounted;                     // 挂载计数
    struct inode *d_inode;             // 指向对应的inode
    struct hlist_node d_hash;          // 哈希链表节点
    struct dentry *d_parent;           // 父目录的dentry
    struct qstr d_name;                // 文件名
    struct list_head d_lru;            // LRU链表节点
    struct list_head d_child;          // 子目录链表节点
    struct list_head d_subdirs;        // 子目录链表头
    struct rcu_head d_rcu;             // RCU头
    // 其他字段省略
};

这个缓存用于加速路径解析从而提高文件和目录的访问效率;

通过缓存目录项,文件系统可以快速查找文件和目录,而无需每次都从磁盘中读取目录项;

一般当进行一个目录或文件的访问通常会有以下操作:

  • 路径解析

    当用户访问文件或目录时,文件系统会解析路径并在Dentry缓存找找到对应的目录项;

    如果在Dentry缓存中找到目录项,文件系统可以直接使用缓存中的Inode编号找到对应的Inode信息从而无需从磁盘中重新依次读取目录项;

  • 缓存命中和未命中

    • 缓存命中:

      如果目录项在Dentry缓存中这种被称为缓存命中,文件可以直接使用缓存中的信息;

    • 缓存未命中;

      如果在Dentry缓存中未找到目录项,即目录项不在Dentry缓存中被称为缓存未命中;

      此时文件系统需要从新从磁盘中读取目录项并添加到Dentry缓存中并进行使用同时方便后续访问时使用;

  • 缓存管理

    Dentry缓存使用引用计数等方式管理缓存项;

    当缓存项的引用计数为0且缓存缓存空间不足时将会移除未使用时间最长的缓存想以释放空间;


文件的增删改查与文件系统关系

请添加图片描述

  • 创建文件(增)

    • 分配inode:

      文件系统首先在inode表中找到一个空闲的inode,并将其分配给新文件;
      初始化inode的元数据,包括文件类型、权限、所有者、时间戳等;

    • 创建目录项(dentry):

      在目标目录中创建一个新的目录项(dentry),将新文件的名称与分配的inode关联起来;
      更新目录的inode信息(如链接计数、修改时间等);

    • 分配数据块:

      当文件内容被写入时,文件系统在数据区找到空闲的数据块,并将其分配给文件;
      更新inode中的数据块指针,指向分配的数据块;

  • 删除文件(删)

    • 删除目录项(dentry):

      文件系统在目标目录中找到对应的目录项,并将其删除;
      更新目录的inode信息(如链接计数、修改时间等);

    • 释放inode:

      文件系统将文件的inode标记为空闲,并从inode表中移除;
      更新inode的元数据,清除文件类型、权限、所有者、时间戳等信息;

    • 释放数据块:

      文件系统将文件占用的数据块标记为空闲,并从数据区中移除;
      更新inode中的数据块指针,清除指向的数据块信息;

  • 修改文件(改)

    • 找到inode:

      文件系统在目录中找到对应的目录项,获取文件的inode;

    • 分配或重新分配数据块:

      当文件内容被修改时,文件系统在数据区找到空闲的数据块,并将其分配给文件;
      如果文件内容增加,需要分配新的数据块;如果文件内容减少,可能需要释放部分数据块;
      更新inode中的数据块指针,指向新的或修改后的数据块;

    • 更新inode元数据:

      更新inode的元数据,包括文件大小、修改时间等信息;

  • 读取文件(查)

    • 找到inode:

      文件系统在目录中找到对应的目录项,获取文件的inode;

    • 读取数据块:

      根据inode中的数据块指针,文件系统读取文件的数据块;
      将数据块中的内容读取到用户提供的缓冲区中;

    • 更新inode元数据:

      更新inode的元数据,包括访问时间等信息;


软硬链接

请添加图片描述

在文件系统中,存在 “链接” 的概念;

指一个文件系统对象与其名称之间的关联;

在Linux中可以使用ln命令为文件或目录创建链接;

其对应文档如下:

$ ln --h
Usage: ln [OPTION]... [-T] TARGET LINK_NAME   (1st form)
  or:  ln [OPTION]... TARGET                  (2nd form)
  or:  ln [OPTION]... TARGET... DIRECTORY     (3rd form)
  or:  ln [OPTION]... -t DIRECTORY TARGET...  (4th form)
In the 1st form, create a link to TARGET with the name LINK_NAME.
In the 2nd form, create a link to TARGET in the current directory.
In the 3rd and 4th forms, create links to each TARGET in DIRECTORY.
Create hard links by default, symbolic links with --symbolic.
By default, each destination (name of new link) should not already exist.
When creating hard links, each TARGET must exist.  Symbolic links
can hold arbitrary text; if later resolved, a relative link is
interpreted in relation to its parent directory.

Mandatory arguments to long options are mandatory for short options too.
      --backup[=CONTROL]      make a backup of each existing destination file
  -b                          like --backup but does not accept an argument
  -d, -F, --directory         allow the superuser to attempt to hard link
                                directories (note: will probably fail due to
                                system restrictions, even for the superuser)
  -f, --force                 remove existing destination files
  -i, --interactive           prompt whether to remove destinations
  -L, --logical               dereference TARGETs that are symbolic links
  -n, --no-dereference        treat LINK_NAME as a normal file if
                                it is a symbolic link to a directory
  -P, --physical              make hard links directly to symbolic links
  -r, --relative              create symbolic links relative to link location
  -s, --symbolic              make symbolic links instead of hard links
  -S, --suffix=SUFFIX         override the usual backup suffix
  -t, --target-directory=DIRECTORY  specify the DIRECTORY in which to create
                                the links
  -T, --no-target-directory   treat LINK_NAME as a normal file always
  -v, --verbose               print name of each linked file
      --help     display this help and exit
      --version  output version information and exit

The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.
The version control method may be selected via the --backup option or through
the VERSION_CONTROL environment variable.  Here are the values:

  none, off       never make backups (even if --backup is given)
  numbered, t     make numbered backups
  existing, nil   numbered if numbered backups exist, simple otherwise
  simple, never   always make simple backups

Using -s ignores -L and -P.  Otherwise, the last option specified controls
behavior when a TARGET is a symbolic link, defaulting to -P.

GNU coreutils online help: <http://www.gnu.org/software/coreutils/>
For complete documentation, run: info coreutils 'ln invocation

在文件系统中链接可以分为两种主要类型:

  • 软链接
  • 硬链接

当默认用ln创建链接时创建的是硬链接,带-s选项表示创建软链接;


软链接

请添加图片描述

软链接是一个符号链接,是一个特殊类型的文件,在文件系统当中存在一个独立的Inode;

其包含指向另一个文件或者目录的路径;

换一种说法就是软链接允许用户创建指向文件或目录的快捷方式从而使得能够通过多个位置来访问一个文件或者是目录;

$ ll -i
total 0
2360201 -rw-rw-r-- 1 _XXX _XXX 0 Jun  4 13:41 log.txt
$ ln -s log.txt soft-link
$ ll -i
total 0
2360201 -rw-rw-r-- 1 _XXX _XXX 0 Jun  4 13:41 log.txt
2360202 lrwxrwxrwx 1 _XXX _XXX 7 Jun  4 13:41 soft-link -> log.txt

软链接的特点如下:

  • 软链接的Inode

    软链接有自己的Inode编号,与目标文件的Inode编号不同故为一个独立的文件;

    软链接的Inode中包含文件类型,权限,所有者,时间戳等元数据,但其数据块指针指向的存储目标文件路径的数据块而不是目标文件的实际数据块;

  • 软链接的数据块

    软链接的数据块存储的是目标文件或目录的路径 (绝对或者相对) ;

    当访问软链接时,操作系统将会读取软链接的数据块从而获取目标文件的路径然后重定向到目标文件;

  • 指向路径

    软链接中存储的是目标文件或目录的路径而不是文件内容本身;

    当访问软链接时操作系统将会自动重定向到目标文件或是目录;

    $ cat log.txt ;cat soft-link 
    $ echo "hello world\n" > soft-link 
    $ cat log.txt ;cat soft-link 
    hello world\n
    hello world\n
    
  • 可以跨文件系统

    软链接可以指向不同文件系统中的文件或是目录,类似指针一样,其存储的是路径信息;

  • 可以指向不存在的文件

    软链接可以指向一个当前不存在的文件或是目录;

    这种链接被称为 “悬挂链接” 或是 “断链” ;

    $ ll -i
    total 0
    2360201 -rw-rw-r-- 1 _XXX _XXX 0 Jun  4 13:53 log.txt
    2360202 lrwxrwxrwx 1 _XXX _XXX 7 Jun  4 13:41 soft-link -> log.txt
    $ rm log.txt 
    $ ll -i
    total 0
    2360202 lrwxrwxrwx 1 _XXX _XXX 7 Jun  4 13:41 soft-link -> log.txt
    

    当删除log.txtsoft-link将标红表示当前为 悬挂链接/断链 ;

  • 权限和大小

    软链接本身有自己的权限和大小,但这些权限通常指影响链接文件本身而不影响目标文件;

    软链接的大小通常是存储的路径长度;


硬链接

请添加图片描述

硬链接是软链接除外的另一种链接类型,允许多个文件名指向同一个文件数据块;

硬链接与源文件共享相同的Inode且并不属于一个单独的文件;

$ ll -i
total 0
2360201 -rw-rw-r-- 1 _XXX _XXX 0 Jun  4 13:59 log.txt
$ ln log.txt hard-link
$ ll -i
total 0
2360201 -rw-rw-r-- 2 _XXX _XXX 0 Jun  4 13:59 hard-link
2360201 -rw-rw-r-- 2 _XXX _XXX 0 Jun  4 13:59 log.txt

换句话说硬链接与目标文件实际上是一个不同名称的同文件,其对应的元信息相同;

硬链接提供了一种在文件系统中创建文件的多个入口点方法;

硬链接的特点如下:

  • 共享Inode

    硬链接与源文件共享相同的Inode编号,因此它们指向同一个数据块;

    任何一个文件在Inode中存在一个引用计数,这个引用计数用来计数当前文件的硬链接数;

    硬链接和源文件在文件系统中是完全等价的,删除任何一个不会影响数据的存在,只有当所有硬链接被删除后数据才会释放;

    $ ll -i
    total 0
    2360201 -rw-rw-r-- 1 _XXX _XXX 0 Jun  4 13:59 log.txt
    $ ln log.txt hard-link
    $ ll -i
    total 0
    2360201 -rw-rw-r-- 2 _XXX _XXX 0 Jun  4 13:59 hard-link
    2360201 -rw-rw-r-- 2 _XXX _XXX 0 Jun  4 13:59 log.txt
    

    该代码中从左至右数第三个字段即为硬链接数,引用计数为1变为2;

  • 不能夸文件系统

    基于Inode相同的理论上;

    硬链接只能在同一个文件系统中创建,不能跨文件系统;

  • 相同的文件属性

    硬链接与目标文件具有相同的Inode,故将会与原文件共享相同的文件属性(如权限,所有者等);

    任何一个硬链接的修改都会影响所有指向统一Inode的硬链接;

  • 不支持目录

    硬链接的创建不支持目录的硬链接,具体原因是避免造成环的问题;

    $ ln todel
    ln: ‘todel’: hard link not allowed for directory
    

    若允许为目录创建硬链接将可能出现以下问题:

    • 例:/usr/home/bin/,此时在bin目录中创建一个指向usr目录的硬链接,即/usr/home/bin/hard-link

      当遍历到目录/usr时,根据路径继续访问/usr/home/bin/hard-link,最终又会回到/usr路径从而导致无限循环;

  • 硬链接的应用场景

    当一个目录被创建时其目录中会默认存在两个目录文件分别为...分别代表当前目录和父目录;

    $ mkdir todel
    $ ls -ai todel/
    2360202 .  2360200 ..
    

    因为目录不被允许创建硬链接,故在设置文件系统时为了方便用户进行访问将设置两个硬链接;

  • 30
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dio夹心小面包

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值