VFS

内核既管内存,又管磁盘IO。
作为LINUX内核来说,它在内存中构建了一个虚拟文件系统VFS,不同于windows上的物理文件系统结构,C盘代表的就是物理的C盘分区,D盘就是D盘的物理分区,
VFS本质就是一颗目录树,每个目录可以映射代表不同的物理设备。

为什么要有VFS?

因为VFS相当于一个中间的解耦层,下层的存储源的存储形式可能是各不相同的,可能是来自不同的硬件设备,但需要将这些包成一个统一的对外接口暴露给上层应用使用。

pagecache

在VFS中,每一个文件都有一个inode id作为唯一标识,一个文件首先要被内核读到内存中,然后开启一个pagecache(默认4K)作为这个文件在内存中的缓存,这样的话如果多个应用都需要这同一个文件,只需要来内存中命中这一份文件,而不需要多次读取,后续对文件的操作都将是基于内存中缓存的操作,将会变得非常快

dirty

pagecache作为文件的缓存,当被进行修改了的时候,实际是对这个缓存内容的修改,此时就会标记成dirty

flush

被标记为dirty,意味着这个缓存的文件是发生了变化的,也就是需要将变化同步更新到磁盘的真实文件中,所以会有一个flush的操作,但flush的触发时机也是分为很多种,每一种的成本也不同:
1、例如可以修改一次缓存文件,就通过内核刷写更新一次内容到磁盘文件中(响应快,但效率最差)
2、考虑到dirty的文件并不是只针对某一个文件的, 而是内核中会存在许多dirty的文件,因此可以当内核中的dirty达到一定比例,内核进行刷写(存在中间数据丢失风险,效率较高)
3、可以周期性的进行刷洗内核中的dirty的文件。(存在中间数据丢失风险,效率较高)

FD(文件描述符)

文件的内存中的pagecache,就意味着多个应用有可能都共享使用一份缓存内容,那么各自的例如对文件的不同位置的读取,就涉及到一个seek偏移量可能是各不相同的,而这些包起来对外来看就统一用FD来表示。

通过df -h可以看到当前VFS的整个结构

这个结构就是首先挂载了来自/dev/sda3的一个/的根目录,然后发现还有一个单独从/dev/sda1挂载的/boot目录,而实际上/下面也应该有一个/boot目录,但这个挂载的/boot目录会覆盖原本/中的/boot目录。

这里看到的实际上就是来自 /dev/sda1的/boot

那如果把这个挂载的/boot去掉呢?

发现挂载结构少了/boot,确实卸载掉了/boot:

此时再去/下查看,发现/boot还在,这个/boot就是/原本的/boot,只不过里边是空的:

此时重新再挂载上刚才卸载的/boot,发现再次查看/boot,内容又回来了:

但这个过程我们可以发现,对于程序而言,这个文件目录树结构非常的稳定,不会随着挂载的变动,目录发生什么变化

并且这之中其实是有一个映射的过程,通过这个过程,我们可以想到,日后如果某个文件夹例如/abc的大小不满足需要,完全可以接入一块新的比较大的外接设备,然后把旧数据迁移过去,然后将外接设备挂载到/abc,也就是覆盖掉原来的/abc,这样就完成了一个无结构修改的静默扩容操作了。

LINUX中的一切皆文件

在冯诺依曼体系结构下,计算器,控制器就相当于我们的CPU,主存储器就相当于我们的内存, 而输入输出设备也就是一切的IO设备。而这一切,在linux之中,全都是用文件进行表示。

文件类型

-:普通文件

可执行,图片,文本

-rwxr-xr-x. 1 root root 764088 4月   5 2012 vi

d:目录

dr-xr-xr-x.  2 root root  4096 6月  12 2019 bin

l:链接

软链接,硬链接。
一个文件的硬链接下的各个文件与源文件共享inodeid,可以通过stat xx文件查看,并且会在文件本身标识着引用数,当删除源文件时,对其他的硬链接文件没有影响。
一个文件的软链接下的各个文件inodeid是不同的。当删除源文件时,软链接的文件也会无法使用

b:块设备

读取的内容位置可以来回漂移。
例如:硬盘

brw-rw---- 1 root disk 8, 1 7月  23 22:37 sda1

c:字符设备

只能向后读取,不能自由读取前后偏移量的数据,可能会有一些编解码约束,不能被切割的字符数据
键盘,socket

