01-带你搞懂Linux虚拟文件系统

1,503 阅读13分钟

为了从更深层次了解计算机的IO ,需要对计算的文件系统进行深入学习,下边是本人的学习笔记,请大家一起参考,要是有说的不对的地方,请大家指正!

Linux 中的文件系统大概分为以下几类

-: # 普通文件类型
l:#软/硬连接类型
d:目录类型
s:socket 
p:pipline
b:块设备
c:字符设备

1、普通文件类型 (-)

通常用户所接触到的文件,比如图形文件、数据文件、文档文件以及声音文件都属于这种文件,这种类型的文件是按照其内部结构又可分为纯文本文件(ASCII)、二进制文件(binary)、数据格式的文件(data)、各种压缩文件。
举个例子 🌰 在linux系统/opt文件夹中创建一个普通文本文件test.txt并将"test info" 写入改文件

[root@node20 opt]# touch test.txt
[root@node20 opt]# echo "test info" > test.txt 

使用ll 指令查看该文件夹下内容

[root@node20 opt]# ll
total 4
drwx--x--x 4 root root 28 May 25 09:37 containerd
-rw-r--r-- 1 root root 10 Sep 23 22:22 test.txt
[root@node20 opt]# 

其中前排的 -rw-r--r-- 的第一个 -就表示普通文件类型
这里提一下文件的权限信息 第一位表示文件类型,剩下的三位一组,分别表示文件的所有者权限,文件所有组权限,其他人的权限
r是read的缩写表示读取
w是write的缩写表示可写
x表示文件可以被执行
-表示响应的权限还没被授予
后边的数字是该文件被指向的次数

2、软/硬连接

是一种特殊文件,指向一个真实存在的文件链接,类似于Windows下的快捷方式,链接文件的不同,又可分为硬链接文件和软链接文件(也称符号连接) 还拿刚才提到的test.txt 来举例子,我们分别来为test.txt创建一个硬链接和软连接

[root@node20 opt]# ln test.txt test1.txt
[root@node20 opt]# ln -s test.txt test2.txt

然后ll查看文件夹下内容

[root@node20 opt]# ll
total 8
drwx--x--x 4 root root 28 May 25 09:37 containerd
-rw-r--r-- 2 root root 10 Sep 23 22:22 test1.txt
lrwxrwxrwx 1 root root  8 Sep 23 22:34 test2.txt -> test.txt
-rw-r--r-- 2 root root 10 Sep 23 22:22 test.txt

其中的test1.txt为硬链接,test2.txt 为软连接
我们可以发现其中的test.txt 和text1.txt 文件的被指向次数都相同,都为2
然后我们通过stat指令查看文件的详细信息

[root@node20 opt]# stat test.txt 
  File: ‘test.txt’
  Size: 10        	Blocks: 8          IO Block: 4096   regular file
Device: fd00h/64768d	Inode: 18089313    Links: 2
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2022-09-23 22:21:49.315836748 -0400
Modify: 2022-09-23 22:22:01.452007290 -0400
Change: 2022-09-23 22:33:54.410503915 -0400
 Birth: -

这里我们记住他的Inode 编号为:18089313
然后我们在查看test1.txt 的详细信息

[root@node20 opt]# stat test1.txt 
  File: ‘test1.txt’
  Size: 10        	Blocks: 8          IO Block: 4096   regular file
Device: fd00h/64768d	Inode: 18089313    Links: 2
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2022-09-23 22:47:25.714321077 -0400
Modify: 2022-09-23 22:22:01.452007290 -0400
Change: 2022-09-23 22:33:54.410503915 -0400
 Birth: -

发现test1.txt 的Inode编号和test.txt 的编号完全相同
软连接的编号是不是一样的呢?我猜应该是不一样的吧,因为他们的被指向次数不一样啊,test2.txt 被指向了一次,test.txt 和test1.txt都被指向了两次,我们来用命令确认一下吧

