相关文章推荐

概述

Linux系统中为了解决文件共享问题,引入了软链接和硬链接的概念,软链接又称符号链接,即 soft link symbolic link ,硬链接即为 hard link
同时它们还带来了隐藏文件路径、增加权限安全及节省存储等好处。

若一个文件指向另一个文件,该文件的内容仅仅是另一个文件的 path ,则其为软链接。
若一个 inode号 对应多个文件名,则称这些文件为硬链接。换言之,硬链接就是同一个文件使用了多个别名。
软链接可由命令 ln 创建,硬链接可由命令 link或ln 创建。

硬链接

由于硬链接的文件是有着相同 inode号 ,仅文件名不同,因此硬链接存在以下几点特性:

  • 文件有相同的 inode 和 data blocks
  • 只能对文件创建硬链接,不能对目录创建硬链接
  • 只能对已存在的文件创建硬链接
  • 不能跨文件系统创建硬链接
  • 删除一个硬链接文件并不影响其他有相同 inode号 的文件
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    $ ll
    total 4
    -rw-r--r-- 1 root root 928 Nov 11 08:37 tstfile
    $ ln tstfile hlink
    $ ll
    total 8
    -rw-r--r-- 2 root root 928 Nov 11 08:37 hlink
    -rw-r--r-- 2 root root 928 Nov 11 08:37 tstfile

    $ stat tstfile
    File: ‘tstfile’
    Size: 928 Blocks: 8 IO Block: 4096 regular file
    Device: 803h/2051d Inode: 4851726 Links: 2
    Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
    Access: 2017-11-11 08:37:07.554081068 +0800
    Modify: 2017-11-11 08:37:07.554081068 +0800
    Change: 2017-11-11 08:37:42.216988088 +0800
    Birth: -

    $ stat hlink
    File: ‘hlink’
    Size: 928 Blocks: 8 IO Block: 4096 regular file
    Device: 803h/2051d Inode: 4851726 Links: 2
    Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
    Access: 2017-11-11 08:37:07.554081068 +0800
    Modify: 2017-11-11 08:37:07.554081068 +0800
    Change: 2017-11-11 08:37:42.216988088 +0800
    Birth: -

    因为硬链接文件具有相同的地位,并没有谁指向谁之说,所以想要查找一个 inode号 对应的所有硬链接文件并不容易,通常需要遍历目录下所有的文件,系统里可通过 find 命令查找一个文件相关的所有硬链接文件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    $ mkdir dir
    $ ln tstfile dir/hlink

    $ find ./ -samefile tstfile
    ./dir/hlink
    ./hlink
    ./tstfile

    $ ls -i tstfile
    4851726 tstfile
    $ find ./ -inum 4851726
    ./dir/hlink
    ./hlink
    ./tstfile

    可以通过命令 strace find ./ -samefile tstfile 查看具体执行的系统调用,发现该命令会在输入的目录下查询所有的文件,输出与参数文件 inode num 一致的文件,所以在目录里文件非常多时,该命令会非常耗时的。

    软连接

    软链接与硬链接不同,若一个文件的数据块中存放的内容是另一文件的路径名时,则该文件就是软链接。
    软链接就是一个普通文件,只是数据块内容有点特殊。
    软链接有着自己的 inode 号以及数据块,因此软链接的创建与使用没有类似硬链接的诸多限制。

    软链接的特性如下:

  • 软链接有自己的文件属性及权限等
  • 可对不存在的文件或目录创建软链接
  • 软链接可跨文件系统创建
  • 软链接即可对文件创建,也可对目录创建
  • 创建软链接后,链接计数 i_nlink 不会增加
  • 删除软链接并不影响被指向的文件
  • 若被指向的原文件被删除,则相关软连接被称为死链接(dangling link),若被指向路径文件被重新创建,即恢复为正常的软链接
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    $ ln -s tstfile slink
    $ ll
    total 4
    lrwxrwxrwx 1 root root 7 Nov 11 08:39 slink -> tstfile
    -rw-r--r-- 1 root root 928 Nov 11 08:37 tstfile

    $ stat tstfile
    File: ‘tstfile’
    Size: 928 Blocks: 8 IO Block: 4096 regular file
    Device: 803h/2051d Inode: 4851726 Links: 1
    Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
    Access: 2017-11-11 08:37:07.554081068 +0800
    Modify: 2017-11-11 08:37:07.554081068 +0800
    Change: 2017-11-11 08:39:04.749766771 +0800
    Birth: -

    $ stat slink
    File: ‘slink’ -> ‘tstfile’
    Size: 7 Blocks: 0 IO Block: 4096 symbolic link
    Device: 803h/2051d Inode: 4851778 Links: 1
    Access: (0777/lrwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
    Access: 2017-11-11 08:39:24.730713312 +0800
    Modify: 2017-11-11 08:39:22.901718205 +0800
    Change: 2017-11-11 08:39:22.901718205 +0800
    Birth: -

    $ readlink slink
    tstfile

    测试程序

    Linux里由命令 stat 来获取一个文件的状态,文件状态保持在数据结构 struct stat 里,定义如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    struct stat {
    dev_t st_dev; /* ID of device containing file */
    ino_t st_ino; /* inode number */
    mode_t st_mode; /* protection */
    nlink_t st_nlink; /* number of hard links */
    uid_t st_uid; /* user ID of owner */
    gid_t st_gid; /* group ID of owner */
    dev_t st_rdev; /* device ID (if special file) */
    off_t st_size; /* total size, in bytes */
    blksize_t st_blksize; /* blocksize for file system I/O */
    blkcnt_t st_blocks; /* number of 512B blocks allocated */
    time_t st_atime; /* time of last access */
    time_t st_mtime; /* time of last modification */
    time_t st_ctime; /* time of last status change */
    };

    其中 st_mode 变量用来表示文件类型的,特征位的定义如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    S_IFMT     0170000   bit mask for the file type bit fields
    S_IFSOCK 0140000 socket
    S_IFLNK 0120000 symbolic link
    S_IFREG 0100000 regular file
    S_IFBLK 0060000 block device
    S_IFDIR 0040000 directory
    S_IFCHR 0020000 character device
    S_IFIFO 0010000 FIFO
    S_ISUID 0004000 set-user-ID bit
    S_ISGID 0002000 set-group-ID bit (see below)
    S_ISVTX 0001000 sticky bit (see below)
    S_IRWXU 00700 mask for file owner permissions
    S_IRUSR 00400 owner has read permission
    S_IWUSR 00200 owner has write permission
    S_IXUSR 00100 owner has execute permission
    S_IRWXG 00070 mask for group permissions
    S_IRGRP 00040 group has read permission
    S_IWGRP 00020 group has write permission
    S_IXGRP 00010 group has execute permission
    S_IRWXO 00007 mask for permissions for others (not in group)
    S_IROTH 00004 others have read permission
    S_IWOTH 00002 others have write permission
    S_IXOTH 00001 others have execute permission

    POSIX标准里定义了如下的宏来检查文件类型:

    1
    2
    3
    4
    5
    6
    7
    S_ISREG(m)  is it a regular file?
    S_ISDIR(m) directory?
    S_ISCHR(m) character device?
    S_ISBLK(m) block device?
    S_ISFIFO(m) FIFO (named pipe)?
    S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)
    S_ISSOCK(m) socket? (Not in POSIX.1-1996.)

    结合上面的描述,我们可以写如下测试代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    $ cat file_stat.cc
    #include <iostream>
    #include <string>
    #include <sys/stat.h>
    #include <unistd.h>

    using namespace std;

    int main(int argc, char* argv[])
    {
    struct stat fstat;
    string file_path = argv[1];

    # use lstat instead of stat as lstat handle symbolic link itself
    # when input path is a symbolic, not the file it refers to.
    int ret = lstat(file_path.c_str(), &fstat);
    cout << "file path: " << file_path << endl;
    cout << "file size: " << fstat.st_size << endl;
    cout << "file ino: " << fstat.st_ino << endl;
    cout << "file mode: " << fstat.st_mode << endl;
    cout << "file nlink: " << fstat.st_nlink << endl;

    if (S_ISREG(fstat.st_mode)) {
    cout << "regular file: " << file_path << endl;
    } else if (S_ISLNK(fstat.st_mode)) {
    cout << "link file: " << file_path << endl;
    char buf[256];
    ret = readlink(file_path.c_str(), buf, 255);
    buf[ret] = '\0';
    cout << "readlink return " << ret << endl;
    cout << "link file real path: " << buf << endl;
    }

    return 0;
    }

    //g++ -std=c++11 -ofile_stat file_stat.cc

    命令行输出

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    $ ./file_stat hlink
    file path: hlink
    file size: 928
    file ino: 4851726
    file mode: 33188
    file nlink: 3
    regular file: hlink

    $ ./file_stat slink
    file path: slink
    file size: 7
    file ino: 4851778
    file mode: 41471
    file nlink: 1
    link file: slink
    readlink return 7
    link file real path: tstfile

    参考

    https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/index.html
    http://www.giannistsakiris.com/2011/04/15/counting-and-listing-hard-links-on-linux/

     
    推荐文章