前言
当您的计算机上连接了存储设备(如硬盘)时,负责与硬件通信的 Linux 内核会检测该设备并在 /dev 中创建一个代表该设备的文件,如 /dev/sda。对于从设备分区表中检测到的每个分区,都会创建另一个文件,如 /dev/sda1
在较新的内核中,一些文件也在 sysfs (/sys) 中创建,并向用户公开更高级的控制和信息。(本文未讨论这些文件)
为了能够真正读取/写入设备,您应该先挂载它。mount命令会挂载存储设备或文件系统,使其可访问并将其附加到现有目录结构。
为了挂载卷,您应该调用 mount 命令。首先,您将创建一个目录(应该是空的)
mkdir /mnt/mydata
然后将卷安装到该位置:
mount /dev/sda1 /mnt/mydata
要查看所有已安装的卷及其选项,您可以使用mount命令或findmnt(均由 util-linux 提供,并在所有发行版中可用):
$ findmnt
TARGET SOURCE FSTYPE OPTIONS
/ /dev/sda4 ext4 rw,relatime,errors=remount-ro
├─/sys sysfs sysfs rw,nosuid,nodev,noexec,relatime
│ ├─/sys/kernel/security securityfs securityfs rw,nosuid,nodev,noexec,relatime
│ ├─/sys/fs/cgroup tmpfs tmpfs ro,nosuid,nodev,noexec,mode=755
│ │ ├─/sys/fs/cgroup/unified cgroup2 cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate
│ │ ├─/sys/fs/cgroup/systemd cgroup cgroup rw,nosuid,nodev,noexec,relatime,xattr,name=systemd
│ │ ├─/sys/fs/cgroup/net_cls,net_prio cgroup cgroup rw,nosuid,nodev,noexec,relatime,net_cls,net_prio
│ │ ├─/sys/fs/cgroup/perf_event cgroup cgroup rw,nosuid,nodev,noexec,relatime,perf_event
│ │ ├─/sys/fs/cgroup/pids cgroup cgroup rw,nosuid,nodev,noexec,relatime,pids
│ │ ├─/sys/fs/cgroup/cpu,cpuacct cgroup cgroup rw,nosuid,nodev,noexec,relatime,cpu,cpuacct
│ │ ├─/sys/fs/cgroup/blkio cgroup cgroup rw,nosuid,nodev,noexec,relatime,blkio
│ │ ├─/sys/fs/cgroup/cpuset cgroup cgroup rw,nosuid,nodev,noexec,relatime,cpuset
│ │ ├─/sys/fs/cgroup/freezer cgroup cgroup rw,nosuid,nodev,noexec,relatime,freezer
│ │ ├─/sys/fs/cgroup/devices cgroup cgroup rw,nosuid,nodev,noexec,relatime,devices
│ │ └─/sys/fs/cgroup/memory cgroup cgroup rw,nosuid,nodev,noexec,relatime,memory
│ ├─/sys/firmware/efi/efivars efivarfs efivarfs rw,nosuid,nodev,noexec,relatime
│ ├─/sys/fs/bpf bpf bpf rw,nosuid,nodev,noexec,relatime,mode=700
│ ├─/sys/kernel/debug debugfs debugfs rw,relatime
│ └─/sys/fs/fuse/connections fusectl fusectl rw,relatime
├─/proc proc proc rw,nosuid,noexec,relatime
│ └─/proc/sys/fs/binfmt_misc systemd-1 autofs rw,relatime,fd=36,pgrp=1,timeout=0,minproto=5,maxproto=5,direct
├─/dev devtmpfs devtmpfs rw,nosuid,size=8144172k,nr_inodes=2036043,mode=755
│ ├─/dev/shm tmpfs tmpfs rw
│ │ └─/dev/shm none tmpfs rw,relatime
│ ├─/dev/pts devpts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000
│ ├─/dev/mqueue mqueue mqueue rw,relatime
│ └─/dev/hugepages hugetlbfs hugetlbfs rw,relatime,pagesize=2M
├─/run tmpfs tmpfs rw,nosuid,nodev,mode=755
│ ├─/run/media/shahriar/Data /dev/sda2 fuseblk rw,nosuid,nodev,relatime,user_id=0,group_id=0,default_permissions,allow_other,blksize=4096
│ ├─/run/user/1000 tmpfs tmpfs rw,nosuid,nodev,relatime,size=1638552k,mode=700,uid=1000,gid=1000
│ │ └─/run/user/1000/gvfs gvfsd-fuse fuse.gvfsd-fuse rw,nosuid,nodev,relatime,user_id=1000,group_id=1000
│ └─/run/snapd/ns tmpfs[/snapd/ns] tmpfs rw,nosuid,nodev,mode=755
│ └─/run/snapd/ns/chromium.mnt nsfs[mnt:[4026532495]] nsfs rw
├─/tmp tmpfs tmpfs rw,nosuid,nodev
├─/snap/slack/7 /dev/loop0 squashfs ro,nodev,relatime
├─/snap/sublime-text/26 /dev/loop1 squashfs ro,nodev,relatime
├─/snap/core/5145 /dev/loop2 squashfs ro,nodev,relatime
├─/snap/chromium/412 /dev/loop4 squashfs ro,nodev,relatime
├─/snap/core/5328 /dev/loop3 squashfs ro,nodev,relatime
└─/snap/telegram-desktop/270 /dev/loop5 squashfs ro,nodev,relatime
$ mount
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
proc on /proc type proc (rw,nosuid,noexec,relatime)
devtmpfs on /dev type devtmpfs (rw,nosuid,size=8144172k,nr_inodes=2036043,mode=755)
securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev/shm type tmpfs (rw)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)
tmpfs on /run type tmpfs (rw,nosuid,nodev,mode=755)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
cgroup2 on /sys/fs/cgroup/unified type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
efivarfs on /sys/firmware/efi/efivars type efivarfs (rw,nosuid,nodev,noexec,relatime)
bpf on /sys/fs/bpf type bpf (rw,nosuid,nodev,noexec,relatime,mode=700)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
/dev/sda4 on / type ext4 (rw,relatime,errors=remount-ro)
mqueue on /dev/mqueue type mqueue (rw,relatime)
hugetlbfs on /dev/hugepages type hugetlbfs (rw,relatime,pagesize=2M)
systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=36,pgrp=1,timeout=0,minproto=5,maxproto=5,direct)
debugfs on /sys/kernel/debug type debugfs (rw,relatime)
tmpfs on /tmp type tmpfs (rw,nosuid,nodev)
/var/lib/snapd/snaps/sublime-text_26.snap on /snap/sublime-text/26 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/slack_7.snap on /snap/slack/7 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/core_5145.snap on /snap/core/5145 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/chromium_412.snap on /snap/chromium/412 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/core_5328.snap on /snap/core/5328 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/telegram-desktop_270.snap on /snap/telegram-desktop/270 type squashfs (ro,nodev,relatime,x-gdu.hide)
fusectl on /sys/fs/fuse/connections type fusectl (rw,relatime)
tmpfs on /run/user/1000 type tmpfs (rw,nosuid,nodev,relatime,size=1638552k,mode=700,uid=1000,gid=1000)
gvfsd-fuse on /run/user/1000/gvfs type fuse.gvfsd-fuse (rw,nosuid,nodev,relatime,user_id=1000,group_id=1000)
tmpfs on /run/snapd/ns type tmpfs (rw,nosuid,nodev,mode=755)
nsfs on /run/snapd/ns/chromium.mnt type nsfs (rw)
none on /dev/shm type tmpfs (rw,relatime)
/dev/sda2 on /run/media/shahriar/Data type fuseblk (rw,nosuid,nodev,relatime,user_id=0,group_id=0,default_permissions,allow_other,blksize=4096,uhelper=udisks2)
要卸载设备,您应该使用umount:
可以指定包含文件系统的设备或挂载点。
umount /dev/sda1
umount /mnt/mydata
关于权限的说明
/dev 中创建的所有设备文件都只能由 root 访问。因此,一般来说,只有 root 可以挂载东西。这非常重要,您需要牢记这一点。如果文件系统具有 Linux 权限支持(Linux 原生文件系统,如 ext2、ext3、ext4、btrfs、xfs 等),它们就会被利用,并且驱动器上的文件将被视为 Linux 系统中的其他文件。但某些文件系统(如 FAT 衍生产品(FAT32、exFAT 等))无法存储权限信息。因此,默认情况下,它们只有 root 可用,除非您在挂载时指定一些选项以允许其他用户。考虑以下示例:
replace xxx with uid/gid of user
mount -t vfat /dev/sda6 /media/FAT32 -o rw,uid=xxx,gid=xxx
还有这个简洁的一行程序,可以将它们安装到您的用户可访问的位置:
$ sudo mount -t vfat /dev/sda6 /media/FAT32 -o rw,uid=$(id -u),gid=$(id -g)
或所有用户...
mount -t vfat /dev/sda6 /media/FAT32 -o rw,umask=0000
文件系统
文件系统控制数据的存储和检索方式。通常,mount 命令会检测必要的选项和文件系统,但您可以像这样手动指定它们:
mount -t ntfs /dev/sda1 /mnt/mydata
您可以通过阅读此文件查看内核支持的所有文件系统:
$ cat /proc/filesystems
nodev sysfs
nodev rootfs
nodev ramfs
nodev bdev
nodev proc
nodev cpuset
nodev cgroup
nodev cgroup2
nodev tmpfs
nodev devtmpfs
nodev debugfs
nodev tracefs
nodev securityfs
nodev sockfs
nodev dax
nodev bpf
nodev pipefs
nodev hugetlbfs
nodev devpts
ext3
ext4
iso9660
nodev autofs
xfs
nodev efivarfs
nodev mqueue
btrfs
squashfs
fuseblk
nodev fuse
nodev fusectl
nodev overlay
第一列表示文件系统是否挂载在块设备上。以nodev开头的文件系统未挂载在设备上。第二列列出了支持的文件系统名称。
当没有指定参数时,mount 命令会循环遍历此处列出的文件系统。
要查看驱动器上现有的文件系统,请使用 lsblk 命令,如下所示:
$ lsblk -f
NAME FSTYPE LABEL UUID MOUNTPOINT
loop0 squashfs /snap/slack/7
loop1 squashfs /snap/sublime-text/26
loop2 squashfs /snap/core/5145
loop3 squashfs /snap/core/5328
loop4 squashfs /snap/chromium/412
loop5 squashfs /snap/telegram-desktop/270
sda
├─sda1
├─sda2 ntfs Data DCDE47AADE477C30 /run/media/shahriar/Data
├─sda3 swap 9269aa88-3a31-4299-a1ef-f1472750717f [SWAP]
└─sda4 ext4 7fe5feba-8c3d-4fe9-ab0a-20776aabf441 /
sdb
├─sdb1 ntfs Recovery 5436D56136D544A0
├─sdb2 vfat 30D6-1226
├─sdb3
└─sdb4 ntfs 560A18780A1856F9
sr0
循环设备显然被 snapd 使用
正如我之前所说,这些是内核支持的文件系统类型,您可能会问,那么还有什么地方可以实现文件系统?答案是 FUSE。
用户空间文件系统 (FUSE) 是类 Unix 操作系统的一种机制,允许非特权用户创建自己的文件系统而无需编辑内核代码。这是通过在用户空间运行文件系统代码来实现的,而 FUSE 内核模块仅提供与实际内核接口的“桥梁”。
但是您应该知道,在 FUSE 文件系统上读取/写入会涉及更多开销,因为提供文件系统功能的代码位于用户空间,并且每次调用该函数都会比涉及内核驱动程序的常规文件操作多遍历内核和用户空间。尽管如此,它在很多情况下都是非常有用的功能。(例如通过网络安装可写的 ntfs 或文件系统)
Linux 世界中还有大量其他文件系统,例如覆盖文件系统,因此我建议您阅读文章末尾的参考资料以进行进一步阅读。
一旦我们了解了 Linux 中挂载的基础知识,我们将学习如何自动化挂载以及 Linux 启动时挂载时会发生什么。
fstab 文件可用于定义如何将磁盘分区、各种其他块设备或远程文件系统挂载到文件系统中。
每个文件系统在单独的一行中描述。每行上的字段由制表符或空格分隔。以“#”开头的行是注释。空白行将被忽略。
如果只给出了目录或设备之一,mount 命令将使用 fstab 来填充另一个参数的值。这样做时,还将使用 fstab 中列出的挂载选项。(可以在 fstab 中设置用户选项以允许非 root 用户挂载)
/etc/fstab 中指定的所有设备将在启动时以及在 mount 中使用-a标志时自动挂载,除非指定了noauto选项。列出但不存在的设备将导致错误,除非使用nofail选项。
$ cat /etc/fstab
<device> <dir> <type> <options> <dump> <fsck>
/dev/sda1 / ext4 noatime 0 1
/dev/sda2 none swap defaults 0 0
/dev/sda3 /home ext4 noatime 0 2
强烈建议使用 UUID 或其他唯一标识符,而不是依赖内核名称描述符(sda1、sdb2、...),因为它们可能会在重启后发生变化!
UUID 是首选方法。您可以使用lsblk -f找出 UUID
$ cat /etc/fstab
<device> <dir> <type> <options> <dump> <fsck>
UUID=CBB6-24F2 /boot vfat defaults 0 2
UUID=0a3407de-014b-458b-b5c1-848e92a327a3 / ext4 defaults 0 1
UUID=b411dc99-f0a0-4c87-9e05-184977be8539 /home ext4 defaults 0 2
UUID=f9fe0b69-a280-415d-a03a-a32752370dee none swap defaults 0 0
现代化安装
在 21 世纪初,第一次尝试解决这个问题被称为 HAL - 硬件抽象层,它实现了它的名字,在设备节点和节点用户之间提供了一个层,因此可以添加和删除存储设备(和其他硬件),而无需重新启动系统,也无需重写 /etc/fstab 文件。
在 HAL 弃用之后,随着在硬件开发快速发展的时期发现更好的方法,一切都被替换了令人眼花缭乱的次数(DeviceKit,devfs 等),udev 最终获胜并成为未来十年的主流。
当在系统中添加或删除设备时,Linux 内核会注意到并发出事件。udev 是一个守护进程,它等待监听这些事件,然后做出相应的响应。udev 在用户空间而不是内核空间中运行。
udisks 是基于新技术(D-Bus、udev)创建的。它使用户空间的安装变得现代化。
udisks 提供:
- 守护进程 udisksd,实现明确定义的 D-Bus 接口,可用于查询和操作存储设备。
- 命令行工具udisksctl可用于查询和使用守护进程。使用 polkit 限制用户使用 udisks 执行的操作。
当您将 USB 闪存驱动器插入 PC 后,只需单击桌面上出现的漂亮的 USB 闪存驱动器图标,它就会处理所有工作和权限检查。
在本文中,我们将介绍如何使用udisksctl进行基本的挂载/卸载。但重要的是要知道,这里提供的所有功能只是 D-Bus 调用的包装器,易于编程,在存储管理自动化中非常有用。(比如提到的点击等)
要查看连接到系统的磁盘列表(序列号已被替换):
$ udisksctl status
MODEL REVISION SERIAL DEVICE
--------------------------------------------------------------------------
Samsung SSD 860 EVO 500GB XXXXXXX XXXXXXXXXXX sda
SanDisk SD8S XXXXXXX XXXXXXXXXXX sdb
SlimtypeDVD A XXXXXXX XXXXXXXXXXX sr0
查看磁盘的详细信息:
$ udiskctl dump
<output not shown due to length>
要使用这个新工具实际挂载文件系统:
udisksctl mount -b /dev/sdb1
然后卸载:
udisksctl unmount -b /dev/sdb1
systemd 挂载单元
考虑到 udev 和 systemd 的合并,以及 systemd 在现代 Linux 发行版中的普遍性。建议抛弃旧习惯,开始享受可用的炫酷新功能。
在 fstab 中添加条目不再是启动时挂载设备的主要方式。事实上,/etc/fstab 中的所有条目在启动时都会转换为 systemd 挂载单元。
systemd .mount 文件示例:
[Mount]
What=/dev/disk/by-uuid/9269aa88-3a31-4299-bbb1-4e528a89d222
Where=/mnt/mydata
Type=ext4
Options=defaults
重要提示: 挂载单元必须以其控制的挂载点目录命名。
例如:必须在单元文件 home-lennart.mount 中配置挂载点 /home/lennart。
因此我们创建一个这样的文件:
vim /etc/systemd/system/mnt-mydata.mount
[Unit]
Description=Mount Some of my files to empty mydata dir
[Mount]
What=/dev/disk/by-uuid/9269aa88-3a31-4299-bbb1-4e528a89d222
Where=/mnt/mydata
Type=ext4
Options=defaults
当然,编辑完成后你应该通知 systemd 加载单元文件:
systemctl daemon-reload
systemctl start mnt-mydata.mount
您可以像查看其他单元一样查看挂载的状态:
systemctl status mnt-mydata.mount
重要提示: 如果您希望在每次启动时安装它,您还应该在单元文件中包含一个 [Install] 部分:
[Unit]
Description=Mount Some of my files to empty mydata dir
[Mount]
What=/dev/disk/by-uuid/9269aa88-3a31-4299-bbb1-4e528a89d222
Where=/mnt/mydata
Type=ext4
Options=defaults
[Install]
WantedBy=multi-user.target
并启用该单元在启动时启动:
systemctl enable mnt-backups.mount