[root@node20 opt]# stat test2.txt 
  File: ‘test2.txt’ -> ‘test.txt’
  Size: 8         	Blocks: 0          IO Block: 4096   symbolic link
Device: fd00h/64768d	Inode: 16785378    Links: 1
Access: (0777/lrwxrwxrwx)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2022-09-23 22:34:49.687244568 -0400
Modify: 2022-09-23 22:34:02.762618594 -0400
Change: 2022-09-23 22:34:02.762618594 -0400
 Birth: -

通过命令我们可以出test2.txt 和text.txt 的Inode编号是不一样的
我们尝试修改源文件text.txt 的内容,发现test1.txt 和test2.txt 的内容会同步修改。那软连接和硬连接有什么区别呢???
下边尝试删除源文件test.txt,发现test1.txt 可以正常打开,test2.txt 文件报错

[root@node20 opt]# rm -f test.txt 
[root@node20 opt]# ll
total 4
drwx--x--x 4 root root 28 May 25 09:37 containerd
-rw-r--r-- 1 root root 10 Sep 23 22:22 test1.txt
lrwxrwxrwx 1 root root  8 Sep 23 22:34 test2.txt -> test.txt
[root@node20 opt]# cat test2.txt 
cat: test2.txt: No such file or directory
[root@node20 opt]# cat test1.txt 
test info
[root@node20 opt]# 

3、目录类型

用于存放文件名以及其相关信息的文件,是内核组织文件系统的基本节点。目录文件可以包含下一级文件目录或者普通文件,在Linux中,目录文件是一种文件。
我们返回根目录,然后是用ll命令查看,以d打头的就是目录类型

[root@node20 opt]# cd /
[root@node20 /]# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
[root@node20 /]# ll
total 16
lrwxrwxrwx.   1 root root    7 Sep 25  2021 bin -> usr/bin
dr-xr-xr-x.   5 root root 4096 Sep 25  2021 boot
drwxr-xr-x   20 root root 3240 Sep 23 22:14 dev
drwxr-xr-x.  77 root root 8192 Sep 23 22:14 etc
drwxr-xr-x.   2 root root    6 Apr 11  2018 home
lrwxrwxrwx.   1 root root    7 Sep 25  2021 lib -> usr/lib
lrwxrwxrwx.   1 root root    9 Sep 25  2021 lib64 -> usr/lib64
drwxr-xr-x.   2 root root    6 Apr 11  2018 media
drwxr-xr-x.   3 root root   18 Sep 25  2021 mnt
drwxr-xr-x.   3 root root   58 Sep 23 22:57 opt
dr-xr-xr-x  112 root root    0 Sep 23 22:14 proc
dr-xr-x---.   4 root root  178 Sep 23 22:54 root
drwxr-xr-x   24 root root  660 Sep 23 22:14 run
lrwxrwxrwx.   1 root root    8 Sep 25  2021 sbin -> usr/sbin
drwxr-xr-x.   2 root root    6 Apr 11  2018 srv
dr-xr-xr-x   13 root root    0 Sep 23 22:14 sys
drwxrwxrwt.  10 root root  240 Sep 23 22:15 tmp
drwxr-xr-x.  13 root root  155 Sep 25  2021 usr
drwxr-xr-x.  19 root root  267 Sep 25  2021 var

4、socket 类型

说明socket类型,需要一个前提小知识:fd(文件描述符),任何一个程序都有的三个文件描述符 0:标准输入
1:标准输出
2:错误输出
他们是在Linux操作系统中怎么体现的呢?

[root@node20 /]# cd /proc/$$/fd
[root@node20 fd]# ll
total 0
lrwx------ 1 root root 64 Sep 23 22:17 0 -> /dev/pts/0
lrwx------ 1 root root 64 Sep 23 22:17 1 -> /dev/pts/0
lrwx------ 1 root root 64 Sep 23 22:17 2 -> /dev/pts/0
lrwx------ 1 root root 64 Sep 23 23:05 255 -> /dev/pts/0

