# 磁盘管理

# 磁盘分区

  • 磁盘设备中默认不存在磁盘分区、分区表。有需求的话,可以划分多个磁盘分区。
    • 磁盘分区之间相互独立,可采用不同的文件系统。
    • 磁盘分区之间相互隔离,一个分区发生故障时不会影响到其它分区。
  • 给 Linux 主机添加磁盘的步骤:
    1. 给计算机接入一个磁盘设备,可选划分磁盘分区。
    2. 用 mkfs 命令将磁盘或磁盘分区格式化为某种文件系统。
    3. 用 mount 命令将文件系统挂载到主机的某个目录,供用户读写。

# fdisk

fdisk
      -l          # 显示系统所有的磁盘及其磁盘分区
      <disk>      # 打开一个终端,对指定磁盘进行配置
  • 进入 fdisk 终端之后,可输入以下命令:
    p      # 显示已有的磁盘分区
    n      # 创建一个分区
    p      # 接着输入 p 会创建主分区,输入 e 会创建扩展分区
    2      # 然后设置分区编号
    Enter  # 设置起始扇区(按回车会使用默认值)
    +20G   # 设置结束扇区(可指定扇区大小)
    d      # 删除一个分区
    w      # 将设置内容保存
    

# 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 count=1024 bs=1M
    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 版本。
  • 优点:
    • 可以创建跨越多个存储设备的逻辑卷。
    • 逻辑卷在创建之后可以改变容量,而传统的磁盘分区不支持。
  • 使用步骤:
    1. 将单个块设备(比如磁盘、磁盘分区)格式化为物理卷(Physical Volume ,PV)。
      • 默认在物理卷开头的第二个扇区中存储 LVM Label 标签,包括 UUID、容量等信息。
      • 物理卷划以物理盘区(Physical Extents ,PE)为单位划分存储空间,默认为 4MB 。
    2. 将多个物理卷组成一个卷组(Volume Group ,VG),提供一个存储资源池。
    3. 在卷组中创建逻辑卷(Logical Volume ,LV)。
      • 执行 fdisk -l 可看到逻辑卷,可格式化为文件系统,挂载后供用户读写。
  • 逻辑卷的分类:
    • 线性卷(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),用于记录一个文件在磁盘中的信息。

  • 新建一个文件保存到磁盘时,系统会给它分配一些 block 用于存储,再分配一个 inode 用于记录文件信息。

  • 每个 inode 有一个数字编号作为唯一标识。

    • inode 里会记录文件的元数据。主要是通过 stat 命令查看到的那些信息,比如文件类型、访问权限、指向该 inode 的硬链接数。
    • inode 里会记录文件内容存储在磁盘的哪些 block 中。
    • 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 反查出当前的文件名。

# 分类

  • 存储在磁盘中的文件系统举例:
    • ext2 :扩展文件系统(extended file system)的第二代,于 1993 年开始被 Linux 内核使用。
    • ext3 :兼容 ext2 。能记录日志,在非正常关机后能根据日志快速恢复关机时没有保存的数据,而不必扫描整个磁盘。
    • ext4 :兼容 ext3 。支持更大的文件系统和文件,支持无限数量的子目录。是 RHEL6 的默认文件系统。
    • xfs :一个优秀的日志文件系统。是 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
      • 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 进程。
  • 存储在网络中的文件系统举例:
    • nfs :网络文件系统(Network File System),可以让不同主机通过网络共享文件。
  • FUSE(Filesystem in USErspace):类 Unix 系统内核提供的一种 API ,允许用户态进程创建、挂载文件系统,不必进入内核态。

# mkfs

mkfs <device>     # 将一个 device 格式化成某种文件系统。device 可以是磁盘设备、磁盘分区或文件
    -t <fs>       # 指定文件系统,默认为 ext2
  • 例:
    mkfs -t ext4 /dev/vdb
    
    • 如果 device 已存在文件系统,则会报错:/dev/vdb appears to contain an existing filesystem
    • 如果 device 已被挂载,则会报错:/dev/vdb contains a mounted filesystem

