上面一篇文章(https://www.cnblogs.com/ckh2014/p/7224851.html)介绍了硬盘的基本结构,那么一块磁盘如何应用取存储数据呢?
它的步骤是这样的:
识别硬盘 --> 磁盘分区 --> 分区格式化 --> 挂载 --> 写入/存储数据
识别硬盘
这是linux内核的事情,在之前博文CentOS启动流程(https://www.cnblogs.com/ckh2014/p/9571066.html)中介绍系统启动时内核会初始化加载硬盘的驱动程序,所以我们登录系统后fdisk会查看到硬盘的相关信息。
磁盘分区
分区模式有MBR和GPT两种模式:
MBR:Master Boot Record,主引导记录模式,也称为MSDOS模式
MBR模式是在硬盘的第一个扇区(512bytes)中,将前446bytes用来记录引导程序,后面64bytes用来记录分区表(partition table),最后2bytes为结束符号。
分区表(64bytes)中每16个bytes标识一个分区,所以只能标识四个主分区,存储容量最大约2.2TB
GPT:GUID Partition table,全局唯一标识磁盘分区表
GPT模式打破了MBR的限制,可以设置多达128个分区,分区的大小根据操作系统的不同有所变化,但是都突破了2T空间的限制。支持高达 18EB (1EB=1024PB,1PB=1024TB) 的卷大小,允许将主磁盘分区表和备份磁盘分区表用于冗余,还支持唯一的磁盘和分区 ID (GUID)。
与 MBR 分区的磁盘不同,GPT的分区信息是在分区中,而不象MBR一样在主引导扇区。为保护GPT不受MBR类磁盘管理软件的危害,GPT在主引导扇区建立了一个保护分区 (Protective MBR)的MBR分区表,这种分区的类型标识为0xEE,这个保护分区的大小在Windows下为128MB,Mac OS下为200MB,在Window磁盘管理器里名为GPT保护分区,可让MBR类磁盘管理软件把GPT看成一个未知格式的分区,而不是错误地当成一个未分区的磁盘。
在MBR硬盘中,分区信息直接存储于主引导记录(MBR)中(主引导记录中还存储着系统的引导程序)。但在GPT硬盘中,分区表的位置信息储存在GPT头中。但出于兼容性考虑,硬盘的第一个扇区仍然用作MBR,之后才是GPT头。
GPT使用34个LBA,最后33个用来做分区信息备份。
逻辑区块位址中,LBA0(MBR相容区块)中446bytes用来存放开机管理程式(boot loader),原本用来存放分区表信息的地方只存放一个特殊标志,用来表示磁盘为GPT分区格式。
LBA1(GPT表头记录)中记录了分区表的位置和大小,同时记录了备份用的GPT分区存放位置。
LBA2-33(实际记录分区信息处),每个LBA默认有4个分区,总共512bytes,每个分区信息用到128bytes,这之中除了识别码和相关记录外,会有64bits来记录开始/结束的磁区号码。
分区格式化
硬盘分区后,如果想要实现对文件的快速存取以及访问,还要依赖文件系统,需要在分区上面创建文件系统。文件系统是一个软件,此处以ext文件系统为。文件储存在硬盘上,硬盘的最小存储单位叫扇区(Sector)。每个扇区储存512字节(相当于0.5KB)。操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个块(block)。这种由多个扇区组成的块是文件存取的最小单位,块的大小,最常见的是4KB,即连续八个sector组成一个block,文件数据都储存在块中,而每个数据文件存在哪些块中,从哪个块开始,到哪个块结束,那么很明显,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存元信息的区域叫做inode,中文译名为”索引节点”。
每一个inode有一个全局唯一的ID(这里的全局指的是某个分区下,某分区下不同文件的inode号不可能相同,不同分区的inode号有可能相同),每个inode中存放了对应文件的inode号,文件类型、属主属组、权限、大小、时间、以及文件数据存储在哪些磁盘块等信息(元数据中没有当前文件的文件名,当前文件的文件名存放在父目录的数据块中),如果一个文件过大,那么他需要的块就会很多,inode中存储的对应块信息就会很多,但是如果文件很大,成千上万的块信息的存储空间就会很大,它可以利用指针,指向另一个更大的空间,存储这些块信息。
inode详细结构示意图如下:
文件系统格式化,也就是将分区分成多个块组,有些是超级块,后面的块组中有些会有超级块的备份。块组的详细示意图如下:
超级块包含信息要比普通块组多一些,下面详细介绍下超级块包含的信息。
超级块详细信息:
1.引导块(boot sector):
如果没有引导代码,这两个扇区内容为空,全部用0填充
2. 超级块(Super Block):
这个块是用来保存该磁盘上所有的块组的信息,比如每一个块组从哪一个块开始,到哪一个块结束等信息,
还包括各种meta信息, 块大小, 每个块组包含块数, 总块数, 第一个块前保留块数, inode节点数, 每个块组的inode节点数, 卷名, 最后挂载时间, 挂载路径,
文件系统是否干净, 是否要调用一致性检查标识,空间inode节点和空闲块的记录信息等, 在分配inode节点和新块的时候使用。
因此一旦超级块坏掉了,其他所有块组的信息都无法读取了,所以超级块有很多备份,存放于其他块组中,但使用时只使用块组0中的超级块,一旦坏了就从其他块组中找一个加以恢复。
3. 块组描述表(Group Description Table)
块组描述符表中包含了所有块组的描述信息,它存放的是块位图(Block Bitmap)和 inode位图(Inode Bitmap),即哪些块是已用的,哪些块是空闲的;哪些inode是已用的,哪些inode是空闲的,这样 的位图比整个磁盘的位图要小得多,故扫描效率会提高很多。而通过Inode表(Inode Table)就可以找到相应的磁盘块(Data Blocks)。组描述符和超级块在每个块组中都有一个备份, 但是激活了稀疏超级块特 征的情况除外。稀疏超级块即不是在所有块组中都存储超级块和组描述符的备份, 默认该策略被激活, 其策略类似当块组号是3,5,7的幂的块组才存副本。
4.数据块位图
每个块对应一个bit, 只包含了本块组的信息。而文件系统创建的时候, 默认每个块组包含的块数跟每个块包含的bit数是一样的。
而该位图表的块的起始位置会在组描述符表中指定, 大小则为块组中包含块数个bit。
5.inode位图
跟块位图类似, 通常情况他也只占用一个块。通常一个块组中的inode节点数会小于block数量。
6.inode节点表
每个inode都占用128位的一个描述信息, 起始位置在组描述符表中表示。
7.数据区
这就是真正存储数据的区域。
inode详细信息:
上面提到inode表是存放每个文件元数据信息的,每一条元数据用一个inode表示,inode结构示意图如下
这里介绍下块指针的概念
直接块指针:
12个直接指针,直接指向存储数据的块。假设一个block块大小为4k,那么直接块指针指向的块的大小总和为4k*12 = 48k,也就是说如果一个文件的数据大小不超过48k,
使用直接块指针指向的块就能满足数据存储的需求。 间接块指针:
如果一个文件的大小大于48k,'直接块指针'指向的块大小无法满足存储需求,这时候就会用到‘间接块指针’,‘间接块指针’指向一个数据块,我们暂且称这个被‘间接块指针’指向的块为‘间接块’,
‘间接块’中不存放数据,而是存放指针,这些存放在‘间接块’中的指针再指向真正存放数据的块,我们还按照上述假设,每个块4k大小,
假设每个指针占用4个字节的空间,那么一个4k的块能存放1024个指针,也就是间接块中能存放1024个指针,这些指针每个再指向一个4k的块,
那么所有能指向的块的大小为4k * 1024 = 4M ,也就是说能够存放小于4M大小的文件。 双重间接块指针:
同理,如果文件大于4M,‘间接块指针’能够指向的所有块的大小总和无法满足要求,这时候就会用到 ‘双重间接块指针’ ,‘双重间接块指针’的原理跟‘间接块指针’的原理相同,
只是又多间接了一层,‘双重间接块指针’先指向一个‘间接块’,这个间接块里存放了1024个指针,‘间接块’里的每个指针再指向一个‘二重间接块’,因为每个块的大小都是4k,
所以每一个‘二重间接块’又能分化出1024个指针,这些来自‘二重间接块’的指针再指向真正存放数据的块。所以,总共算下来,能够存放1024 * 1024 * 4k = 4G 的数据。 三重间接块指针:
三重间接块指针的原理同上,以此类推。三级指针可以储存文件数据大小为**1024*4=4TB
Linux中cp、rm、mv 、ln对inode的影响:
cp命令
分配一个未被使用的inode号,在inode表中添加一个新项目,(注意:如果是cp文件到已经存在的文件,则inode号采用被覆盖之前的目标文件的inode号)
在目录中新建一个目录项,并指向步骤1中的inode
把数据复制到block中
rm命令
减少链接数量,如果链接数为0,释放inode(inode号被重新使用);
如果inode被释放,则数据块放到可用空间列表中;
删除目录中的目录项
mv命令
1. 如果mv命令的目标文件和源文件所在的文件系统相同:
使用新文件名建立目录项;
删除带有原来文件名的目录项;
注意:该操作对inode表没有影响(除时间戳),对数据的位置也没有影响,不移动任何数据。(即使是mv到一个已经存在的目标文件,新目录指向源文件inode,会先删除目标文件的目录项)
2. 如果目标文件和源文件所在的文件系统不相同,就是cp和rm;
ln命令
符号(软)链接:
符号链接的内容是它引用文件的名称,可以是任意文件或目录,也可以链接不同文件系统的文件,甚至可以链接不存在的文件,这就产生一般称为断裂的问题,还可以不断的循环链接源文,但是其大小为指向的路径字符串的长度;不增加或减少目标文件inode 的引用计数。
使用ln -s source_file softlink_file (注意:源文件(source_file)的路径是相对路径(也可以是绝对路径,通常使用的是相对路径),一定是相对于软链接文件的路径,而非相对于当前工作目录的路径)创建符号链接,在对符号链接进行读写操作的时候,系统会自动把该操作转换为对源文件的操作,但是删除链接文件时,系统仅仅删除符号链接文件,而不是删除源文件本身。
硬链接:
不允许给目录创建硬链接,创建硬链接会增加inode的引用计数(不能跨驱动或分区创建硬链接),硬链接件对应于同一文件系统上的一个物理文件,硬链接节点编号是相同的,创建硬链接链接数递增,删除文件时:rm命令递减计数的链接,文件如果存在,至少有一个链接数,当链接数为0时,该文件被删除。
使用ln existfile newfile 命令创建硬链接
硬链接于软连接的区别:
1、本质不同:硬链接是指向同一个文件,软链接指向的不是同一个文件
2、删除时:硬链接不受影响,软链接失效
3、创建链接时:创建硬链接链接数加1,创建软链接连接数不变
4、是否可以跨分区:硬链接不可以跨分区,软链接可以跨分区
5、目录是否可以创建链接:硬链接不可以对目录创建,软链接可以对目录创建
6、硬链接的inode号相同,软链接inode号不同
挂载
Linux中的根目录以外的文件要想被访问,需要将其“关联”到根目录下的某个目录来实现,这种关联操作就是“挂载”,这个目录就是“挂载点”,解除关联关系的过程称之为“卸载”。
注意:“挂载点”的目录需要以下几个要求:
(1)目录事先存在,可以用mkdir命令新建目录;
(2)挂载点目录不可被其他进程使用到;
(3)挂载点下原有文件将被隐藏。
最后介绍下磁盘管理和文件系统的相关命令:
1.分区相关的命令
对硬盘分区:只能管理15个分区
# fdisk -l //显示当前系统上所有的磁盘分区情况
# fdisk DEVICE: //对某个设备分区,比如硬盘sdb
自命令:管理功能
p: print,显示已有分区
n: new,创建一个分区
d: delete, 删除一个分区
t: 调整分区id
l: 列表显示分区id
w: write: 写入磁盘并退出
q:quit, 放弃更新并退出
通过上面的命令创建好分区后,需要重新读取硬盘分区表
有三种命令可以实现:
1. partx -a /dev/device
-n M:N 读取哪几个分区
2. kpartx -a /dev/device
-f: force
3. partprobe [/dev/device]
查看内核是否已经识别新分区
cat /proc/partitions
有些时候重新读取新分区后,旧的已经删除的分区还在,这时可通过 partprobe重新读取分区信息,会删除旧的记录。
2、文件系统格式化相关命令
mkfs命令:
# mkfs.FS_TYPE /dev/DEVICE
# mkfs -t FS_TYPE /dev/DEVICE
-l 'LABEL': //设定卷标
FS_TYPE文件系统类型有 ext2、ext3、ext4、xfs、btrfs、vfat
mke2fs: ext系列文件系统专用管理工具
-t {ext2 | ext3 | ext4} // 文件系统类型
-b {1024 | 2048 | 4096} // 块大小
-L 'LABEL' // 设定卷标
-j: 相当于 -t ext3 // 开启日志功能
-i #: // 为数据空间中每多少个字节创建inode
-N # : // 为数据空间创建多少个inode
-m # : // 为管理人员预留的空间占用的百分比,默认为百分之5
-O FEATURE[...]: // 启用指定特性
-O ^FEATURE: // 关闭指定特性
mkswap: 创建交换分区
-L 'LABEL'
blkid: 块设备属性信息查看
-U UUID: 根据指定的UUID来查找对应的设备
-L LABEL: 根据指定的LABEL来查找对应的设备
e2label: 管理ext系列文件系统的LABEL
#e2label DEVICE [LABEL]
tune2fs: 重新设置ext系列文件系统可调整参数的值
-l: 查看指定文件系统超级块信息
-L 'LABEL':修改LABEL
-m #: 修改预留给管理员的空间百分比
-j: ext2升级为ext3
-O: 文件系统属性启用或禁用
-o: 调整文件系统的默认挂载选项
-U UUID:修改UUID号
dumpe2fs: // 查看卷组信息
-h: 查看超级块信息
文件系统检测:
fsck.FS_TYPE
fsck -t FS_TYPE
-a: 自动修复错误
-r: 交互式修复错误 e2fsck: ext系列文件专用的检测修复工具
-y: 自动回答yes
-f: 强制修复
3. 挂载与卸载相关命令
挂载命令:mount
卸载命令: umount
挂载命令:mount
mount命令: 通过查看/etc/mtab文件显示当前系统已挂载的所有设备
查看内核追踪到的已挂载的所有设备: cat /proc/mounts
mount [-fnrsvw] [-t vfstype] [-o options] device dir //注意: 挂载点下原有文件在挂载完成后会被临时隐藏
device:指明要挂载的设备
(1)设备文件:例如:/dev/sda5
(2)卷标:-L 'LABEL' 例如 -L "MYDATA"
(3) UUID,-U 'UUID' 例如 -U 'ABD12ds12'
(4)伪文件系统名称:proc,sysfs,devtmpfs,configfs
dir:挂载点
事先存在:建议使用空目录:
进程正在使用中的设备无法被卸载(/application/htdocs)
常用选项:
-t vfstype: 指定要挂载的设备上的文件系统类型
-r: readonly,只读挂载
-w: read and write,读写挂载
-n: 不更新/etc/mtab
-a: 自动挂载所有支持自动挂载的设备:(定义在了 /etc/fstab文件中,且挂载选项中有“自动挂载”功能)
-U 'UUID': 以UUID指定要挂载的设备
-B,--bind: 绑定目录到另一个目录上
-o options: (挂载文件系统的选项)
async: 异步模式:
sync: 同步模式:
atime/noatime:
diratime/nodiratime:目录的访问时间戳
auto/noauto: 是否支持自动挂载
exec/noexec:是否支持将文件系统上应用程序运行为进程
dev/nodev: 是否支持在此文件系统上使用设备文件
suid/nosuid:
remount:重新挂载
ro:
rw:
user/nouser:是否运行普通用户挂载此设备
acl: 启用此文件系统上的acl功能
注意:上述选项可多个同时使用,彼此使用逗号分隔
默认挂载选项:defaults(rw,suid,dev,exec,auto,nouser,and async)
卸载命令:umount DEVICE
挂载交换分区:
启用:swapon
swapon [OPTION]...[DEVICE]
-a: 激活所有的交换分区
-p: PRIORITY:指定优先级
禁用: swapoff [OPTION]...[DEVICE]
查看正在访问指定文件系统的进程:
fuser -v MOUNT_POINT
fuer -km MOUNT_POINT 终止所有正在访问指定文件系统的进程
内存空闲使用状态:
free [OPTION]
-m: 以MB为单位
-g: 以GB为单位
文件系统空间占用等信息的查看工具:
df [OPTION]
-h: human-readable
-l: inodes instead of blocks
-P: 以Posix兼容的格式输出
查看某目录总体空间占用状态:
du [OPTIONS] DIR
-h: human-readable
-s: summary
要实现开机自动挂载,则要修改挂载的配置文件: /etc/fstab
每行定义一个要挂载的文件系统:
1.要挂载的设备或伪文件系统: 设备文件、LABEL(LABEL="")、UUID(UUID="")、伪文件系统名称(proc、sysfs)
2.挂载点
3.文件系统类型
4.挂载选项: defaults
5.转储频率:
0:不做备份
1:每天转储
2:每隔一天转储
6.自检次序:
0:不自检
1:首先自检,一般只有rootfs才用1
四、实例
1.创建一个20G的文件系统,块大小为2048,文件系统为ext4,卷标为TEST,要求此分区开机后自动挂载至/testing目录,且默认有acl挂载选项
a. 创建20G的分区
b. mke2fs -t ext4 -b 2048 -L TEST /dev/DEVICE
c. 编辑/etc/fstab: 增加一行:LABEL="TEST" /testing ext4 defaults,acl 0 0
2.创建一个5G的文件系统,卷标HUGE,要求此分区开机自动挂载至/mogdata目录,文件系统类型为ext3
a. 创建5G分区
b. mkfs.ext3 -L HUGE /dev/DEVICE
c. 修改配置文件/etc/fstab: 增加一行:LABEL='HUGE' /mogdata ext3 defaults 0 0
3.写一个脚本:
(1)列出当前系统识别到的所有磁盘设备
# fdisk -l | grep -o "^Disk /dev/[sh]d[a-z]"
(2)如磁盘数量为1,则显示其空间使用信息,如果大于1则显示最后一条
#!/bin/bash
#
disknum=$(fdisk -l | grep -o "^Disk /dev/[sh]d[a-z]" | wc -l)
if [ $disknum -eq 1 ]: then
fdisk -l `fdisk -l | grep -o "^Disk /dev/[sh]d[a-z]" | cut -d' ' -f2`
else
fdisk -l `fdisk -l | grep -o "^Disk /dev/[sh]d[a-z]" | tail -1 | cut -d' ' -f2 `
fi