命令解释: /proc 这个目录是Linux系统下一个很重要的目录。 它跟/etc, /home等这些系统目录不同, 它不是一个真正的文件系统, 而是一个虚拟的文件系统。 它不存在于磁盘, 而是存在于系统内存中,proc以文件系统的方式为访问系统内核的操作提供接口
$$ 查看当前的进程号
不同的进程中有不同的文件描述符,不同的文件描述符中保存着针对一个文件不同的偏移量
上面的话很重要哦,下面通过一个实验来证明他
-----------------------------------分割线--------------------------------------------------
创建一个文件并随便写入些内容

[root@node20 opt]# vim testfd.txt
[root@node20 opt]# cat testfd.txt 
dfi
sdfsdfadf
1
123
123
123
123
[root@node20 opt]#

我们创建一个文件描述符,去读取这个文件

exec 7< /opt/testfd.txt 
[root@node20 opt]# cd /proc/$$/fd
[root@node20 fd]# ll
total 0
lrwx------ 1 root root 64 Sep 23 22:17 0 -> /dev/pts/0
lrwx------ 1 root root 64 Sep 23 22:17 1 -> /dev/pts/0
lrwx------ 1 root root 64 Sep 23 22:17 2 -> /dev/pts/0
lrwx------ 1 root root 64 Sep 23 23:05 255 -> /dev/pts/0
lr-x------ 1 root root 64 Sep 23 22:17 7 -> /opt/testfd.txt

我们看到了"7"号文件描述符已经存在了
我们使用lsof这个命令来查看文件描述符的详细内容

[root@node20 fd]# lsof -op $$
COMMAND  PID USER   FD   TYPE DEVICE OFFSET     NODE NAME
bash    1428 root  cwd    DIR    0,3           21757 /proc/1428/fd
bash    1428 root  rtd    DIR  253,0              64 /
bash    1428 root  txt    REG  253,0        50333247 /usr/bin/bash
bash    1428 root  mem    REG  253,0        50333147 /usr/lib/locale/locale-archive
bash    1428 root  mem    REG  253,0             563 /usr/lib64/libnss_files-2.17.so
bash    1428 root  mem    REG  253,0             545 /usr/lib64/libc-2.17.so
bash    1428 root  mem    REG  253,0             551 /usr/lib64/libdl-2.17.so
bash    1428 root  mem    REG  253,0           64684 /usr/lib64/libtinfo.so.5.9
bash    1428 root  mem    REG  253,0             520 /usr/lib64/ld-2.17.so
bash    1428 root  mem    REG  253,0        16800876 /usr/lib64/gconv/gconv-modules.cache
bash    1428 root    0u   CHR  136,0    0t0        3 /dev/pts/0
bash    1428 root    1u   CHR  136,0    0t0        3 /dev/pts/0
bash    1428 root    2u   CHR  136,0    0t0        3 /dev/pts/0
bash    1428 root    7r   REG  253,0    0t0 18089326 /opt/testfd.txt
bash    1428 root  255u   CHR  136,0    0t0        3 /dev/pts/0
[root@node20 fd]#

ps:如果有提示-bash: lsof: command not found 这个错误的小伙伴,请先用yum install lsof -y 这个命令来安装lsof
我们看到了我们创建的"7"号文件描述符,他的OFFSET 指向了0,说明没有读取到文件
接下来我们使用 read命令来读取这个文件

[root@node20 fd]# read myinfo 0<& 7

read 会读取 7号文件描述符中的内容到myinfo 这个环境变量中,我们来打印这个环境变量

[root@node20 fd]# echo $myinfo
dfi
[root@node20 fd]#

myinfo 中的内容为dfi ,这是为啥呢?
read 会读到以换行符结束前的内容,因为我们在创建文件的时候,写入的第一行内容就是dfi
我们再次查看文件描述符的OFFSET

