Please enable Javascript to view the contents

Linux查漏补缺-1-链接

 ·  ☕ 8 分钟

1.简介

操作机某软件异常,进行重装时。对软链接理解存在偏差导致误删除目录问题详细参考 2.3.5 删除目录的软链

  1. 因为未移除感觉软链接,导致安装失败;

  2. 尝试删除软链接/root/bin -> /bin

  3. 执行命令rm /root/bin/时,报错Is a directory

  4. 想当然的执行rm -rf /root/bin/ 导致将其link的目录/bin删除。

    目录软链接的删除,应为rm /root/bin,而非rm -rf /root/bin/

本文详细记录由链接引伸出来的一系列概念。

2.说明

2.0 基础知识

inode的相关说明:注1

  • 跨文件系统(磁盘分区),inode不唯一;
  • 每个文件都会占用一个 inode ,文件内容由 inode 来指向blok获取;
  • inode与文件名为一对多关系;

系统内部读取文件步骤注1

  1. 系统找到文件名对应的inode
  2. 通过 inode 号码,获取inode信息
  3. 根据 inode 信息,找到文件数据所在的 block,读出数据。

链接: UNIX文件系统提供的一种将不同文件链接至同一个文件的机制;实际上是一种文件共享的方式,是 POSIX 中的概念,主流文件系统都支持链接文件。

查看文件信息的命令

  • ls命令

    示例:

    1
    2
    3
    4
    
    hex@hex-PC:/tmp/ln-test$ ls -li
    total 4
    60822147 -rw-rw-r-- 1 hex hex 12 Oct 25 17:36 test
    60822175 lrwxrwxrwx 1 hex hex  4 Oct 25 17:36 test-sl -> test
    

    参数:

    • -i 查看inode

    输出:

    • 第一列:inode号
    • 第二列:文件权限
    • 第三列:链接数目,指一共多少个文件名指向这个inode
    • 第四列:文件拥有者
    • 第五列:文件所属group
    • 第六列:文件大小
    • 第七列:文件内容上一次变动的时间
    • 第八列:文件名
  • stat命令

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    hex@hex-PC:/tmp/ln-test$ stat test-sl 
      File: test-sl -> test
    Size: 4               Blocks: 0          IO Block: 4096   symbolic link
    Device: 10302h/66306d   Inode: 60822175    Links: 1
    Access: (0777/lrwxrwxrwx)  Uid: ( 1000/     hex)   Gid: ( 1000/     hex)
    Access: 2022-10-25 17:36:18.216885854 +0800
    Modify: 2022-10-25 17:36:16.184873570 +0800
    Change: 2022-10-25 17:36:16.184873570 +0800
     Birth: 2022-10-25 17:36:16.184873570 +0800
    

    参数: 无

    输出:

    • File:显示文件名

    • Size:显示文件大小

    • Blocks:文件使用的数据块总数

    • IO Block:IO块大小

    • symbolic link:文件类型(软链接)

    • Inode:inode 号

    • Links:链接数,即有多少文件名指向这个 inode

    • Uid:文件拥有者的 Uid

    • Gid:文件所属group的 Gid

    • Access:文件的读、写、执行权限

    • 文件的时间戳,共有三个:

      • Change:简写为ctime,文件状态(链接数、大小、权限、Blocks数)改变时间。
      • Modify:简写为mtime,文件内容的修改时间,文件内容被修改时更新。
      • Access:简写为atime,文件内容的访问时间。文件内容被访问时更新。
      • Birth: 文件创建时间。

2.1 软链接、硬链接区别

功能项软链接硬链接
使用对象文件、目录文件
inode是否相同不同相同
是否跨文件系统(磁盘分区)
原文件删除软链接不可用不受影响
与原文件的关联项执行命令时,原文件名参数原文件inode
原文件是否必须存在
执行命令ln -s <src> <file-sl>ln <src> <file-hl>

2.2 软链接

软链接只是一个符号链接,其实就是新建立一个文件,这个文件就是专门用来指向别的文件。大小很小,权限是777,而真正的信息是由指向的原文件决定;

  • 删除软链接文件,不影响原文件;
  • 删除原文件,则相应的软链接不可用(cat那个软链接文件,则提示“没有该文件或目录“);

2.3 硬链接

硬链接实际上是为文件建一个别名,指向同一个inode。

  • 创建硬链接时,不会建立inode,只是在原文件的inode link count域再增加1;
  • 删除硬链接时,在原文件的inode link count域再减1;

系统调用会检查inode link count的数值,如果>=1,那么inode不会被回收,文件的内容不会被删除。

2.4 引申问题

2.4.1 软链接跳转靠的是文件名而非inode

验证思路:设置软链接时,原文件参数传递为相对路径;再将软链接移动到其他目录下;如果报错Not Found则说明成立

