在前面的部分中,我们学习了如何在文件级别操作数据。在这一节中,我们将从设备级别来考虑数据。Linux在处理存储设备方面有着惊人的能力,无论是物理存储(如硬盘)、网络存储,还是虚拟存储设备(如RAID冗余磁盘阵列和LVM逻辑卷管理器)。 然而,由于这不是一个关于系统管理的课程,我们不会深入探讨整个主题。我们要做的是介绍一些用于管理存储设备的概念和关键命令。
为了完成本部分的练习,我们将使用USB闪存驱动器、CD-RW光盘(适用于配备CD-ROM刻录机的系统)和软盘(同样,如果系统配备的话)。

随着Linux桌面的不断进步,管理存储设备变得越来越简单。想象一下,我们只需将设备插入电脑,系统就会自动识别并准备好使用。 这种便利在过去是无法想象的,那时我们需要手动进行许多配置。而在服务器环境中,由于其复杂的存储需求和配置,手动管理仍然是常态。
要管理存储设备,首先要做的就是将设备连接到文件系统的树结构中,这个过程被称为挂载。挂载的作用就像是给设备找一个家,让它能与操作系统进行交流。 在类Unix系统(如Linux)中,所有设备都挂载在一个统一的文件系统树上。这与MS-DOS和Windows系统不同,后者为每个设备创建独立的文件系统树(例如C:\、D:\等)。 通过这种方式,Linux能够更灵活地管理和访问不同的存储设备。
挂载是将存储设备连接到文件系统树的过程,使设备的内容可以通过文件系统访问。这与Windows的驱动器字母概念不同,Linux使用统一的文件系统树。
/etc/fstab文件列出了在启动时要挂载的设备(通常是硬盘分区)。这就像是系统的“设备清单”,告诉Linux哪些设备需要在开机时自动连接。以下是一个典型的/etc/fstab文件示例:
|UUID=1234-5678 / ext4 defaults 1 1 UUID=8765-4321 /home ext4 defaults 1 2 UUID=abcd-efgh /boot ext4 defaults 1 2 tmpfs /dev/shm tmpfs defaults 0 0 devpts /dev/pts devpts gid=5,mode=620 0 0 sysfs /sys sysfs defaults 0 0 proc /proc proc defaults 0 0 UUID=wxyz-1234 swap swap defaults 0 0
这个文件就像是系统的“设备管家”,管理着所有存储设备的连接。对我们来说,最重要的是前三个硬盘分区:
|UUID=1234-5678 / ext4 defaults 1 1 UUID=8765-4321 /home ext4 defaults 1 2 UUID=abcd-efgh /boot ext4 defaults 1 2
这些就像是电脑的“房间分配表”,告诉系统哪个硬盘分区对应哪个目录。文件的每一行由六个字段组成,就像填写表格一样:
mount命令就像是系统的“设备管家”,负责把各种存储设备连接到文件系统中。不带参数输入命令,就像查看“设备连接清单”:
|$ mount /dev/sda2 on / type ext4 (rw) proc on /proc type proc (rw) sysfs on /sys type sysfs (rw) devpts on /dev/pts type devpts (rw,gid=5,mode=620) /dev/sda5 on /home type ext4 (rw) /dev/sda1 on /boot type ext4 (rw) tmpfs on /dev/shm
这个列表就像是“设备连接表”,格式是:设备名称 挂载在 挂载点 类型 文件系统类型 (选项)。比如第一行告诉我们:硬盘分区/dev/sda2挂载在根目录/,文件系统是ext4,可以读写(rw表示read-write)。
这个列表最有趣的是最后两行。倒数第二行显示一个U盘挂载在/media/我的U盘,最后一行显示一个网络存储挂载在/mnt/网络存储。
这就像是告诉你:“U盘插在哪个文件夹,网络硬盘连在哪个位置”。
让我们用一个实际的例子来学习。假设我们要使用一张光盘。首先,看看插入光盘前的系统状态:
|$ mount /dev/sda2 on / type ext4 (rw) proc on /proc type proc (rw) sysfs on /sys type sysfs (rw) devpts on /dev/pts type devpts (rw,gid=5,mode=620) /dev/sda5 on /home type ext4 (rw) /dev/sda1 on /boot type ext4 (rw) tmpfs on /dev/shm
现在插入一张Ubuntu安装光盘,系统会自动挂载它:
|$ mount /dev/sda2 on / type ext4 (rw) proc on /proc type proc (rw) sysfs on /sys type sysfs (rw) devpts on /dev/pts type devpts (rw,gid=5,mode=620) /dev/sda5 on /home type ext4 (rw) /dev/sda1 on /boot type ext4 (rw) tmpfs on /dev/shm
插入光盘后,多了一行:/dev/sr0 on /media/Ubuntu-20.04.3 type iso9660。这告诉我们光盘设备/dev/sr0被挂载到了/media/Ubuntu-20.04.3目录,文件系统类型是iso9660(光盘专用格式),ro表示只读。
有时候,系统自动选择的挂载位置可能并不符合我们的需求。这时,我们可以通过手动操作来改变挂载位置。首先,我们需要将光盘从当前挂载点卸载:
|$ sudo umount /dev/sr0
然后创建一个我们喜欢的挂载点:
|$ sudo mkdir /mnt/我的光盘
最后手动挂载到新位置:
|$ sudo mount -t iso9660 /dev/sr0 /mnt/我的光盘
现在我们可以查看光盘内容:
|$ ls /mnt/我的光盘
有时候,当我们尝试卸载一个设备时,系统可能会返回一个错误信息,提示“device is busy”:
|$ sudo umount /dev/sr0 umount: /mnt/我的光盘: device is busy
这意味着当前设备正在被使用,可能是因为我们在该设备的目录中,或者有其他进程正在访问这个设备的文件。 为了成功卸载,我们需要确保没有任何进程在使用该设备,并且我们自己也不在该设备的目录中:
|$ cd ~ $ sudo umount /dev/sr0
现在就可以成功卸载了!
当我们使用free命令查看内存使用情况时,会看到一个叫“buffers”的东西。计算机系统总是想尽可能快地运行,但有时候会被一些慢设备拖慢,比如打印机。即使是最快的打印机,对计算机来说也很慢。如果计算机每次都要等打印机打印完一页,那就太慢了。
在早期的PC时代,这真是个大问题。每次打印时,计算机就像被按了暂停键一样,什么都做不了。为了不让计算机被拖慢,聪明的人发明了打印机缓冲区。 这个缓冲区就像一个中间人,帮忙把数据从计算机快速传到缓冲区,然后再慢慢传给打印机。这样,计算机就能继续忙自己的事情,不用等打印机。
这种缓冲的想法在计算机里用得很广泛。操作系统会把从存储设备读取的数据和要写入的数据尽量长时间地放在内存里,等到合适的时候再和慢设备打交道。 比如在Linux系统上,时间用得越久,内存看起来越满,但这并不是说Linux把内存都用光了,而是它在尽量利用内存来加速处理。
这种缓冲允许对存储设备的写入非常快速,因为对物理设备的写入被推迟到将来的时间。同时,要发送到设备的数据在内存中堆积。操作系统会不时地将这些数据写入物理设备。
卸载设备需要将所有剩余数据写入设备,以便可以安全地移除它。如果设备在首先卸载之前被移除,则可能不会传输所有要发送到设备的数据。在某些情况下,这些数据可能包括重要的目录更新,这将导致文件系统损坏。
有时候,我们会发现很难搞清楚设备的名字。以前这可不是个问题,因为设备总是固定在一个地方,不会变来变去。 类Unix系统就喜欢这种稳定性。在Unix刚被开发出来的时候,换个磁盘驱动器可是个大工程,得用叉车把像洗衣机那么大的设备从机房里搬出来。 不过,随着时间的推移,现代的桌面硬件配置变得越来越灵活,Linux也变得比它的前辈们更加适应这种变化。
在上面的例子中,我们利用了现代Linux桌面“自动”挂载设备的能力,然后事后确定名称。但是,如果我们管理服务器或其他不发生这种情况的环境怎么办?我们如何弄清楚?
首先,让我们看看系统如何命名设备。如果我们列出/dev目录(所有设备所在的位置)的内容,我们可以看到有很多很多设备:
|$ ls /dev
此列表的内容揭示了设备命名的一些模式。以下是几个常见的:
此外,我们经常看到符号链接,如/dev/cdrom、/dev/dvd和/dev/floppy,它们指向实际的设备文件,作为便利提供。
如果你在不会自动挂载可移动设备的系统上工作,可以使用以下技术来确定可移动设备在连接时的命名方式。首先,启动/var/log/messages或/var/log/syslog文件的实时视图(你可能需要超级用户权限):
|$ sudo tail -f /var/log/messages
文件的最后几行会显示出来,然后暂时停住。接下来,我们插入一个可移动设备,比如一个16MB的U盘。几乎立刻,系统内核就会发现这个设备并开始识别它:
|Jul 23 10:07:53 linuxbox kernel: usb 3-2: new full speed USB device using uhci_hcd and address 2 Jul 23 10:07:53 linuxbox kernel: usb 3-2: configuration #1 chosen from 1 choice Jul 23 10:07:53 linuxbox kernel: scsi3 : SCSI emulation for USB Mass Storage devices Jul 23
当显示再次暂停时,我们可以按下Ctrl-c返回到命令提示符。输出中有趣的部分是多次提到的“[sdb]”,这正好符合我们对SCSI磁盘设备名称的预期。了解这一点后,以下两行信息就显得特别有意义了:
|Jul 23 10:07:59 linuxbox kernel: sdb: sdb1 Jul 23 10:07:59 linuxbox kernel: sd 3:0:0:0: [sdb] Attached SCSI removable disk
这告诉我们设备名称是/dev/sdb(整个设备)和/dev/sdb1(设备上的第一个分区)。正如我们所看到的,使用Linux充满了有趣的侦探工作!
使用tail -f /var/log/messages技术是观察系统在近实时做什么的好方法。
有了设备名称,我们现在可以挂载闪存驱动器:
|$ sudo mkdir /mnt/flash $ sudo mount /dev/sdb1 /mnt/flash $ df Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda2 15115452 5186944 9775164 35% / /dev/sda5 59631908 31777376 24776480 57% /home /dev/sda1 147764 17277 122858 13% /boot tmpfs 776808 0 776808
只要我们的设备一直插在计算机上,并且计算机没有进行重启操作,那么这个设备的名称就会一直保持不变。也就是说,系统会一直识别这个设备为同一个名称,方便我们进行后续的操作和管理。
假设你的U盘现在用的是Windows的FAT32格式,但你想改成Linux的ext4格式,这样在Linux系统上使用会更稳定。这需要两个步骤:
下面的操作会完全擦除U盘上的所有数据!请确保:
/dev/sdb1,不是/dev/sda1)如果你不小心格式化了错误的设备,可能会丢失重要数据!
fdisk程序允许我们在非常低的级别直接与类似磁盘的设备(如硬盘驱动器和闪存驱动器)交互。使用此工具,我们可以编辑、删除和创建设备上的分区。
要使用我们的闪存驱动器,我们必须首先卸载它(如果需要),然后按如下方式调用fdisk程序:
|$ sudo umount /dev/sdb1 $ sudo fdisk /dev/sdb
注意,我们必须以整个设备的方式指定设备,而不是按分区号。程序启动后,我们将看到以下提示:
|Command (m for help):
输入“m”将显示程序菜单:
|Command action a toggle a bootable flag b edit bsd disklabel c toggle the dos compatibility flag d delete a partition l list known partition types m print this menu n add a new partition o create a new empty
在我们开始对设备进行任何更改之前,首先要做的就是查看当前设备的分区布局。我们可以通过输入“p”命令来打印出设备的分区表,这样我们就能清楚地看到设备上已经存在的分区信息。 这一步非常重要,因为它能帮助我们了解设备的当前状态,并确保我们在进行任何操作之前对设备的分区情况有一个全面的了解。
|Command (m for help): p Disk /dev/sdb: 16 MB, 16006656 bytes 1 heads, 31 sectors/track, 1008 cylinders Units = cylinders of 31 * 512 = 15872 bytes Device Boot Start End Blocks Id System /dev/sdb1 2 1008 15608+ b W95 FAT32
在此示例中,我们看到一个16MB设备,具有单个分区(1),使用设备上可用1008个柱面中的1006个。分区被标识为Windows 95 FAT32分区。 一些程序将使用此标识符来限制可以对磁盘执行的操作类型,但大多数时候更改它并不重要。但是,为了演示的目的,我们将更改它以指示Linux分区。
为此,我们必须首先找出用于标识Linux分区的ID。在上面的列表中,我们看到ID“b”用于指定现有分区。要查看可用分区类型的列表,我们参考程序菜单。在那里我们可以看到以下选择:
|l list known partition types
如果我们在提示符处输入“l”,将显示大量可能的类型。其中我们看到“b”用于我们的现有分区类型,“83”用于Linux。 回到菜单,我们看到这个选择来更改分区ID:
|t change a partition's system id
我们在提示符处输入“t”并输入新的ID:
|Command (m for help): t Selected partition 1 Hex code (type L to list codes): 83 Changed system type of partition 1 to 83 (Linux)
这完成了我们需要做的所有更改。到目前为止,设备还没有被触及(所有更改都存储在内存中,而不是在物理设备上),所以我们将修改的分区表写入设备并退出。为此,我们在提示符处输入“w”:
|Command (m for help): w The partition table has been altered! Calling ioctl() to re-read partition table. WARNING: If you have created or modified any DOS 6.x partitions, please see the fdisk manual page for additional information. Syncing disks. $
如果我们决定保持设备不变,我们可以在提示符处输入“q”,这将退出程序而不写入更改。我们可以安全地忽略听起来不祥的警告消息。
完成分区编辑后(尽管可能很轻量),是时候在我们的闪存驱动器上创建新文件系统了。为此,我们将使用mkfs(“make file system”的缩写),它可以创建各种格式的文件系统。
要在设备上创建ext3文件系统,我们使用“-t”选项指定“ext3”系统类型,后跟包含我们要格式化的分区的设备名称:
|$ sudo mkfs -t ext3 /dev/sdb1 mke2fs 1.40.2 (12-Jul-2007) Filesystem label= OS type: Linux Block size=1024 (log=0) Fragment size=1024 (log=0) 3904 inodes, 15608 blocks 780 blocks (5.00%) reserved for the super user First data block=1 Maximum filesystem blocks=15990784
当选择ext3作为文件系统类型时,程序将显示大量信息。要将设备重新格式化为其原始FAT32文件系统,指定“vfat”作为文件系统类型:
|$ sudo mkfs -t vfat /dev/sdb1
这种分区和格式化过程可以在向系统添加额外存储设备时使用。虽然我们使用了一个小闪存驱动器,但相同的过程可以应用于内部硬盘和其他可移动存储设备,如USB硬盘。
在我们之前对/etc/fstab文件的讨论中,我们在每行的末尾看到一些神秘的数字。每次系统启动时,它都会在挂载文件系统之前例行检查文件系统的完整性。
这是由fsck程序(“file system check”的缩写)完成的。每个fstab条目中的最后一个数字指定检查设备的顺序。在我们上面的示例中,我们看到根文件系统首先被检查,然后是home和boot文件系统。最后一个数字为零的设备不会例行检查。
除了检查文件系统的完整性外,fsck还可以修复损坏的文件系统,成功率各不相同,取决于损坏的程度。在类Unix文件系统上,恢复的文件部分被放置在lost+found目录中,位于每个文件系统的根目录中。
要检查我们的闪存驱动器(应该首先卸载),我们可以执行以下操作:
|$ sudo fsck /dev/sdb1 fsck 1.40.8 (13-Mar-2008) e2fsck 1.40.8 (13-Mar-2008) /dev/sdb1: clean, 11/3904 files, 1661/15608 blocks
根据我的经验,除非有硬件问题(如磁盘驱动器故障),否则文件系统损坏相当罕见。在大多数系统上,启动时检测到的文件系统损坏将导致系统停止并指示你在继续之前运行fsck。
对于一些仍在使用足够老以配备软盘驱动器的计算机的人,我们也可以管理这些设备。准备空白软盘供使用是一个两步过程。
首先,我们对软盘执行低级格式化,然后创建文件系统。要完成格式化,我们使用fdformat程序指定软盘设备的名称(通常是/dev/fd0):
|$ sudo fdformat /dev/fd0 Double-sided, 80 tracks, 18 sec/track. Total capacity 1440 kB. Formatting ... done Verifying ... done
接下来,我们使用mkfs将FAT文件系统应用到软盘:
|$ sudo mkfs -t msdos /dev/fd0
注意,我们使用“msdos”文件系统类型来获得较旧(和较小)样式的文件分配表。软盘准备好后,可以像其他设备一样挂载。
虽然我们通常认为计算机上的数据被组织成文件,但也可以以“原始”形式考虑数据。例如,如果我们看一个磁盘驱动器,我们看到它由大量数据“块”组成,操作系统将其视为目录和文件。 但是,如果我们可以将磁盘驱动器简单地视为数据块的大集合,我们可以执行有用的任务,如克隆设备。
dd程序是一个非常强大的工具,用于在不同位置之间复制数据块。它的名字来源于“data definition”,但它的功能远不止于此。dd可以在设备之间、文件之间,甚至在设备和文件之间进行数据传输。由于历史原因,它的语法有些独特,通常的使用方式如下:
|dd if=input_file of=output_file [bs=block_size [count=blocks]]
假设我们有两个相同大小的USB闪存驱动器,我们想要将第一个驱动器精确复制到第二个驱动器。
如果我们将两个驱动器连接到计算机,它们分别分配给设备/dev/sdb和/dev/sdc,我们可以用以下方式将第一个驱动器上的所有内容复制到第二个驱动器:
|dd if=/dev/sdb of=/dev/sdc
如果我们只有一个设备连接到计算机,而没有其他设备可以直接复制到,我们可以选择将这个设备的内容完整地复制到一个普通文件中。 这样做的好处是,我们可以在以后需要的时候,随时将这个文件恢复到另一个设备上,或者进行其他的复制操作。这种方法就像是给设备拍了一张“快照”,保存了当时的所有数据。
|dd if=/dev/sdb of=flash_drive.img
dd命令非常强大。虽然它的名字来源于“data definition”,但它有时被称为“destroy disk”,因为用户经常错误输入if或of规范。在按回车之前,始终仔细检查你的输入和输出规范!
制作光盘就像做菜一样,需要两个步骤:第一步是准备"菜谱"(制作iso镜像文件),第二步是"下锅"(把镜像文件刻录到光盘上)。
假设你有一张珍贵的Ubuntu安装光盘,担心用久了会坏掉。这时候你可以制作一个"数字备份",就像给光盘拍一张"数字照片"。 首先插入光盘,然后制作镜像:
|dd if=/dev/sr0 of=我的Ubuntu备份.iso
这个命令就像是"复制光盘",把整个光盘的内容保存为我的Ubuntu备份.iso文件。以后如果原版光盘坏了,你就可以用这个备份文件制作新的光盘。
注意:这个方法只适用于数据光盘和DVD,不适用于音乐CD,因为音乐CD的存储方式不同。
有时候你想把一堆文件制作成光盘,比如制作一个软件合集或者备份重要文档。这就像是把散落的照片整理成一本相册。 首先,创建一个文件夹,把所有要制作成光盘的文件放进去:
|$ mkdir ~/我的光盘文件 $ cp 重要文档.pdf ~/我的光盘文件/ $ cp 软件安装包.exe ~/我的光盘文件/ $ cp 照片文件夹 ~/我的光盘文件/ -r
然后使用genisoimage命令制作镜像:
|genisoimage -o 我的光盘.iso -R -J ~/我的光盘文件
这个命令会创建一个名为我的光盘.iso的镜像文件。-R选项让Linux系统能正确显示长文件名,-J选项让Windows系统也能正常使用。
如果你在网上搜索光盘刻录的教程,可能会看到两个程序:mkisofs和cdrecord。这些是旧版本的程序,现在已经被新的程序替代了。
新版本的程序叫做:
genisoimage(替代mkisofs)wodim(替代cdrecord)这就像是软件升级一样,新版本功能更好,兼容性更强。
我们有一个小窍门,可以在iso镜像文件还在硬盘上的时候,就像它已经刻录到光盘上一样来使用它。这个方法就是利用mount命令中的“-o loop”选项。
这个选项的作用是将iso镜像文件模拟成一个设备,然后我们再通过“-t iso9660”指定文件系统类型,把这个“设备”挂载到我们的文件系统中。这样一来,我们就可以像使用真实的光盘一样来访问这个iso镜像文件了。
|mkdir /mnt/iso_image mount -t iso9660 -o loop image.iso /mnt/iso_image
在上面的示例中,我们创建了一个名为/mnt/iso_image的挂载点,然后将镜像文件image.iso挂载在该挂载点。镜像挂载后,可以像真正的CD-ROM或DVD一样对待。记住在不再需要时卸载镜像。
可重写CD-RW介质需要在使用前擦除或清空。为此,我们可以使用wodim,指定CD刻录机的设备名称和要执行的清空类型。wodim程序提供几种类型。最最小(和最快)的是“fast”类型:
|wodim dev=/dev/cdrw blank=fast
要写入镜像,我们再次使用wodim,指定光学介质刻录机设备的名称和镜像文件的名称:
|wodim dev=/dev/cdrw image.iso
除了设备名称和镜像文件外,wodim支持大量选项。两个常见的是“-v”用于详细输出,和“-dao”,它以disc-at-once模式写入光盘。如果你准备光盘用于商业复制,应使用此模式。wodim的默认模式是track-at-once,这对于录制音乐轨道很有用。
验证我们下载的iso镜像的完整性通常很有用。在大多数情况下,iso镜像的分发者也会提供校验和文件。校验和是异国数学计算的结果,产生一个表示目标文件内容的数字。如果文件的内容即使改变一位,产生的校验和也会大不相同。生成校验和的最常见方法使用md5sum程序。当你使用md5sum时,它产生一个唯一的十六进制数字:
|md5sum image.iso 34e354760f9bb7fbf85c96f6a3f94ece image.iso
下载镜像后,你应该对其运行md5sum并将结果与发布者提供的md5sum值进行比较。
除了检查下载文件的完整性外,我们还可以使用md5sum来验证新写入的光学介质。为此,我们首先计算镜像文件的校验和,然后计算介质的校验和。验证介质的技巧是将计算限制为仅包含镜像的光学介质部分。我们通过确定镜像包含的2048字节块的数量(光学介质总是以2048字节块写入)并从介质读取那么多块来做到这一点。在某些类型的介质上,这不是必需的。以disc-at-once模式写入的CD-R可以这样检查:
|md5sum /dev/cdrom 34e354760f9bb7fbf85c96f6a3f94ece /dev/cdrom
许多类型的介质,如DVD,需要精确计算块数。在下面的示例中,我们检查镜像文件dvd-image.iso和DVD读取器/dev/dvd中光盘的完整性。你能弄清楚这是如何工作的吗?
|md5sum dvd-image.iso; dd if=/dev/dvd bs=2048 count=$(( $(stat -c “%s” dvd-image.iso) / 2048 )) | md5sum
让我们做一些实际的练习,动手操作才能掌握技巧:
|# 1. 挂载U盘 $ sudo mkdir /mnt/我的U盘 $ sudo mount /dev/sdb1 /mnt/我的U盘 $ ls /mnt/我的U盘 # 2. 重新格式化U盘(注意:会删除所有数据!) $ sudo umount /dev/sdb1 $ sudo mkfs -t ext4 /dev/sdb1 $ sudo mount /dev/sdb1 /mnt/我的U盘 # 3. 制作光盘镜像 $ mkdir ~/备份文件 $ cp 重要文档.pdf ~/备份文件/ $