[root@node20 fd]# lsof -op $$
COMMAND  PID USER   FD   TYPE DEVICE OFFSET     NODE NAME
bash    1592 root  cwd    DIR    0,3           27327 /proc/1592/fd
bash    1592 root  rtd    DIR  253,0              64 /
bash    1592 root  txt    REG  253,0        50333247 /usr/bin/bash
bash    1592 root  mem    REG  253,0        50333147 /usr/lib/locale/locale-archive
bash    1592 root  mem    REG  253,0             563 /usr/lib64/libnss_files-2.17.so
bash    1592 root  mem    REG  253,0             545 /usr/lib64/libc-2.17.so
bash    1592 root  mem    REG  253,0             551 /usr/lib64/libdl-2.17.so
bash    1592 root  mem    REG  253,0           64684 /usr/lib64/libtinfo.so.5.9
bash    1592 root  mem    REG  253,0             520 /usr/lib64/ld-2.17.so
bash    1592 root  mem    REG  253,0        16800876 /usr/lib64/gconv/gconv-modules.cache
bash    1592 root    0u   CHR  136,1    0t0        4 /dev/pts/1
bash    1592 root    1u   CHR  136,1    0t0        4 /dev/pts/1
bash    1592 root    2u   CHR  136,1    0t0        4 /dev/pts/1
bash    1592 root    7r   REG  253,0    0t4 18089331 /opt/testfd.txt
bash    1592 root  255u   CHR  136,1    0t0        4 /dev/pts/1
[root@node20 fd]# 

发现7号文件描述符的OFFSET 已经变成4了
我们在打开一个新的窗口连接到虚拟机,进入到/proc/$$/fd 这个目录下,
使用ll查看就没有7号这个文件描述符,
我们也可以把上边创建文件描述符,读取文件内容的命令在新窗口执行一次,对比发现,他们的不同的文件描述符的OFFSET 是隔离的
-----------------------------------分割线--------------------------------------------------

补充完了上边的知识,现在可以演示socket 类型了

[root@node20 /]# exec 9<> /dev/tcp/www.baidu.com/80
[root@node20 /]# lsof -op $$
COMMAND  PID USER   FD   TYPE DEVICE OFFSET     NODE NAME
bash    1618 root  cwd    DIR  253,0              64 /
bash    1618 root  rtd    DIR  253,0              64 /
bash    1618 root  txt    REG  253,0        50333247 /usr/bin/bash
bash    1618 root  mem    REG  253,0             573 /usr/lib64/libresolv-2.17.so
bash    1618 root  mem    REG  253,0             561 /usr/lib64/libnss_dns-2.17.so
bash    1618 root  mem    REG  253,0        50333147 /usr/lib/locale/locale-archive
bash    1618 root  mem    REG  253,0             563 /usr/lib64/libnss_files-2.17.so
bash    1618 root  mem    REG  253,0             545 /usr/lib64/libc-2.17.so
bash    1618 root  mem    REG  253,0             551 /usr/lib64/libdl-2.17.so
bash    1618 root  mem    REG  253,0           64684 /usr/lib64/libtinfo.so.5.9
bash    1618 root  mem    REG  253,0             520 /usr/lib64/ld-2.17.so
bash    1618 root  mem    REG  253,0        16800876 /usr/lib64/gconv/gconv-modules.cache
bash    1618 root    0u   CHR  136,0    0t0        3 /dev/pts/0
bash    1618 root    1u   CHR  136,0    0t0        3 /dev/pts/0
bash    1618 root    2u   CHR  136,0    0t0        3 /dev/pts/0
bash    1618 root    9u  IPv4  28496    0t0      TCP node20:54904->39.156.66.14:http (ESTABLISHED)
bash    1618 root  255u   CHR  136,0    0t0        3 /dev/pts/0
[root@node20 /]#

我们定义了9号文件描述符,并将9号文件描述符的输入输出指向了www.baidu.com 的80 端口,我们就看到了9号文件描述符是个TCP 连接

5、管道类型