crw--w---- 1 root tty       4,  32 7月  23 22:37 tty32
crw--w---- 1 root tty       4,  33 7月  23 22:37 tty33
crw--w---- 1 root tty       4,  34 7月  23 22:37 tty34
crw--w---- 1 root tty       4,  35 7月  23 22:37 tty35
crw--w---- 1 root tty       4,  36 7月  23 22:37 tty36
crw--w---- 1 root tty       4,  37 7月  23 22:37 tty37
crw--w---- 1 root tty       4,  38 7月  23 22:37 tty38
crw--w---- 1 root tty       4,  39 7月  23 22:37 tty39

s:socket(底层类型,不能直接看到)

[root@dream01 fd]# exec 8<> /dev/tcp/www.baidu.com/80
[root@dream01 fd]# cd /proc/$$/fd
[root@dream01 fd]# ll
总用量 0
lrwx------ 1 root root 64 7月  12 01:30 0 -> /dev/pts/0
lrwx------ 1 root root 64 7月  12 01:31 1 -> /dev/pts/0
lrwx------ 1 root root 64 7月  12 01:31 2 -> /dev/pts/0
lrwx------ 1 root root 64 7月  12 01:31 255 -> /dev/pts/0
lr-x------ 1 root root 64 7月  12 01:31 8 -> socket:[24388]
[root@dream01 fd]# lsof -op $$
COMMAND  PID USER   FD   TYPE DEVICE OFFSET    NODE NAME
bash    9310 root  cwd    DIR    0,3          24173 /proc/9310/fd
bash    9310 root  rtd    DIR    8,3              2 /
bash    9310 root  txt    REG    8,3        2621442 /bin/bash
bash    9310 root  mem    REG    8,3        3932199 /lib64/libresolv-2.12.so
bash    9310 root  mem    REG    8,3        3932187 /lib64/libnss_dns-2.12.so
bash    9310 root  mem    REG    8,3        3932189 /lib64/libnss_files-2.12.so
bash    9310 root  mem    REG    8,3        1971152 /usr/lib/locale/locale-archive
bash    9310 root  mem    REG    8,3        3932173 /lib64/libc-2.12.so
bash    9310 root  mem    REG    8,3        3932179 /lib64/libdl-2.12.so
bash    9310 root  mem    REG    8,3        3932216 /lib64/libtinfo.so.5.7
bash    9310 root  mem    REG    8,3        3932163 /lib64/ld-2.12.so
bash    9310 root  mem    REG    8,3        2230781 /usr/share/locale/zh_CN/LC_MESSAGES/libc.mo
bash    9310 root  mem    REG    8,3        1967051 /usr/lib64/gconv/gconv-modules.cache
bash    9310 root    0u   CHR  136,0    0t0       3 /dev/pts/0
bash    9310 root    1u   CHR  136,0    0t0       3 /dev/pts/0
bash    9310 root    2u   CHR  136,0    0t0       3 /dev/pts/0
bash    9310 root    8r  IPv4  24388    0t0     TCP dream01:59731->39.156.66.14:http (ESTABLISHED)  ##这就是socket类型
bash    9310 root  255u   CHR  136,0    0t0       3 /dev/pts/0

p:pipeline(底层类型,不能直接看到)

上面代码管道符左右都会各自启动一个子进程去执行花括号里的内容,而我们知道管道符的作用是将管道符左边的输出作为右边的输入,那它是如何实现的呢?

生成的子进程号分别是4512和4513,此时看下它们的文件描述符:

可以看到左侧子进程(4512)的输出到pipe管道文件描述符上,而右侧子进程(4513)的输入也为同一个pipe管道文件描述符上,这样就完成了管道符的功能。

通过lsof查看:

两者指向的都是一个inodeid 39968的pipe,并且一个是写,一个是读。

[eventpoll]:

内核提供给epoll的内存区域。
因为redis就是基于epoll实现连接的,启动redis,然后看redis的文件描述符可以看到:

lrwx------ 1 root root 64 7月  23 22:37 0 -> /dev/null
lrwx------ 1 root root 64 7月  23 22:37 1 -> /dev/null
lrwx------ 1 root root 64 7月  23 22:37 2 -> /dev/null
lr-x------ 1 root root 64 7月  23 22:37 3 -> pipe:[9002]
l-wx------ 1 root root 64 7月  23 22:37 4 -> pipe:[9002]
lrwx------ 1 root root 64 7月  23 22:37 5 -> [eventpoll]
lrwx------ 1 root root 64 7月  23 22:37 6 -> socket:[9006]

有意思的实验-生成挂载镜像

 dd if=/dev/zero of=mydisk.img bs=1048576 count=100

输入是/dev/zero(空),输出是mydisk.img,一个块的大小是1048576(1M),一共有100个块组成,最后生成的就是100M的被0填充的文件

losetup /dev/loop0 mydisk.img

