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