想要演示管道类型,也需要几个前提知识
$$可以表示当前进程的pid
$BASHPID也可以表示当前进程的pid
|管道可以衔接两个命令
{ }代码块
猜猜看下边的代码会输出什么?

[root@node20 /]# x=15
[root@node20 /]# echo $x
15
[root@node20 /]# { x=20;echo "asdf"; }|cat
asdf
[root@node20 /]# echo $x

定义了一个临时变量x,并赋值15,然后在代码块中将x赋值20,并将"asdf" 交给了管道由cat 进行输出,最后取值x,x的值会是15还是会是20呢?

15

答案是15!!!,让我们来看看为什么?
|管道左边会开启一个子进程,右边也会开启一个子进程,我们在当前的进程将x赋值为了15,在子进程中修改了x的值,由于进程间是隔离的我们在主进程中打印x的值,依然是15。
下边的实验来证明|会开启两个子进程
-------------------------------------分割线----------------------------------------------
首先查看当前进程

[root@node20 /]# echo $$
1618

当前的进程pid 为1618
然后管道左边打印当前进程,管道右边进行输出

[root@node20 /]# echo $$ | cat
1618

what?什么情况,这里也是 1618,刚才不是说好的左边会开启一个单独的进程吗?现在翻车了???
没有翻车,这里只是为了证明 $$ 的优先级比较高,linux 会先执行管道左边的$$ 然后输出
刚刚我们说过了$BASEHPID 也能打印当前进程,我们换个命令试一下

[root@node20 /]# echo $BASHPID |cat
1686

结果已经出来了,管道左边的进程是1686,说明管道左边会开启一个新的进程

-------------------------------------分割线----------------------------------------------
咱们的目的是为了证明linux 有管道这种文件类型
我们来看一下下边的程序

[root@node20 /]# { echo $BASHPID; read x; } | { cat ; ehco $BASHPID ; read z ; }
1713

我们用管道连接了两个代码块,左边的代码块打印了当前的进程id,也就是新的进程id,然后用read 命令进行了阻塞;管道右边将左边的进程id进行了打印,同时也打印了个右边的新的进程id,同样使用read 进行了阻塞;由于read 的阻塞,右边的进程没有输出出来,通过上边的实验我们已经知道了,当前标签页的进程id为1618,我们重新开一个新的标签页,去查询进程号为1618的进程

[root@node20 /]# ps -ef |grep 1618
root       1618   1616  0 Sep23 pts/0    00:00:00 -bash
root       1713   1618  0 01:37 pts/0    00:00:00 -bash
root       1714   1618  0 01:37 pts/0    00:00:00 -bash
root       1738   1720  0 01:42 pts/2    00:00:00 grep --color=auto 1618
[root@node20 /]# 

我们可以看到1618 产生了两个新的子进程,分别是 1713 和1714
然后我们使用lsof 命令查看这两个子进程的文件描述符

[root@node20 /]# lsof -op 1713
COMMAND  PID USER   FD   TYPE DEVICE OFFSET     NODE NAME
bash    1713 root  cwd    DIR  253,0              64 /
bash    1713 root  rtd    DIR  253,0              64 /
bash    1713 root  txt    REG  253,0        50333247 /usr/bin/bash
bash    1713 root  mem    REG  253,0             573 /usr/lib64/libresolv-2.17.so
bash    1713 root  mem    REG  253,0             561 /usr/lib64/libnss_dns-2.17.so
bash    1713 root  mem    REG  253,0        50333147 /usr/lib/locale/locale-archive
bash    1713 root  mem    REG  253,0             563 /usr/lib64/libnss_files-2.17.so
bash    1713 root  mem    REG  253,0             545 /usr/lib64/libc-2.17.so
bash    1713 root  mem    REG  253,0             551 /usr/lib64/libdl-2.17.so
bash    1713 root  mem    REG  253,0           64684 /usr/lib64/libtinfo.so.5.9
bash    1713 root  mem    REG  253,0             520 /usr/lib64/ld-2.17.so
bash    1713 root  mem    REG  253,0        16800876 /usr/lib64/gconv/gconv-modules.cache
bash    1713 root    0u   CHR  136,0    0t0        3 /dev/pts/0
bash    1713 root    1w  FIFO    0,9    0t0    36649 pipe
bash    1713 root    2u   CHR  136,0    0t0        3 /dev/pts/0
bash    1713 root    9u  IPv4  28496    0t0      TCP node20:54904->39.156.66.14:http (CLOSE_WAIT)
bash    1713 root  255u   CHR  136,0    0t0        3 /dev/pts/0
[root@node20 /]#