让环卫接口设备/dev/loop0挂载刚刚生成的文件mydisk.img 也就是/dev/loop0不再指向一个物理地址,而是指向了新生成的这个文件。

 mke2fs /dev/loop0

格式化成ext2的文件格式。

到现在为止,我们已经成功挂载到了一块虚拟环卫设备,那能不能也类似的让linux中的某个虚拟文件路径映射到这个虚拟设备上? 就类似于上面/boot的效果。

我们先生成一个虚拟路径:

mkdir -p ~/io_test/mnt/xxoo/
mount -t ext2  /dev/loop0 ~/io_test/mnt/xxoo/

指定挂载的文件格式为ext2,把它(/dev/loop0)挂载到~/io_test/mnt/xxoo/的虚拟路径上

[root@dream01 io_test]# df -lh
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda3        97G  9.4G   83G  11% /
tmpfs           931M     0  931M   0% /dev/shm
/dev/sda1       194M   27M  158M  15% /boot
/dev/loop0       97M  1.6M   91M   2% /root/io_test/mnt/xxoo

此时可以看到,文件路径映射已经形成了这两者的映射。
此时移动到~/io_test/mnt/xxoo/,发现内容不是空的, 而是已经是挂载的设备的内容了:

[root@dream01 xxoo]# ll
总用量 12
drwx------ 2 root root 12288 7月  19 22:23 lost+found

仿docker的思想雏形方向体现

先看一下我们的bash程序在哪:

[root@dream01 xxoo]# whereis bash
bash: /bin/bash /usr/share/man/man1/bash.1.gz

我们在虚拟路径里照样创建一个bin的目录,并将系统的bin拷贝过来:

[root@dream01 xxoo]# mkdir bin
[root@dream01 xxoo]# cp /bin/bash bin/

然后查看一下bash需要的第三方依赖是什么:

[root@dream01 bin]# ldd bash linux-vdso.so.1 =>  (0x00007fffd1bff000)libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007f9f2ca1f000)libdl.so.2 => /lib64/libdl.so.2 (0x00007f9f2c81b000)libc.so.6 => /lib64/libc.so.6 (0x00007f9f2c486000)/lib64/ld-linux-x86-64.so.2 (0x00007f9f2cc49000)

然后把这些依赖也都拷贝过来:

[root@dream01 xxoo]# mkdir lib64
[root@dream01 xxoo]# cp /lib64/{libtinfo.so.5,libdl.so.2,libc.so.6,ld-linux-x86-64.so.2} lib64/

接下来,如果我们能让当前的虚拟挂载点作为根目录,是不是就可以也可以启动我们刚刚拷贝过来的bash了呢?

先看一下当前的bash进程号:

[root@dream01 xxoo]# echo $$
1264

把根目录切换到当前目录,并启动当前目录的bash:

[root@dream01 xxoo]# chroot ./

然后发现确实启动了一个新的bash解释程序,我们打印一下当前的进程号:

bash-4.1# echo $$
39999

发现是39999

此时如果打印这么一句生成文件的指令:

bash-4.1# echo "hello" > /hello.txt

然后退出bash,发现内容被输出到了我们刚刚指定的新的根目录,也就是xxoo目录下:

[root@dream01 xxoo]# ll
总用量 15
drwxr-xr-x 2 root root  1024 7月  19 22:35 bin
-rw-r--r-- 1 root root     6 7月  19 22:47 hello.txt
drwxr-xr-x 2 root root  1024 7月  19 22:41 lib64
drwx------ 2 root root 12288 7月  19 22:23 lost+found

是不是略有一点docker的味道呢?但实际docker肯定不是这么简单的,要复杂的多,但我们做的好像已经有点这种味道了,这样让相当于一个操作系统内出现多个子操作系统,并且每个人都有每个人的根目录。docker也是基于虚拟文件系统的支撑才得以实现的。
而我们操作的其实都是这块挂载设备,卸载之后,发送给他人,他人依旧可以挂载使用,对里面的内容进行修改。 其实回想docker,最开始也是要生成一个img的镜像文件,然后对其里面进行服务注入。