# mount

mount                   # 显示已挂载的所有目录
    <fs> <dir>          # 将一个文件系统挂载到指定目录
        -t ext4         # 指定文件系统的类型,默认会自动识别
        -r              # 挂载为只读模式
        -L "sdb3"       # 添加卷标
        -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 工具备份该文件系统的频率,可以取值:
      0   # 不备份。这是默认值
      1   # 每天备份一次
      2   # 每 2 天备份一次
      
      不过目前的 Linux 发行版一般没有安装 dump 工具。
    • pass 字段表示开机自检的顺序,可以取值:
      0   # 不检查。这是默认值
      1   # 启动时检查,检查不通过则不能正常开机,需要进入救援模式修复
      2   # 启动之后才检查
      

# umount

umount
      <fs>        # 卸载文件系统,需要指定对应的磁盘分区或挂载点
          -f      # 强制卸载
      -a          # 卸载所有文件系统

# autofs

使用 autofs 服务可以在用户访问一个尚未挂载的文件系统时自动挂载它,降低空闲时的系统负载。

# df

df          # 显示系统所有文件系统的信息
    [file]  # 显示指定文件所属的文件系统的信息
    -h      # 以人类容易阅读的单位显示文件大小(默认单位为 kBytes)
    -i      # 显示 inode 的数量、使用率
    -a      # 显示所有文件系统(包括 proc、swap)
    -T      # 增加显示文件系统的类型

# 扩容

  • 如果增加了磁盘设备的容量,用 fdisk -l 命令可看到其容量变化,但其中的磁盘分区、文件系统的容量并不会改变,还需要手动扩容。

  • 常见的扩容方案:

    • 格式化:取消挂载磁盘,重新创建磁盘分区,然后格式化为文件系统。
    • 在线扩容:如果存在磁盘分区则用 growpart 命令扩容,然后用 resize2fs 命令扩容文件系统。
  • 相关命令:

    growpart <disk> <n>         # 扩展磁盘中的 n 号磁盘分区。这会修改分区表,使它占据后续的所有磁盘空间,直到磁盘末尾或下一个分区
    
    resize2fs <device> [size]   # 调整文件系统的容量
    
    • resize2fs 命令支持处理 ext2、ext3、ext4 文件系统。
      • 文件系统已挂载时,支持在线扩容,但不支持在线缩容。
      • size 可使用 K、M、G、T 等单位。如果未指定 size ,则默认占用 device 的全部容量。
    • 例:
      growpart  /dev/vdb  1
      resize2fs /dev/vdb
      

# 数据备份

按实现方式分类:

  • 物理备份
    • :直接拷贝文件,甚至拷贝整个磁盘。
    • 这样拷贝、恢复的速度很快,接近磁盘的读写速度上限。但是会拷贝多余的数据。
  • 逻辑备份
    • :从每个文件中读取要备份的数据,然后拷贝到其它文件中。
    • 这样备份速度慢,但是占用磁盘空间小。

按是否停止服务分类:

  • 在线备份
    • :又称为热备份,在备份的同时保持提供服务,允许用户访问服务器。
  • 离线备份
    • :又称为冷备份,通常需要停止服务器,中断服务。

按备份策略分类:

  • 完全备份
    • :将所有文件都备份。
    • 这种备份策略最简单,恢复速度最快,但是备份速度最慢。
  • 增量备份
    • :只备份上一次备份操作后的改动记录,恢复文件时需要按顺序使用每一次增量备份,任何一次增量备份都不能出问题。
    • 这样备份速度最快,每次占用的磁盘空间也小,但是可靠性最低,恢复速度最慢。
  • 差异备份
    • :将上一次完全备份后的改动记录都备份下来,这样恢复文件时只需要使用一个完全备份和一个差异备份。
    • 效果介于完全备份与增量备份之间。