我们可以看到1713的1号文件描述符也就是标准输出,TYPE 类型成了一个FIFO的管道类型;
我们继续使用lsof命令查看1714的文件描述符

[root@node20 /]# lsof -op 1714
COMMAND  PID USER   FD   TYPE DEVICE OFFSET     NODE NAME
bash    1714 root  cwd    DIR  253,0              64 /
bash    1714 root  rtd    DIR  253,0              64 /
bash    1714 root  txt    REG  253,0        50333247 /usr/bin/bash
bash    1714 root  mem    REG  253,0             573 /usr/lib64/libresolv-2.17.so
bash    1714 root  mem    REG  253,0             561 /usr/lib64/libnss_dns-2.17.so
bash    1714 root  mem    REG  253,0        50333147 /usr/lib/locale/locale-archive
bash    1714 root  mem    REG  253,0             563 /usr/lib64/libnss_files-2.17.so
bash    1714 root  mem    REG  253,0             545 /usr/lib64/libc-2.17.so
bash    1714 root  mem    REG  253,0             551 /usr/lib64/libdl-2.17.so
bash    1714 root  mem    REG  253,0           64684 /usr/lib64/libtinfo.so.5.9
bash    1714 root  mem    REG  253,0             520 /usr/lib64/ld-2.17.so
bash    1714 root  mem    REG  253,0        16800876 /usr/lib64/gconv/gconv-modules.cache
bash    1714 root    0r  FIFO    0,9    0t0    36649 pipe
bash    1714 root    1u   CHR  136,0    0t0        3 /dev/pts/0
bash    1714 root    2u   CHR  136,0    0t0        3 /dev/pts/0
bash    1714 root    9u  IPv4  28496    0t0      TCP node20:54904->39.156.66.14:http (CLOSE_WAIT)
bash    1714 root  255u   CHR  136,0    0t0        3 /dev/pts/0
[root@node20 /]# 

1714进程的0号文件描述符也就是标准输入文件描述符也是一个FIFO类型的管道,并且他们的NODE 编号都是36649
通过以上的实验证明了linux 中pipline 的文件类型

6、块设备

块设备文件 : 就是存储数据以供系统存取的接口设备,简单而言就是硬盘。例如一号硬盘的代码是 /dev/hda1等文件
块设备可以用df命令进行查看

[root@node20 /]# df
Filesystem              1K-blocks    Used Available Use% Mounted on
devtmpfs                   919588       0    919588   0% /dev
tmpfs                      931496       0    931496   0% /dev/shm
tmpfs                      931496    9788    921708   2% /run
tmpfs                      931496       0    931496   0% /sys/fs/cgroup
/dev/mapper/centos-root  17811456 2821672  14989784  16% /
/dev/sda1                 1038336  198540    839796  20% /boot
tmpfs                      186300       0    186300   0% /run/user/0

这里显示了文件的挂载信息,我的是虚拟机
块设备就是硬盘挂载的那些东西,运维用的会比较多,这里本文不再介绍。

7、字符设备

字符设备文件:即串行端口的接口设备,例如键盘、鼠标等等。
这里为了学习IO ,字符设备也略过了.....

8、额外的话

刚开始学习linux 的话,好多命令都记不住,推荐一下小的插件,在Utools 里边,名字就叫做linux ,可以随时查看linux 相关操作,这里放个gif吧

utool-linux.gif