IO-内存IO/磁盘IO/网络IO扫盲(一):Linux虚拟文件系统、文件描述符、IO重定向、inode

linux系统基本结构

Linux系统基本结构
Linux和核心就是kernel(内核),他负责整个系统的运行和对硬件的调度。将用户对系统的调用和各类硬件的调用都统一和封装。应用只要和kernel沟通,让kernel和各类硬件去交流,我们应用开发技术人员就不需要管他就好了。

kernel

基本概念

VFS(Virtual Filesystem Switch): 虚拟文件系统,是一个目录树。树上不同的节点可以映射到物理的文件地址,可以挂载。

文件描述符FD: 每一个进程在内核中,都对应有一个“打开文件”数组,存放指向文件对象的指针,而 fd 是这个数组的下标。
Inode 文件在硬盘中的存储对应的block的索引节点。

文件描述符FD

每一个进程在内核中,都对应有一个“打开文件”数组,存放指向文件对象的指针,而 fd 是这个数组的下标。

我们对文件进行操作时,系统调用,将fd传入内核,内核通过fd找到文件,对文件进行操作。

既然是数组下标,fd的类型为int, < 0 为非法值, >=0 为合法值。在linux中,一个进程默认可以打开的文件数为1024个,fd的范围为0~1023。可以通过设置,改变最大值。

在这个数组中,fd的值为0、1、2是固定的,分别代表着特殊的意思。
0 表示标准输入文件path地址
1 表示标准输出文件path地址
2 表示标准错输出文件path地址
所以,通常在程序中打开的fd都是从3开始的。
对于0 1 2 我们可以通过操作来控制他的输出对象分别是哪里
比如说:
fd=0的值改变为指向键盘,那就是键盘输入作为整个进程的输入设备,比如命令 cat 0<cat.txt
fd=1的值改变为文件,那就是标准输出输出到一个文件中,比如命令 cat 1> cat.txt

1、lsof命令
lsof是list open files的简称,它的作用主要是列出系统中打开的文件,基本上linux系统中所有的对象都可以看作文件,lsof可以查看用户和进程操作了哪些文件,也可以查看系统中网络的使用情况,以及设备的信息。
在这里插入图片描述
创建一个文件描述符8,用来读取ooxx.txt
NODE列:表示Inode号
如果lsof加上-o参数的话,会显示一列OFFSET,表示当前读文件位置的指针。
在这里插入图片描述
使用read读文件:
在这里插入图片描述
新开了一个bash标签页,用一个新的文件描述符6,去读取ooxx.txt
证明两个进程读取文件时,不会相互影响:
在这里插入图片描述
我们可以得出这个结论,内核为每一个进程各自维护了一套数据,包括fd文件描述符。
fd维护了一些关于文件的偏移、Inode号,以及元数据信息等。

这些内容看起来和Java没什么关系,但是在使用Java进行文件IO时,关系到每种不同的写法的成本。所以还是很重要的~

Inode

可以看该博客进行细节了解:http://www.ruanyifeng.com/blog/2011/12/inode.html
感谢阮一峰大神(_)

inode是一个重要概念,是理解Unix/Linux文件系统和硬盘储存的基础。

理解inode,要从文件储存说起。

文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector)。每个扇区储存512字节(相当于0.5KB)。

操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个"块"(block)。这种由多个扇区组成的"块",是文件存取的最小单位。"块"的大小,最常见的是4KB,即连续八个 sector组成一个 block。

文件数据都储存在"块"中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为"索引节点"。

每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。每个inode节点的大小,一般是128字节或256字节。

困扰我多年的疑问: 一个inode指向的一个固定的block区域,一个文件我看只有一个inode,那为什么有的文件1M,有的1G,这个怎么玩?????/(ㄒoㄒ)/~~
答疑:一个文件对应的在系统中的inode这个解答是没错,但是其实他是一个层次型的inode结构。一个文件存储的结构其实是一个树状的,我们对外的inode其实就是这个树的根的inode。
保存一个大文件时结构是这样的
inode–>[block:下一层的inode_list,数据]–>[block:下一层的inode_list,数据]
如下图的结构
在这里插入图片描述

VFS(Virtual Filesystem Switch)

虚拟文件系统,是一个目录树。他将一切对硬件的操作和文件的操作都作为文件来操作;才有了我们所说的“一切皆文件”的思想。
一切接文件的思想
虚拟文件系统进行了一个抽象:一切接文件。
磁盘文件、摄像头、打印机,都被看做是文件,基于文件的这种抽象,就可以应用到IO流了。
那么如何区分这些不同的文件?我们引入文件类型:
在这里插入图片描述
下面我们开始搞事情,做一个小实验,证明一切皆文件
用dd命令生成一个空的mydisk.img文件
在这里插入图片描述
挂载到虚拟的环回设备上,挂载之后进行格式化
在这里插入图片描述
把新的loop0挂载到原来的/mnt/ooxx目录中去
在这里插入图片描述
现在/mnt/ooxx是空的
在这里插入图片描述
我们希望用子目录模仿根目录里面的目录结构,以及程序的摆放位置。