IO那些事01-IO总述和文件描述符相关推荐

  1. [转]文件IO详解(二)---文件描述符(fd)和inode号的关系

    原文:https://www.cnblogs.com/frank-yxs/p/5925563.html 文件IO详解(二)---文件描述符(fd)和inode号的关系 ---------------- ...

  2. Linux C:文件描述符、IO重定向、恢复标准输入输出

    目录 一.文件描述符 二.IO重定向 三.重定向回终端.伪终端 四.恢复标准输入输出 一.文件描述符 在Linux中,文件描述符是一个非负整数的数据类型.是FILE结构体中的一个成员属性. 每打开或者 ...

  3. 网络与IO知识扫盲(一):Linux虚拟文件系统,文件描述符,IO重定向

    系统IO原理 在 Linux 中: VFS(Virtual Filesystem Switch):虚拟文件系统,是一个目录树.树上不同的节点可以映射到物理的文件地址,可以挂载. 相当于一个解耦层,在具 ...

  4. 【Linux篇】第九篇——基础IO(系统文件IO+文件描述符+重定向+文件系统+软硬链接)

    ⭐️这篇博客就要开始聊一聊Linux中基础IO相关知识,IO相信大家都不陌生,我们在C/C++中对文件进行读写的操作,也就是文件IO,这篇博客我也会带大家回顾一下.这篇博客还会介绍系统中的文件IO调用 ...

  5. LSMW批处理使用方法(01)_总述及界面说明

    一.总述 在SAP系统中,批处理操作有多种方法.如果是对一个事物码(TCODE)进行批处理操作,常用的是LSMW.LSMW全称是Legacy System Migration Workbench.它能 ...

  6. 第3章 文件IO | 001 文件描述符

    概述 在Linux系统中一切皆可以看成是文件,文件又可分为:普通文件.目录文件.链接文件和设备文件.文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,其是一个非 ...

  7. Linux系统编程20:基础IO之从内核代码深刻理解Linux是如何管理文件及文件描述符的本质是什么

    文章目录 (1)文件描述符到底是什么 A:输出描述符 B:文件描述符 (2)系统如何管理文件 (3)一切皆文件 (4)用源代码验证 (5)FILE (1)文件描述符到底是什么 A:输出描述符 编写如下 ...

  8. 【Linux】基础IO(万字详解) —— 系统文件IO | 文件描述符fd | 重定向原理

  9. Linux操作系统~系统文件IO,什么是文件描述符fd?什么是vfs虚拟文件系统

    目录 1.open() (1).第二个参数flags-通过比特位传多组标记 2.文件描述符fd(open函数的返回值) (1).fd的本质 (2).vfs-虚拟文件系统(一切皆文件) (3).调用re ...

最新文章

  1. PHP脚本批量清除nginx缓存的方法
  2. php将中文插入数据库出现乱码
  3. [hadoop新实战2]hadoop伪分布式安装序列(支持ubuntu和redhat)
  4. 【Ubuntu】 Ubuntu 16.04 安装经典菜单 0.10
  5. Flask自定义时间过滤器
  6. /usr/include/sys/types.h基本系统数据类型
  7. OpenCV4Android人脸检测功能
  8. [转载] python并行处理任务_Python 并行任务技巧
  9. 时间变为.05PU sql长度写法
  10. 【渝粤教育】国家开放大学2018年秋季 0300-22T图形创意 参考试题
  11. 使用JAVA实现邮件发送功能
  12. 刻录光驱只能读不能写怎么办?来看看!
  13. 君正X1000芯片软件开发手册
  14. 楼兰宝盒显示网络服务器无响应,捷达vs5-圈里有谁跟我一样,安装了楼兰宝盒后,用手机启动车子出现无钥匙解锁失灵时候使坏,和前部辅助系统出现故障问题,不用手机启动就没事...
  15. 数据分析常见的英文缩写(一)
  16. KlipC数据显示2022年日元兑美元汇率有进一步下跌的风险和可能性
  17. 为什么黑客用python-《Python绝技》:运用Python成为顶级黑客
  18. vivo手机android耗电快怎么解决,vivo手机耗电严重怎么办 如何解决手机耗电严重的问题...
  19. ESP32开发环境的搭建和 ESP-IDF支持以下调试方法
  20. Spacy model download

热门文章

  1. 几款主流好用的富文本编辑器(所见即所得常用编辑器)介绍
  2. java 计算π_Java实现计算圆周率π的两种方法 - 博客频道 - CSDN.NET
  3. 区块链应该打造国产操作系统
  4. 【STM32】两轮自平衡小车学习笔记1
  5. 1949 年的国庆节(10 月 1 日)是星期六,今年……(C语言)
  6. 诸多检测、实验让你自己判断转基因大豆油是否安全无害!
  7. cmd如何切换到E盘
  8. HDLBits练习汇总-13-时序逻辑设计测试--状态机(一)
  9. 使用计算机语言编程是用几进制,使用计算机高级语言编程,将下列变量值的二进制编码打印输出。请给出程序源代码和输出结果(可截屏):...
  10. ECSHOP商品页调用热销商品的教程