软链接查找使用的文件名,为ln执行命令时传入的原文件,如果是相对路径,则根据软链接路径进行查找。

  1. 准备测试环境

    1
    2
    3
    4
    5
    
    # 创建测试目录
    mkdir /tmp/ln-test/
    cd /tmp/ln-test/
    # 生成测试 原文件
    echo "source file" > test
    
  2. 设置软链接

    1
    
    ln -s test test-sl
    
  3. 查看文件状态

    执行命令ls -li(i参数为查看文件inode)

    1
    2
    3
    4
    
    hex@hex-PC:/tmp/ln-test$ ls -li
    total 4
    60822147 -rw-rw-r-- 1 hex hex 12 Oct 25 17:36 test
    60822175 lrwxrwxrwx 1 hex hex  4 Oct 25 17:36 test-sl -> test
    

    执行命令stat <filename>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    hex@hex-PC:/tmp/ln-test$ stat test-sl 
      File: test-sl -> test
      Size: 4               Blocks: 0          IO Block: 4096   symbolic link
    Device: 10302h/66306d   Inode: 60822175    Links: 1
    Access: (0777/lrwxrwxrwx)  Uid: ( 1000/     hex)   Gid: ( 1000/     hex)
    Access: 2022-10-25 17:36:18.216885854 +0800
    Modify: 2022-10-25 17:36:16.184873570 +0800
    Change: 2022-10-25 17:36:16.184873570 +0800
     Birth: 2022-10-25 17:36:16.184873570 +0800
    
  4. 移动软链接至其他目录

    1
    
    mv test-sl ../
    
  5. 通过软链接cat文件内容

    stat 查看软链接属性,File: test-sl -> test 显示软链接指向当前目录test文件,但test文件不存在。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    hex@hex-PC:/tmp/ln-test$ cd ../
    hex@hex-PC:/tmp$ stat test-sl 
      File: test-sl -> test
      Size: 4               Blocks: 0          IO Block: 4096   symbolic link
    Device: 10302h/66306d   Inode: 60822175    Links: 1
    Access: (0777/lrwxrwxrwx)  Uid: ( 1000/     hex)   Gid: ( 1000/     hex)
    Access: 2022-10-25 17:40:57.608602008 +0800
    Modify: 2022-10-25 17:36:16.184873570 +0800
    Change: 2022-10-25 17:40:34.131967367 +0800
     Birth: 2022-10-25 17:36:16.184873570 +0800
    hex@hex-PC:/tmp$ cat test-sl 
    cat: test-sl: No such file or directory
    
  6. 软链接所在新目录创建新的test文件,再查看

    1
    2
    3
    4
    5
    6
    
    hex@hex-PC:/tmp$ echo "Other test file" > test
    hex@hex-PC:/tmp$ ls -li test*
    60822202 -rw-rw-r-- 1 hex hex 16 Oct 25 17:47 test
    60822175 lrwxrwxrwx 1 hex hex  4 Oct 25 17:36 test-sl -> test
    hex@hex-PC:/tmp$ cat test-sl 
    Other test file
    

软链接移动前后,inode为60822175,未改变;但前后的原文件test的inode已发生改变60822147 -> 60822202。说明软链接是通过File: test-sl -> test记录所跳转的原文件,而非inode。

2.4.2 硬链接无法跨分区,而软链接可以的原因

硬盘格式化的时候,操作系统自动将硬盘分为两个区域:

  • 数据区:存放文件内容

  • inode 区:存放 inode 包含的信息,也叫作 inode table

硬链接通过与原文件共用同一inode访问blok读取文件内容。跨分区无法共享inode,所以硬链接无法跨分区。

软链接记录的是原文件的文件名, 而非inode。可以跨分区。

延伸个问题:Linux是如何跨分区寻找inode的

2.4.3 硬链接不支持目录的原因

系统限制对目录进行硬链接只是一个硬性规定,并不是逻辑上不允许、技术上不可行。

其实使用 ln -d 命令也允许 root 用户尝试建立目录硬链接,且...都是目录的硬链接。

  1. 由于 Linux 操作系统中的目录是以 / 为节点的树状结构,对目录的硬链接有可能破坏这种结构,甚至形成循环如: /usr/bin -> /usr/ ,在使用遍历目录的命令时(如: ls -R )系统就会陷入无限循环中。

  2. 如果使用 hard link 链接到目录时, 链接的数据需要连同被链接目录下面的所有数据都创建链接。因此造成环境相当大的复杂度。

    举例来说,如果要将 /etc 使用实体链接创建一个 /etc_hd 的目录时,那么在 /etc_hd 下面的所有文件名同时都与 /etc 下面的文件名要创建 hard link 的,而不是仅链接到 /etc_hd/etc 而已。 并且,未来如果需要在 /etc_hd 下面创建新文件时,连带的, /etc 下面的数据又得要创建一次 hard link

2.3.4 ...是目录的硬链接,特殊在哪里

创建目录时,默认会生成两个目录项: ...

  • . 相当于当前目录的硬链接;
  • .. 相当于父目录的硬链接。

目录硬链接总数:

  • 空目录的硬链接总数,等于2(.<file-name>);
  • 目录的硬链接总数,等于 2 + 它的子目录总数(子目录的 ..);

2.3.5 目录软链接删除,注意区分 软链接/软链接

目录的软链接=文件,而非目录文件;例如

目录文件:/tmp/ln-test/test-dir 等同于 /tmp/ln-test/test-dir/