1、找到bash所在位置,拷贝过来
在这里插入图片描述
2、将bash需要动态链接的文件,也拷贝过来
在这里插入图片描述
将根目录切换到当前目录,并将当前目录下的bin下的bash启动
在这里插入图片描述
在当前bash中输出“hello mashibing”,重定向到根目录下的abc,txt文件中
在这里插入图片描述
可以看到abc.txt被输出到新的根目录下。

那么Docker呢?也是这个原理吗?
这个实验不同于Docker,Docker更加复杂,它不只是文件系统的命名空间的一个子域。

Docker复用的是物理机的内核,Docker里面跑的是进程,先有镜像,有了img镜像之后,才有container容器的概念。
源于整个虚拟文件系统的支撑。

Socket 文件类型示例

用文件描述符8,指向一个socket连接
在这里插入图片描述
在这里插入图片描述

关于/proc目录

/proc 表示内核映射目录,保存一些系统运行时内核的一些属性
与其它常见的文件系统不同的是,/proc是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊的属性文件
在这里插入图片描述
/proc/$$是当前bash的文件
$$ BASHPID 是当前bash的pid
/proc/$$/fd 是当前程序的所有的文件描述符,也可以使用lsof -op $$看见当前进程的文件描述符的细节,包括偏移量、指针等等。

关于标准输入和标准输出的重定向

bash符号中< 表示输入 > 表示输出

有的时候可以切换文件描述符来切换标准输入和标准输出目标。

  • 例1:ls 命令的标准输出
    ls ./ 1> ls.out   
    # 将ls的标准输出1重定向到ls.out,此时的原本打印的结果全部输出到ls.out文件中了。
    
  • 例2:cat 命令的标准输入和输出
    cat 0< 00xx.txt 1> cat.out
    # 表示从标准输入(文件描述=0)来源为00xx.txt文件,标准输出(文件描述符=1)目标为cat.out
    # 执行后,00xx.txt文件中的内容被拷贝到cat.out中了
    
  • 例3:read命令
    read a  
    # 表示从以键盘为标准输入源。将键盘输入的数据赋值给变量a,按回车结束输入。
    read a  0< cat.out 
    # 表示强制切换标准输入源为文件输入,文件为cat.out,在cat.out中有换行就会结束输入
    # 执行后,cat.out文件中的第一行会被打印到bash窗口中
    
  • 例4: 上方的进阶< >的多重操作
    # xxoo目录不存在,在bash窗口中执行
    ls ./ /xxoo 1> ls03.out 2> ls02.out
    # 表示把输入的目录信息放到ls02.out文件,把输出错误流信息放到ls02.out
    ls ./ /xxoo 2> 1 1> ls04.out
    ls ./ /xxoo 2>& 1 1> ls04.out
    ls ./ /xxoo 1> ls04.out 2>& 1
    # 注意3个命令的区别
    # 2> 1 表示错误输出到文件名为1的文件中
    # 2>& 1 表示标准错误输出目标切换为标准输出所指向的目标
    # 2>& 1 1> ls04.out 表示先把标准错误输出到标准输出流指向的文件描述符,这时错误是输出到屏幕,然后1> ls04.out 表示把标准输出目标指向ls04.out文件
    # 1> ls04.out 2>& 1 表示先把标准输出目标切换到ls04.out文件,然后把标准错误输出目标指向标准输出所指向的目标,即ls04.out。所以错误输出和标准输出都会在ls04.out文件中
    

管道 |

head -2 test.txt  
# 输出文件的前2行
tail -3 test.txt 
# 输出到文件的最后3行
head -8 test.txt | tail -1
# 输出第八行,原理:先取前面的8行,然后用管道取最后1行

Linux父进程子进程

  • 开启子进程bash
    在bash命令中输入/bin/bash 就打开了一个新的bash进程,要退出的话exit; 就退出到父进程的bash;
  • 关于变量
    父进程定义的变量x,不能在子进程取到,除非使用export
    这也是为什么在/etc/profile/配环境变量的时候,要添加export的原因
    在这里插入图片描述

关于管道和代码块

  • 特别注意的管道的坑
    • 变量和子进程在管道中
      a=1
      echo $a
      # 输出 1
      { a=9 ; echo "1222"; }  | cat 
      # 输出 1222
      echo $a 
      # 输出 1
      # 理由: bash是解释执行的,系统看到“|” 就会创建一个子进程来执行命令,这个时候在子进程中a被赋值为9,但是原来的父进程中都没被变过
      
    • 管道和$ 、 、 BASHPID
    echo $$ | cat
    echo $BASHPID |cat
    # 上面这2个的执行,最后打印的进程号是不一样的,$$打印的是当前父进程的,¥BASHPID打印的是管道拉起的子进程的。因为$$的优先级高于 | 。所以echo $$ | cat 相当于 echo进程ID字符串 | cat
    
  • 多子进程的高级篇(1个图基本能理解)
    在这里插入图片描述

参考文件:
https://blog.csdn.net/u010889616/article/details/47868887
https://hanquan.blog.csdn.net/article/details/106744323
http://www.ruanyifeng.com/blog/2011/12/inode.html
https://www.cnblogs.com/duzhaoqi/p/7210052.html

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值