# 磁盘管理
# 磁盘分区
- 磁盘设备中默认不存在磁盘分区、分区表。有需求的话,可以划分多个磁盘分区。
- 磁盘分区之间相互独立,可采用不同的文件系统。
- 磁盘分区之间相互隔离,一个分区发生故障时不会影响其它分区。
- 给 Linux 主机添加磁盘的步骤:
- 给计算机接入一个磁盘设备,可选划分磁盘分区。
- 用 mkfs 命令将磁盘或磁盘分区格式化为某种文件系统。
- 用 mount 命令将文件系统挂载到主机的某个目录,供用户读写。
# fdisk
fdisk
-l # 显示系统所有的磁盘及其磁盘分区
<disk> # 打开一个终端,对指定磁盘进行配置
- 进入 fdisk 终端之后,可输入以下命令:
p # 显示已有的磁盘分区 n # 创建一个分区 p # 接着输入 p 会创建主分区,输入 e 会创建扩展分区 2 # 然后设置分区编号 Enter # 设置起始扇区(按回车会使用默认值) +20G # 设置结束扇区(可指定扇区大小) d # 删除一个分区 w # 将设置内容保存
- 例:
[root@CentOS ~]# fdisk -l Disk /dev/vda: 53.7 GB, 53687091200 bytes, 104857600 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk label type: dos Disk identifier: 0x0009ac89 Device Boot Start End Blocks Id System /dev/vda1 * 2048 104857566 52427759+ 83 Linux
- fdisk 命令显示的 1GB 等于 1000MB ,而 df 命令采用 1024 进制。
Disk label type: dos
表示该磁盘采用 MS-DOS 类型的分区表,即 DPT 分区表。存在一个磁盘分区 /dev/vda1 。
# Swap 分区
:交换分区,一种特殊的磁盘分区,用于临时存储内存中的数据。类似于 Windows 系统的磁盘虚拟内存。
- Linux 会运行一个内核线程 kswapd0 ,负责管理 Swap 分区。
- 当物理内存 page 数不足时,会将某些暂时不用的 page 数据移到 Swap 分区中存储,该过程称为 swap out 。
- 当 CPU 在内存中找不到这些数据而报出主缺页异常时,会将数据从 Swap 分区载入内存,该过程称为 swap in 。
- 启用 Swap 分区可以缓解系统内存不足的问题,避免进程被 OOM 杀死。
- 但是 CPU 从磁盘 Swap 分区读写数据,比从物理内存读写数据慢得多,导致磁盘 IO 量变多、CPU 使用率增高(因为 iowait )。
- 物理内存严重不足时,会频繁进行 swap out 和 swap in ,可能导致 CPU 使用率达到 100% 。
- 因此:
- 如果系统内存长期不足,应该增加物理内存。
- 如果系统内存长期足够,应该禁用 Swap 。
- 如果系统内存偶尔不足,可以考虑启用 Swap ,并调整内核参数 vm.swappiness 。
# mkswap
mkswap
[device] # 将一个 device 格式化成 swap 分区。device 可以是磁盘设备、磁盘分区或文件
- 例:
[root@CentOS ~]# dd if=/dev/zero of=/swapfile bs=1M count=1024 1024+0 records in 1024+0 records out 1073741824 bytes (1.1 GB) copied, 2.73547 s, 393 MB/s [root@CentOS ~]# mkswap /swapfile Setting up swapspace version 1, size = 1048572 KiB no label, UUID=32de6480-099b-4ff1-b4f2-ea44b75ae430 [root@CentOS ~]# chmod 600 /swapfile [root@CentOS ~]# swapon /swapfile
- 编辑 /etc/fstab 文件,可以让系统开机时自动挂载 swap 分区:
/swapfile swap swap defaults 0 0
# swapon
swapon
[device] # 启用指定的 swap 分区
-a # 启用 /etc/fstab 中定义的所有 swap 分区
-s # 显示已启用的所有 swap 分区
# swapoff
swapoff
[device] # 停用指定的 swap 分区
-a # 停用所有 swap 分区
# LVM
:逻辑卷管理器(Logical Volume Manager ,LVM),用于创建逻辑卷。
- 安装:
yum install lvm2
- 目前常用的是 LVM v2 版本,向下兼容 v1 版本。
- 优点:
- 可以创建跨越多个存储设备的逻辑卷。
- 逻辑卷在创建之后可以改变容量,而传统的磁盘分区不支持。
- 使用步骤:
- 将单个块设备(比如磁盘、磁盘分区)格式化为物理卷(Physical Volume ,PV)。
- 默认在物理卷开头的第二个扇区中存储 LVM Label 标签,包括 UUID、容量等信息。
- 物理卷划以物理盘区(Physical Extents ,PE)为单位划分存储空间,默认为 4MB 。
- 将多个物理卷组成一个卷组(Volume Group ,VG),提供一个存储资源池。
- 在卷组中创建逻辑卷(Logical Volume ,LV)。
- 执行 fdisk -l 可看到逻辑卷,可格式化为文件系统,挂载后供用户读写。
- 将单个块设备(比如磁盘、磁盘分区)格式化为物理卷(Physical Volume ,PV)。
- 逻辑卷的分类:
- 线性卷(linear volume):将多个 pv 串联,从实际地址映射到逻辑卷的连续地址。
- 条状卷(striped volume):将多个 pv 并联,数据轮流写入各个 pv 。
- 镜像卷(mirrored volume):数据只写入一个 pv ,其它 pv 会同步拷贝,作为备份。
- 快照卷(snapshot volume):对相同 VG 中的另一个逻辑卷进行快照备份。
- 采用写时复制(Copy On Write ,COW):快照卷内先保存指向数据 inode 的硬链接,当数据变化时才拷贝到快照卷。因此不会中断服务,支持增量备份。
# pv
lvmdiskscan # 扫描本机的块设备,找出可格式化为 pv 的
pvcreate <device>... # 将块设备格式化为 pv
pvscan # 列出本机已有的 pv
pvremove <pv>... # 删除 pv
- 这会擦除块设备中的 LVM label 。
# vg
vgcreate <vg> <pv>... # 创建一个 vg ,包含指定的 pv
vgdisplay # 列出已有的 vg
vgrename <vg> <vg> # 重命名 vg
vgchange <vg> # 修改 vg 的配置
-a <y|n> # --active ,是否启用
vgextend <vg> <pv>... # 给 vg 添加 pv
vgreduce <vg> <pv>... # 从 vg 移除 pv
- vg 只包含一个 pv 时,不能移除 pv ,只能删除整个 vg 。
vgremove <vg> # 删除 vg
# lv
lvcreate <vg> # 在 vg 中创建一个 lv ,默认为 linear 类型
-L 50MB # 指定容量,单位可以为 KB、MB、GB 等
-i <n> # 创建 striped 类型的 lv ,并指定 pv 的数量
-v # 显示详细的日志
-vv # 显示更详细的日志
- 例:
[root@CentOS ~]# lvcreate vg1 -L 50MB Rounding up size to full physical extent 52.00 MiB # 自动将容量调整为 PE 的整数倍 Logical volume "lvol0" created.
- 创建的 lv 会自动命名,编号从 0 递增。例如 lvol0 对应的设备路径为 /dev/vg1/lvol0 。
lvdisplay [lv]... # 列出已有的 lv
lvrename <lv> <lv> # 重命名 lv
lvchange <lv> # 修改 lv 的配置
-a <y|n> # --active ,是否启用
lvextend <lv> # 增加 lv 的容量
-L 10MB # 增加到指定大小
-L +10MB # 增加指定大小
lvreduce <lv> # 减少 lv 的容量
-L 10MB # 减少到指定大小
-L -10MB # 减少指定大小
lvremove <lv> # 删除 lv
# 文件系统
# block
:文件系统读写数据时的最小单位。
- 即使一个文件只包含 1 byte 数据,存储时也要占用 1 个 block 。
- 不同磁盘分区的文件系统可以设置不同的 block size 。
- 例如,ext3 文件系统的 block size 默认为 4096 Bytes 。
- 碎片整理:将磁盘中存储同一个文件的 block 整理到一起,提高读写速度。
- block group :磁盘中的所有 block 通常被分成几组。
- super block :用于存储整个文件系统的信息,包括 block、inode 的数量、使用情况等。
- 一般位于第一个 block group 中。
# inode
:索引节点(index node),用于记录一个文件在磁盘中的信息。
新建一个文件保存到磁盘时,系统会给它分配 n 个 block 的磁盘空间用于存储文件,再分配一个 inode 数据结构用于记录文件信息。
每个 inode 有一个数字编号作为唯一标识。
- inode 里会记录文件的元数据。主要是通过 stat 命令查看到的那些信息,比如文件类型、访问权限、指向该 inode 的硬链接数。
- inode 里会记录文件内容存储在磁盘的哪些 block 中。
- inode 里并不会记录文件名,文件名记录在目录文件中。
- 因此移动、重命名文件时,不会改变文件本身(最后修改时间不变),而会改变所属的目录文件。
每个文件系统会在磁盘中存储一张索引表(inode table),记录所有已使用的 inode 的内容。
- 当指向一个 inode 的所有文件都被删除时,系统就会认为该 inode 及其 block 不再使用,可以释放,供新创建的文件使用。
例:
- 假设 Linux 系统要读取路径为 path 的某个文件的内容,需要先查询到该文件路径对应的 inode 编号为 xxx 。然后到磁盘的 inode table 中,找到该 inode 的内容,知道该文件存储在哪些 block 。最后读取这些 block 。
- 不能直接根据一个 inode 反查出对应的文件路径,遍历大量文件才能找出来。
- 如果在同一个文件系统内用
mv f1 f2
的方式移动文件,则新的文件依然使用之前的 inode ,不会在磁盘中重新拷贝一份数据,因此几乎不消耗时间。 - 在同一个文件系统内用
echo Hello > f1
的方式修改文件时, inode 编号不会变化。用vim f1
的方式修改文件时,默认会先创建一个备份文件进行修改,然后替换原文件,因此 inode 编号会变化。
例:在 Python 中编辑文件
>>> f = open('f1', 'w+') # 打开文件,这会自动分配一个文件描述符,并根据文件路径查询到 inode ,读取文件的内容 >>> f.write('Hello\n') # 给文件写入内容,即使覆盖原有内容,即使写入很长的内容,也不会改变文件的 inode 6 >>> f.name # 查看文件名 'f1' >>> f.close() # 关闭文件,即关闭文件描述符
- 当 Python 编辑文件时,可以另开一个终端,执行
mv f1 f2
重命名文件。在 Windows 上则不允许修改已打开的文件。- 此时 Python 写入文件的内容会根据原 inode 写入磁盘,关联到 f2 。
- 不过在 Python 中执行
f.name
,依然会返回最初的文件名 f1 ,不会根据 inode 反查出当前的文件名。
- 当 Python 编辑文件时,可以另开一个终端,执行
# 分类
存储在磁盘中的文件系统举例:
- ext2 :扩展文件系统(extended file system)的第二代,于 1993 年开始被 Linux 内核使用。
- ext3 :兼容 ext2 。能记录日志,在异常关机后能根据日志快速恢复关机时没有保存的数据,而不必扫描整个磁盘。
- ext4 :兼容 ext3 。支持更大的文件系统和文件,支持无限数量的子目录。于 2008 年发布,是 RHEL6 的默认文件系统。
- xfs :一个比 ext4 更优秀的日志文件系统。于 2000 年发布,是 RHEL7 的默认文件系统。
- btrfs
- FAT :1977 年由比尔盖茨发布。目前最常见的一种 FAT 系统是 FAT32 ,它限制了单个文件最大为 4G 。
- NTFS :1993 年由微软公司发布,适用于 Windows2000 及以后的 Windows 版本。单个文件没有大小限制。
存储在 flash 中的文件系统如下,常用于嵌入式系统:
- jffs2
- yaffs
- cramfs :一个只读的压缩文件系统。文件被压缩后节约了大量存储空间,要载入 RAM 时才解压缩。
- romfs
存储在 RAM 中的文件系统如下,读写速度很快:
- rootfs :根文件系统。
- 它的挂载点是它自己的 / 目录。
- Linux 系统启动时,内核会创建一个只读(read-only)模式的 rootfs 。启动完成之后,可以将 rootfs 切换成读写(read-write)模式,或者在它上面挂载其它文件系统。
- 比如将一个 ext4 文件系统挂载到 rootfs 的 / 目录,使得 rootfs 对用户不可见。
- ramdisk :一种较旧的内存文件系统,已被 tmpfs 取代。
- tmpfs :一种临时文件系统,将文件存储在 RAM 内存中,因此重启主机时这些文件会丢失。
- Linux 系统默认创建了几个 tmpfs 文件系统并分别挂载到 /dev/shm、/run、/sys/fs/cgroup、/run/user/0 ,在这些目录下写入的文件会存储到 RAM 的共享内存(shared memory)中,也可能被存储到 Swap 分区。
- 挂载时,默认限制了每个 tmpfs 文件系统的体积为 RAM 内存的一半。可以手动修改,例如:
mount -o remount,rw,nosuid,nodev,size=4G /dev/shm
- proc :挂载到 /proc 目录。
- sysfs :挂载到 /sys 目录。
- devtmpfs :挂载到 /dev 目录。
- Linux 内核 2.6 版本开始使用一个用户态的守护进程 systemd-udevd 管理 /dev 目录下的设备,取代了传统的 devfsd 进程。
- rootfs :根文件系统。
存储在网络中的文件系统举例:
- nfs :网络文件系统(Network File System),可以让不同主机通过网络共享文件。
FUSE(Filesystem in USErspace):类 Unix 系统内核提供的一种 API ,允许用户态进程创建、挂载文件系统,不必进入内核态。
ext2/ext3/ext4 文件系统通常会浪费 7% 的磁盘空间不能使用,不如 xfs 文件系统。
- ext2/ext3/ext4 文件系统在创建时,默认会为磁盘中每 16KB 空间创建一个 inode 数据结构, inode size 为 256 bytes 。因此磁盘空间的
256/(16*1024)≈1.6%
用于存储 inode ,剩下的空间才是 df 命令显示的文件系统 Size 。- 这样事先创建了所有 inode ,如果用户只创建少量文件,则大量 inode 未被使用,因为存储 inode 数据而浪费了磁盘空间。如果用户创建大量文件,用完了 inode ,则即使磁盘空间未写满,也不能新建文件。
- xfs 文件系统会在新建文件时动态创建 inode ,因此 inode 总量不是固定的。
- 另外,ext2/ext3/ext4 文件系统的 inode 编号占 32bit ,因此最多可创建 2^32 个 inode 。而 xfs 文件系统的 inode 编号占 64bit 。
- ext2/ext3/ext4 文件系统在创建时,默认会保留
reserved-blocks-percentage=5
百分比的磁盘空间,不允许非 root 进程写入。当普通的磁盘空间写满时,让 root 进程写入 reserved-blocks 来继续工作。- df 命令显示的 Used 磁盘已用空间,包括普通磁盘的已用空间、 reserved-blocks 的已用空间。而
Avail = Size - Used
,Use% = 1 - Avail/Size
。 - 例如下例,Use% 达到 100% ,但 Used 小于 Size ,说明 reserved-blocks 尚未写满。
[root@CentOS ~]# df -hT Filesystem Type Size Used Avail Use% Mounted on /dev/vda1 ext4 50G 48G 0 100% /
- 如果磁盘总体积较大,比如 1TB ,则容易因为平时不用 reserved-blocks 而浪费很多磁盘空间,可以降低其百分比,或改用 xfs 文件系统。
- xfs 文件系统会保留大概 0.03% 的磁盘空间,用于存储 xfs 文件系统本身的日志数据,不能被用户访问。
- df 命令显示的 Used 磁盘已用空间,包括普通磁盘的已用空间、 reserved-blocks 的已用空间。而
- ext2/ext3/ext4 文件系统在创建时,默认会为磁盘中每 16KB 空间创建一个 inode 数据结构, inode size 为 256 bytes 。因此磁盘空间的
# mkfs
mkfs <device> # 将一个 device 格式化成某种文件系统。device 可以是磁盘设备、磁盘分区或文件
-t <fs> # 指定文件系统的类型,默认为 ext2
- 例:
mkfs -t xfs /dev/vdb
- 如果 device 已存在文件系统,则会报错:
/dev/vdb appears to contain an existing filesystem
- 如果 device 已被挂载,则会报错:
/dev/vdb contains a mounted filesystem
- 如果 device 已存在文件系统,则会报错:
- 创建文件系统之后,可以查看其详细的配置信息:
tune2fs <device> -l # 显示一个 ext2/ext3/ext4 文件系统的配置 -m 5 # 修改 reserved-blocks-percentage
xfs_info <device> # 显示一个 xfs 文件系统的配置
# mount
mount # 显示已挂载的所有目录
<fs> <dir> # 将一个文件系统挂载到指定目录
-t xfs # 指定文件系统的类型,默认会自动识别
-r # 挂载为只读模式
-L "vda2" # 添加卷标
-o options # 添加一些挂载选项,以逗号分隔
-a # 挂载 /etc/fstab 中定义的所有文件系统
例:
mount /dev/vdb /data
- 挂载点必须已存在,否则会报错:
mount point /data does not exist
- 如果文件系统已挂载,或挂载点正在被某进程使用,则会报错:
/dev/vdb is already mounted or /data busy
- 挂载点必须已存在,否则会报错:
文件系统要挂载到主机目录树中某个目录,才能被用户读写。
- 被挂载的目录称为挂载点(mount point)。
- 挂载后,原本位于挂载点目录下的文件会对用户不可见,相当于被新的一层目录覆盖了。
- 例如:当系统插入 CD 之后会自动生成磁盘分区 /dev/cdrom ,要执行
mount /dev/cdrom /media/cdrom
将它挂载,才能读写。
常见的挂载选项:
defaults # 默认选项,一般为 async,auto,dev,exec,nouser,rw,suid remount # 如果该文件系统已挂载,则重新挂载。常用于将一个只读模式的文件系统挂载为读写模式,例如 mount -o remount,rw /dir async # 允许异步 IO sync # 禁止异步 IO ,只能同步 IO atime # 自动更新 inode 的 access time ,默认启用该选项 noatime relatime # 如果当前 modify time 比 access time 更新,则更新 access time norelatime dev # 允许读写该文件系统中的设备文件 nodev exec # 允许执行该文件系统中的二进制文件 noexec suid # 执行该文件系统中的程序时,允许 SUID 文件权限 nosuid auto # 允许被 mount -a 挂载 noauto user # 允许被非 root 用户挂载、卸载 nouser ro # 以只读模式挂载 rw # 以读写模式挂载
用 mount 命令挂载的目录在系统重启后不会再挂载,而在 /etc/fstab 文件中可以设置系统开机时自动挂载的目录,例如:
# device mount_point fs_type options dump pass /dev/vdb /data xfs defaults 1 1 /swapfile swap swap defaults 0 0
- dump 字段表示通过 dump 工具备份该文件系统的频率,可以取值:不过目前的 Linux 发行版一般没有安装 dump 工具。
0 # 不备份。这是默认值 1 # 每天备份一次 2 # 每 2 天备份一次
- pass 字段表示开机自检的顺序,可以取值:
0 # 不检查。这是默认值 1 # 启动时检查,检查不通过则不能正常开机,需要进入救援模式修复 2 # 启动之后才检查
- dump 字段表示通过 dump 工具备份该文件系统的频率,可以取值:
# umount
umount
<fs> # 卸载文件系统,需要指定对应的磁盘分区或挂载点
-f # 强制卸载
-a # 卸载所有文件系统
# autofs
使用 autofs 服务可以在用户访问一个尚未挂载的文件系统时自动挂载它,降低空闲时的系统负载。
# df
df # 显示系统所有文件系统的信息
[file] # 显示指定文件所属的文件系统的信息
-h # 显示成人类容易阅读的单位。这会自动选用 MB、GB 等单位,不会四舍五入,而是向上取整
-i # 显示 inode 的数量、使用率
-a # 显示所有文件系统(包括 proc、swap)
-T # 增加显示文件系统的类型
- 例:
[root@CentOS ~]# df -hT Filesystem Type Size Used Avail Use% Mounted on devtmpfs devtmpfs 1.9G 0 1.9G 0% /dev tmpfs tmpfs 1.9G 24K 1.9G 1% /dev/shm tmpfs tmpfs 1.9G 1.8M 1.9G 1% /run tmpfs tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup tmpfs tmpfs 379M 0 379M 0% /run/user/0 /dev/vda1 ext4 50G 24G 23G 52% / /dev/vdb xfs 100G 5G 95G 5% /data
- 文件系统 /dev/vda1 为 ext4 类型,挂载到了根目录 / 。通常是供整个 Linux 系统使用的磁盘,称为系统盘。
- 如果系统盘写满了,会导致 SSH 登录失败、执行一些 shell 命令失败。
- 文件系统 /dev/vdb 为 xfs 类型,挂载到了目录 /data 。通常是供某些程序存储数据的磁盘,称为数据盘。
- 上述两个文件系统保存在磁盘设备中,而临时文件系统 devtmpfs、tmpfs 保存在内存中。
- swap 分区不属于文件系统,因此不包含在 df 命令的显示结果中。
- 文件系统 /dev/vda1 为 ext4 类型,挂载到了根目录 / 。通常是供整个 Linux 系统使用的磁盘,称为系统盘。
# 扩容
如果增加了磁盘设备的容量,用 fdisk -l 命令可看到其容量变化,但其中的磁盘分区、文件系统的容量并不会改变,还需要手动扩容。
常见的扩容方案:
- 格式化:取消挂载磁盘,重新创建磁盘分区,然后格式化为文件系统。
- 在线扩容:如果存在磁盘分区则用 growpart 命令扩容,然后用 resize2fs 命令扩容文件系统。
ext2/ext3/ext4 文件系统可用以下命令扩容:
growpart <disk> <n> # 扩展磁盘中的 n 号磁盘分区。这会修改分区表,使它占据后续的所有磁盘空间,直到磁盘末尾或下一个分区
resize2fs <fs> [size] # 调整文件系统的容量
- 文件系统已挂载时,可用 resize2fs 命令在线扩容,但不支持在线缩容。
- size 可使用 K、M、G、T 等单位。如果未指定 size ,则默认占用 device 的全部容量。
- 例:
growpart /dev/vdb 1 resize2fs /dev/vdb
xfs 文件系统可用以下命令扩容:
xfs_growfs <fs> -d <size> # 调整到指定大小。如果不指定,则默认占用 device 的全部容量
# 数据备份
按实现方式分类:
- 物理备份
- :直接拷贝文件,甚至拷贝整个磁盘。
- 这样拷贝、恢复的速度很快,接近磁盘的读写速度上限。但是会拷贝多余的数据。
- 逻辑备份
- :从每个文件中读取要备份的数据,然后拷贝到其它文件中。
- 这样备份速度慢,但是占用磁盘空间小。
按是否停止服务分类:
- 在线备份
- :又称为热备份,在备份的同时保持提供服务,允许用户访问服务器。
- 离线备份
- :又称为冷备份,通常需要停止服务器,中断服务。
按备份策略分类:
- 完全备份
- :将所有文件都备份。
- 这种备份策略最简单,恢复速度最快,但是备份速度最慢。
- 增量备份
- :只备份上一次备份操作后的改动记录,恢复文件时需要按顺序使用每一次增量备份,任何一次增量备份都不能出问题。
- 这样备份速度最快,每次占用的磁盘空间也小,但是可靠性最低,恢复速度最慢。
- 差异备份
- :将上一次完全备份后的改动记录都备份下来,这样恢复文件时只需要使用一个完全备份和一个差异备份。
- 效果介于完全备份与增量备份之间。