目录的软链接:/tmp/ln-test/test-dir-sl <不等于> /tmp/ln-test/test-dir-sl/

​ rm 前者 == 删除软链接; rm后者 == 删除原目录。

验证:

  1. 准备环境

    1
    2
    3
    4
    
    # 创建目录
    mkdir -p /tmp/ln-test/test-dir
    
    cd /tmp/ln-test/test-dir
    
  2. 设置软链接

    1
    
    ln -s test-dir/ test-dir-sl
    
  3. 查看文件状态

    1
    2
    3
    4
    
    hex@hex-PC:/tmp/ln-test$ ls -li 
    total 4
    60822338 drwxrwxr-x 2 hex hex 4096 Oct 26 10:22 test-dir
    60822339 lrwxrwxrwx 1 hex hex    9 Oct 26 10:23 test-dir-sl -> test-dir/
    

    软链接 stat

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    hex@hex-PC:/tmp/ln-test$ stat test-dir-sl
      File: test-dir-sl -> test-dir/
      Size: 9               Blocks: 0          IO Block: 4096   symbolic link
    Device: 10302h/66306d   Inode: 60822339    Links: 1
    Access: (0777/lrwxrwxrwx)  Uid: ( 1000/     hex)   Gid: ( 1000/     hex)
    Access: 2022-10-26 10:23:12.046192861 +0800
    Modify: 2022-10-26 10:23:06.810155908 +0800
    Change: 2022-10-26 10:23:06.810155908 +0800
     Birth: 2022-10-26 10:23:06.810155908 +0800
    

    软链接/ 的stat

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    hex@hex-PC:/tmp/ln-test$ stat test-dir-sl/
      File: test-dir-sl/
      Size: 4096            Blocks: 8          IO Block: 4096   directory
    Device: 10302h/66306d   Inode: 60822338    Links: 2
    Access: (0775/drwxrwxr-x)  Uid: ( 1000/     hex)   Gid: ( 1000/     hex)
    Access: 2022-10-26 10:23:12.046192861 +0800
    Modify: 2022-10-26 10:22:18.821817214 +0800
    Change: 2022-10-26 10:22:18.821817214 +0800
     Birth: 2022-10-26 10:22:18.821817214 +0800
    

    可尝试执行stat test-dirstat test-dir/,会发现与stat test-dir-sl/inode一致。

  4. 测试删除

    通过上一步stat查看软链接/软链接已足够说明问题,通过删除再二次验证

    删除软链接/

    1
    2
    
    hex@hex-PC:/tmp/ln-test$ rm test-dir-sl/
    rm: cannot remove 'test-dir-sl/': Is a directory
    

    删除软链接

    1
    2
    3
    4
    
    hex@hex-PC:/tmp/ln-test$ rm test-dir-sl
    hex@hex-PC:/tmp/ln-test$ ls -li
    total 4
    60822338 drwxrwxr-x 2 hex hex 4096 Oct 26 10:22 test-dir
    

2.3.6 应用场景

软链接:

  1. 灵活切换不同版本的目标程序

    在开发的过程中,对于同一个工具软件,可能要安装多个不同的版本,例如:Python2Python3

    当在终端窗口中输入:python 时,启动的是 python2.7 版本。

    如果有一天需要使用 python3.5 版本,只需要把软链接 python 指向 python3.5 即可。

  2. 动态库版本管理

    场景描述: 想象这样一个情景,一个程序需要使用 foo_1.1 文件中的共享资源,由于 foo 经常改变版本号。每次升级后都得将使用 foo_1.1 的所有程序更新到 foo_1.2 文件,那么每次更新 foo 版本后,都要重复上边的工作。

    解决方案:创建一个 foo 的软链接指向 foo_1.2。这时,当一个程序访问 foo 时,实际上是访问 foo_1.2。当升级到 foo_1.3 时,只需要更新软链接指向。这不仅解决了版本升级问题,而且还允许在系统中保存两个不同的版本,如果 foo_1.3 有错误,再更新回原来的 foo_1.2 链接就可以。

  3. 快捷方式,将目录层次较深的文件链接到一个更易访问的目录中。

硬链接:

  1. 不同角度对文件进行分类
  2. 文件多人共享
  3. 文件备份

3.总结

  1. 目录的软链接比较特殊,尤其删除时注意区分带与不带的/的区别。<软链接>/原目录本身,后<软链接>软链接文件
  2. 硬链接无法跨分区是实现机制决定的,无法创建目录的硬链接是硬性规定(...都是目录的硬链接);
  3. 硬链接共用同一inode,增加硬链接相当于文件inode新的别名
  4. 软链接通过创建时的原文件参数查找原文件,而非原文件inode原文件参数==绝对路径,软链接可到处移动。否则不可以);

4.参考

[注1]: linux-inode说明

[注2]: “醉卧沙场:计算机专业性文章及回答总索引-存储和文件系统”

[注3]:Linux是如何跨分区寻找inode

[注4]: “鸟哥私房菜-链接ln说明”

分享

Hex
作者
Hex
CloudNative Developer

目录