本文将从文件、文件系统、文件系统管理、文件原理几个方面由浅入深地介绍 Linux 的文件系统。
“一切皆是文件” 是 Unix/Linux 的基本哲学之一。
这句话中的 “文件” 不仅仅是指我们通常所说的文件,而是指 Linux 世界中的所有、任意、一切东西都可以通过文件的方式访问和管理。它是一个抽象的概念,这才符合哲学意味。
linux 中有 7 种文件类型:普通文件、目录文件、块设备文件、字符设备、套接字文件、管道文件、链接文件。
这些文件都可以用一套相同的API(如:open/read/write/close等)进行操作。
这正好又符合 Unix/Linux 的另一个哲学思想“简单原则”,即著名的 KISS(keep it simple, stupid),意思是“保持简单和笨拙”。
Linux下的文件除了文件内容本身,还有一些必要的属性信息。如下图所示:
2533274790421609 drwxrwxrwx 1 canpool canpool 4.0K Jun 6 23:53 projects
canpool@DESKTOP-ODCM7SC:~/projects/canpool/shcanpool$ stat projects/
File: projects/
Size: 4096 Blocks: 0 IO Block: 4096 directory
Device: fh/15d Inode: 2533274790421609 Links: 1
Access: (0777/drwxrwxrwx) Uid: ( 1000/ canpool) Gid: ( 1000/ canpool)
Access: 2022-06-06 23:53:17.886556500 +0800
Modify: 2022-06-06 23:53:17.885560400 +0800
Change: 2022-06-06 23:53:17.885560400 +0800
Birth: -
备注:细心的读者可能会发现目录大小都是 4K,实际上这只是凑巧,但目录大小基本上都是 block 大小的倍数,下文会介绍。
由于“Linux 一切皆文件”,那么用户在使用 Linux 的过程中,就难免要跟各种文件打交道。如果 Linux 中的各种库、头文件、命令、配置文件等都自由放置的话,那么这将是灾难性的,将会给应用和管理造成诸多困扰。于是 FHS(Filesystem Hierarchy Standard,文件系统层次标准)就应运而生了,下面是遵循 FHS 标准的大致模样。
canpool@DESKTOP-ODCM7SC:~/projects/canpool/shcanpool$ ls /
bin dev home lib lib64 media opt root sbin srv tmp var
boot etc init lib32 libx32 mnt proc run snap sys usr
FHS 标准使得众多的Linux发布包有了可以遵循的标准,使得软件和用户可以预测已经安装了的文件和目录的位置。它定义了如下的内容:
FHS 实际上仅是规范在根目录(/)下面各个主要目录应该放什么样的文件。FHS定义了两层规范:
由于 FHS 仅是定义出最上层(/)及子层(/usr, /var)的目录内容应该要放置的文件数据,因此,在其他子目录层级内,就可以随开发人员自行配置了。
下面是对一些必要目录的介绍:
目录 | 介绍 |
---|---|
/bin | bin 是 Binaries (二进制文件) 的缩写, 这个目录存放着最经常使用的命令。 |
/boot | 这里存放的是启动 Linux 时使用的一些核心文件,包括一些连接文件以及镜像文件。 |
/dev | dev 是 Device(设备) 的缩写, 该目录下存放的是 Linux 的外部设备,在 Linux 中访问设备的方式和访问文件的方式是相同的。 |
/etc | etc 是 Etcetera(等等) 的缩写,这个目录用来存放所有的系统管理所需要的配置文件和子目录。 |
/home | 用户的主目录,在 Linux 中,每个用户都有一个自己的目录,一般该目录名是以用户的账号命名的,如上图中的 alice、bob 和 eve。 |
/lib | lib 是 Library(库) 的缩写这个目录里存放着系统最基本的动态连接共享库,其作用类似于 Windows 里的 DLL 文件。几乎所有的应用程序都需要用到这些共享库。 |
/lost+found | 这个目录一般情况下是空的,当系统非法关机后,这里就存放了一些文件。 |
/media | linux 系统会自动识别一些设备,例如U盘、光驱等等,当识别后,Linux 会把识别的设备挂载到这个目录下。 |
/mnt | 系统提供该目录是为了让用户临时挂载别的文件系统的,我们可以将光驱挂载在 /mnt/ 上,然后进入该目录就可以查看光驱里的内容了。 |
/opt | opt 是 optional(可选) 的缩写,这是给主机额外安装软件所摆放的目录。比如你安装一个ORACLE数据库则就可以放到这个目录下。默认是空的。 |
/proc | proc 是 Processes(进程) 的缩写,/proc 是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,这个目录是一个虚拟的目录,它是系统内存的映射,我们可以通过直接访问这个目录来获取系统信息。这个目录的内容不在硬盘上而是在内存里,我们也可以直接修改里面的某些文件,比如可以通过下面的命令来屏蔽主机的ping命令,使别人无法ping你的机器:echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all |
/root | 该目录为系统管理员,也称作超级权限者的用户主目录。 |
/sbin | s 就是 Super User 的意思,是 Superuser Binaries (超级用户的二进制文件) 的缩写,这里存放的是系统管理员使用的系统管理程序。 |
/selinux | 这个目录是 Redhat/CentOS 所特有的目录,Selinux 是一个安全机制,类似于 windows 的防火墙,但是这套机制比较复杂,这个目录就是存放selinux相关的文件的。 |
/srv | 该目录存放一些服务启动之后需要提取的数据。 |
/sys | 这是 Linux2.6 内核的一个很大的变化。该目录下安装了 2.6 内核中新出现的一个文件系统 sysfs 。sysfs 文件系统集成了下面3种文件系统的信息:针对进程信息的 proc 文件系统、针对设备的 devfs 文件系统以及针对伪终端的 devpts 文件系统。该文件系统是内核设备树的一个直观反映。当一个内核对象被创建的时候,对应的文件和目录也在内核对象子系统中被创建。 |
/tmp | tmp 是 temporary(临时) 的缩写这个目录是用来存放一些临时文件的。 |
/usr | usr 是 unix shared resources(共享资源) 的缩写,这是一个非常重要的目录,用户的很多应用程序和文件都放在这个目录下,类似于 windows 下的 program files 目录。 |
/usr/bin | 系统用户使用的应用程序。 |
/usr/sbin | 超级用户使用的比较高级的管理程序和系统守护程序。 |
/usr/src | 内核源代码默认的放置目录。 |
/var | var 是 variable(变量) 的缩写,这个目录中存放着在不断扩充着的东西,我们习惯将那些经常被修改的目录放在这个目录下。包括各种日志文件。 |
/run | 是一个临时文件系统,存储系统启动以来的信息。当系统重启时,这个目录下的文件应该被删掉或清除。如果你的系统上有 /var/run 目录,应该让它指向 run。 |
文件名路径分为绝对路径和相对路径两种:
路径在文件系统中是一个很关键的索引,标识着文件所在的位置,文件的底层操作也要依赖它的路径。
Linux 中的文件属性和内容,实际上是通过文件系统进行管理的。
Linux 内核源码目录 fs 下存在几十种文件系统,如下是 linux-5.13.5 下的文件系统介绍:
9p configfs ext2 hugetlbfs nfsd qnx4 ufs
adfs cramfs ext4 iomap nilfs2 qnx6 unicode
affs crypto f2fs isofs nls quota vboxsf
afs debugfs fat jbd2 notify ramfs verity
autofs devpts freevxfs jffs2 ntfs reiserfs xfs
befs dlm fscache jfs ocfs2 romfs zonefs
bfs ecryptfs fuse kernfs omfs squashfs zzzz
btrfs efivarfs gfs2 lockd openpromfs sysfs
cachefiles efs hfs minix orangefs sysv
ceph erofs hfsplus netfs overlayfs tracefs
cifs exfat hostfs nfs proc ubifs
coda exportfs hpfs nfs_common pstore udf
文件系统 | 简介 |
---|---|
vfs | VFS(Virtual Filesystem Switch)称为虚拟文件系统或虚拟文件系统转换,是一个内核软件层,在具体的文件系统之上抽象的一层,用来处理与Posix文件系统相关的所有调用,表现为能够给各种文件系统提供一个通用的接口,使上层的应用程序能够使用通用的接口访问不同文件系统,同时也为不同文件系统的通信提供了媒介 |
9p | 9P (Plan 9 File System Protocol),Plan 9 和 Inferno 使用的分布式文件系统协议 |
adfs | ADFS (Acorn Disc Filing System),Acron磁盘文件系统,RiscOS 操作系统中使用的标准文件系统 |
affs | Amiga 计算机使用的文件系统 |
afs | AFS (Andrew File System) 分布式文件系统 |
autofs | 自动挂载文件系统 |
befs | BeOS 使用的文件系统 |
bfs | 数据库文件系统 |
btrfs | BTRFS (B-tree, Butter FS, Better FS),是一款写时复制(CoW, Copy-on-Write)文件系统 |
cachefiles | 网络缓存文件系统 |
ceph | Ceph是加州大学Santa Cruz分校的Sage Weil(DreamHost的联合创始人)专为博士论文设计的新一代自由软件分布式文件系统 |
cifs | 通用Internet文件系统(Common Internet File System,简写为CIFS),用于访问符合SNIA CIFS标准的服务器。CIFS对SMB协议进行改进和标准化(SMB协议可用于在Linux和Windows之间共享文件),是一种虚拟文件系统 |
coda | Coda文件系统(Coda File System)适用于分布式网络环境。它是1987年在卡耐基梅隆大学以AFS2为原型开发出来的。Linux Virtual Server就采用了Coda文件系统 |
configfs | configfs 是一个基于内存的文件系统,它提供了与sysfs相反的功能。sysfs 是一个基于文件系统的内核对象视图,而configfs 是一个基于文件系统的内核对象管理器(或称为config_items) |
cramfs | CRAMFS文件系统是专门针对闪存设计的只读压缩的文件系统 |
crypto | 加密文件系统 |
debugfs | 调试文件系统 |
devpts | 设备文件系统 |
dlm | 内核或用户空间的通用分布式锁管理器 |
ecryptfs | eCryptfs是Linux平台下的企业文件加密系统。它起源于Erez Zadok’s Cryptfs,通过FiST框架实现层叠式文件系统 |
efivarfs | 从 linux 3.8 开始,内核中添加的一个新的文件系统,efivarfs文件系统是为了解决在sysfs中使用entries来维EFIvariables的缺点 |
efs | EFS是用于非ISO9660 CD-ROM的较旧文件系统 |
erofs | EROFS(增强型只读文件系统)华为的EROFS超级文件系统,Linux内核5.4中也正式将华为EROFS超级文件系统合入了主线 |
exfat | exFAT(Extended File Allocation Table File System,扩展FAT,即扩展文件分配表)是Microsoft在Windows Embeded 5.0 以上(包括Windows CE 5.0、6.0、Windows Mobile5、6、6.1)中引入的一种适合于闪存的文件系统,为了解决FAT32等不支持4G及其更大的文件而推出。对于闪存,NTFS文件系统不适合使用,exFAT更为适用。对于磁盘则不太适用 |
exportfs | 用于文件系统导出支持 |
ext2 | 专门为Linux系统设计的文件系统,具有速度快和CPU占用率低等选特点。既可以用于标准的块设备,也可以应用到移动存储介质上。ext2不包括日志功能 |
ext4 | 与ext2相比包含了日志功能,维护了最近更改的源数据(和文 件有关的信息,包括权限、所有者、创建时间、访问时间等)的记录,如果源数据由于非法关机等原因遭到破坏,文件系统将不能正常工作。通过ext4的日志系 统,可以对源数据进行适当的恢复。此外,ext3的日志功能可使硬盘读写头的移动达到最佳化 |
f2fs | F2FS (Flash Friendly File System) 是专门为基于 NAND 的存储设备设计的新型开源 flash 文件系统。特别针对NAND 闪存存储介质做了友好设计。F2FS 于2012年12月进入Linux 3.8 内核。F2FS仅支持Linux操作系统 |
fat | FAT是文件配置表(英语:File Allocation Table,首字母缩略字:FAT),是一种由微软发明并拥有部分专利的文件系统,供MS-DOS使用,也是所有非NT核心的微软窗口使用的文件系统 |
freevxfs | FreeVxFS是支持VERITAS VxFS(TM)的文件系统 |
fscache | Facebook在Linux上实现硬盘级的Flashcache/Bcache来应对大数据存储的挑战 |
fuse | 用户空间文件系统 |
gfs2 | GFS2 是一个基于 GFS的先进的集群文件系统 |
hfs | MAC下常用的混合文件系统 |
hfsplus | HFS Plus 或者HFS+是苹果公司 (Apple)的MAC机所使用的光盘文件系统,为了取代混合文件系统 (Hybrid File System-HFS)。这同时也是数字音乐播放器iPOD用的格式 |
hostfs | 主机文件系统 |
hpfs | HPFS一般指高性能文件系统。高性能文件系统 (High Performance File System ,HPFS),HPFS是Microsoft的LAN Manager中的文件系统,同时也是IBM的LAN Server和OS/2产品 |
hugetlbfs | 内存大页面支持特殊文件系统 |
iomap | 输入输出映射 |
isofs | 从High Sierra(CD-ROM使用的最初标准)发展而来的光盘文件系统,是CD-ROM的标准文件系统 |
jbd2 | jbd的全拼是journaling block driver 文件系统的日志功能,jbd2是ext4文件系统版本。自从Linux系统引入了Ext4文件系统了,就有一个JBD2为之服务,其实JBD2也可以为其它的文件系统服务,但是目前来说只有Ext4和OCFS2文件系统用它 |
jffs2 | JFFS2的全名为Journalling Flash File System Version2(闪存日志型文件系统第2版),其功能就是管理在MTD设备上实现的日志型 |
文件系统 | |
jfs | JFS( JOURNAL FILE SYSTEM),一种字节级日志文件系统,借鉴了数据库保护系统的技术,以日志的形式记录文件的变化 |
kernfs | 内核虚拟文件系统 |
lockd | Linux锁管理器 |
minix | MINIX操作系统中的原生文件系统,只支持长度为30字符以下的文件名 |
netfs | |
nfs | 网络文件系统,英文Network File System(NFS),是由SUN公司研制的UNIX表示层协议(presentation layer protocol),能使使用者访问网络上别处的文件就像在使用自己的计算机一样 |
nfs_common | NFS客户端和服务器共享的Linux文件系统例程 |
nfsd | NFS服务器支持 |
nilfs2 | NILFS2 是一种 Log-Structure File System。最早的 Log-Structure File System 由 TCL/TK 语言的创始人 John Kenneth Ousterhout 在 Sprite 操作系统中实现。其基本思想是将底层设备当作一种只能追加写 (append) 的设备。将文件修改顺序追加写入磁盘,而不覆盖旧数据。顺序写入能避免很多寻道 (seek) 操作。seek 是一种机械操作,很难提高速度。因此减少 seek 能极大提高文件系统的写效率 |
nls | 文件系统本地语言支持 |
notify | 文件系统变化通知机制 |
ntfs | NTFS文件系统最早出现于1993年的Windows NT操作系统中,它的出现大幅度地提高了微软原来的FAT文件系统的性能 |
ocfs2 | OCFS1问世于2002年10月,它是Oracle公司为了让RAC用户避免必须要与裸设备打交道而开发出来的。这个文件系统用来存放数据库相关的文件,比如数据文件,控制文件,在线日志文件,归档日志文件等。OCFS2是下一代的ORACLE集群文件系统,它已经被设计成为一种通用的文件系统,它不仅可以存放数据库相关的文件,还可以存放oracle二进制文件(指Oracle_home下的软件文件-译者注)和配置文件,从而使RAC的管理更加轻松 |
omfs | OMFS是由SonicBlue公司创建的用于ReplayTV DVR和MP3 player的文件系统 |
openpromfs | Linux Sun Openprom文件系统 |
orangefs | OrangeFS 分布式文件系统 |
overlayfs | Overlayfs是一种类似aufs的一种堆叠文件系统,于2014年正式合入Linux-3.18主线内核,目前其功能已经基本稳定(虽然还存在一些特性尚未实现)且被逐渐推广,特别在容器技术中更是势头难挡 |
proc | 基于内存的伪文件系统,不占用外存空间,只是以文件的方式为访问Linux内核数据提供接口。由于proc文件系统是虚拟的,所以无需挂载。用户和应用程序可以通过/proc得到系统的运行信息,并可以改变内核的某些参数 |
pstore | 主要用于存储内核异常时的log信息。实现方式是,管理一块“非易失性的存储空间”,如不断电的RAM或外部存储,当系统异常时,将log信息写到Pstore管理的存储空间,直到下一次系统正常时,在将log读出来,以文件形式提供给用户使用 |
qnx4 | 黑莓(BlackBerry QNX)操作系统的实时文件系统 |
qnx6 | 黑莓(BlackBerry QNX)操作系统的实时文件系统 |
quota | 设置磁盘限制使用情况(也称为磁盘配额)。目前,它适用于ext2,ext3,ext4,jfs,ocfs2和reiserfs文件系统 |
ramfs | ramfs是Linux下一种基于RAM做存储的文件系统 |
reiserfs | ReiserFS是一个非常优秀的文件系统。也是最早用于Linux的日志文件系统之一。ReiserFS的开发者非常有魄力,整个文件系统完全是从头设计的。目前,ReiserFS可轻松管理上百G的文件系统,这在企业级应用中非常重要 |
romfs | ROMFS是一种简单的只读文件系统,主要是用来当做初始文件系统来使用的,在嵌入式linux或是uclinux中通常使用这种文件系统来作为引导系统,甚至uclinux有时就直接把ROMFS作为其根文件系统,而不是将其作为系统启动中的过渡文件系统 |
squashfs | SquashFS是一套基于Linux内核使用的压缩只读文件系统。该文件系统能够压缩系统内的文档,inode以及目录,文件最大支持2^64字节。SquashFS是基于GPL协议的开源软件。初始的版本使用gzip压缩,2.6.34版本Linux内核增加了支持LZMA和LZO压缩,并且在2.6.38内核版本上增加支持XZ压缩 |
sysfs | sysfs是一个基于内存的文件系统,它的作用是将内核信息以文件的方式提供给用户程序使用。sysfs可以看成与proc,devfs和devpty同类别的文件系统,该文件系统是虚拟的文件系统,可以更方便对系统设备进行管理。它可以产生一个包含所有系统硬件层次视图,与提供进程和状态信息的proc文件系统十分类似 |
sysv | UNIX中最常用的system V文件系统 |
tracefs | tracefs是用于激活跟踪的伪文件系统 |
ubifs | 无序区块镜像文件系统(Unsorted Block Image File System, UBIFS)是用于固态存储设备上,并与LogFS相互竞争,作为JFFS2的后继文件系统之一。UBIFS由IBM与Nokia的工程师Thomas Gleixner,Artem Bityutskiy所设计,开发于2007年,并于2008年10月第一次加入Linux核心2.6.27版。专门为了解决MTD(Memory Technology Device)所遇到的瓶颈。由于Nand Flash容量的暴涨,YAFFS等皆无法操控大的Nand Flash空间。UBIFS通过子系统UBI处理与MTD device之间的动作。与JFFS2一样,UBIFS 建构于MTD之上,因而与一般的块设备不兼容。UBIFS在设计与性能上均较YAFFS2、JFFS2更适合MLC NAND FLASH。例如:UBIFS 支持 凑数(write-back), 其写入的数据会被缓存起来, 直到有必要写入时才写到flash, 这样大大降低分散小区块数量,并提高读写效率。UBIFS文件系统目录存储在flash上,UBIFS 挂载时不需要扫描整个存储器来重新创建文件目录。支持实时压缩,而且可选择性压缩部份文件。另外UBIFS使用日志(journal),可减少对flash index的更新频率。UBIFS 是 Nokia N900 智能手机上的默认文件系统 |
udf | UDF 是统一光盘格式 (Universal Disc Format)的英文缩写 ,由国际标准化组织于1996 年制定的通用光盘文件系统 。它采用标准的包刻录技术 (PW ,Packet Writing)来简化刻录机的使用。UDF文件系统使用户可以如同操作硬盘那样来使用CD-RW刻录机 |
ufs | UFS是UNIX文件系统的简称,它来源于4.3Tahoe发行版中提供的BSD Fat Fast File System(FFS)系统,属于FFS的演化版本。UFS几乎是大部分UNIX类操作系统默认的基于磁盘的文件系统,包括Solaris、Free BSD、Open BSD、Net BSD、HP-UX等,甚至Apple的OS X也能支持UFS文件系统 |
unicode | Unicode是一种宽字符集,用来支持UTF-8字符 |
vboxsf | VirtualBox虚拟机来宾共享文件夹支持 |
verity | 基于文件的只读真实性效验 |
xfs | XFS一种高性能的日志文件系统,最早于1993年,由Silicon Graphics为他们的IRIX操作系统而开发,是IRIX 5.3版的默认文件系统。2000年5月,Silicon Graphics以GNU通用公共许可证发布这套系统的源代码,之后被移植到Linux 内核上。XFS 特别擅长处理大文件,同时提供平滑的数据传输。XFS 最初是由 Silicon Graphics,Inc. 于 90 年代初开发的。那时,SGI 发现他们的现有文件系统(existing filesystem,EFS)正在迅速变得不适应当时激烈的计算竞争。为解决这个问题,SGI 决定设计一种全新的高性能 64 位文件系统,而不是试图调整 EFS在先天设计上的某些缺陷。因此,XFS 诞生了,并于 1994 年随 IRIX 5.3 的发布而应用于计算 |
zonefs | 西部数据开发的Zonefs已经确定要在Linux 5.6内核中登场了,这并不是一个传统意义上面的通用文件系统(比如Ext4、NTFS),而是一个针对特定硬件设备的文件系统,它为分区式存储设备进行了高度的优化。SMR HDD(叠瓦式硬盘)有一个毛病,它只有在顺序写入时才能正常发挥性能,一旦要进行数据覆盖写入,就需要改写这块数据所在区块内的所有磁道,造成严重的性能下降。传统的文件系统不会因为SMR HDD的特性而特别对待它,而Zonefs就是西部数据对SMR HDD和其他分区式存储设备提出的优化方案。它在传统的块设备之外提供了另一种映射,将分区式存储设备上面的每一个区都映射成一个单独的文件,这带来的好处就是应用程序无需修改太多的代码即可利用上分区式存储设备的优点,很多原本要自己做的控制都被集成在Zonefs里面了,不用开发者来动手了,这大大减少了使用分区式存储设备的迁移工作量 |
文件 /proc/filesystems 记录了 Linux 内核支持的文件系统列表(包括已编译到内核或当前加载其内核模块的文件系统):
canpool@DESKTOP-ODCM7SC:~$ cat /proc/filesystems
nodev sysfs
nodev rootfs
nodev bdev
nodev proc
nodev tmpfs
nodev binfmt_misc
nodev debugfs
nodev sockfs
nodev usbfs
nodev pipefs
nodev anon_inodefs
nodev devpts
ext3
ext2
ext4
nodev ramfs
nodev hugetlbfs
vfat
msdos
iso9660
fuseblk
nodev fuse
nodev fusectl
yaffs
yaffs2
nodev overlayfs
nodev overlay
nodev mqueue
如果文件系统标有“nodev”,则不需要挂载块设备。例如:虚拟文件系统、网络文件系统等都是“nodev”的例子。
我们都知道磁盘分区完毕后还需要进行格式化(format),之后操作系统才能够使用这个文件系统。
格式化,顾名思义,就是按一定格式进行初始化。因为每种操作系统所设定的文件属性/权限并不相同,为了存放这些文件所需的数据,因此就需要将分区槽进行格式化,以成为操作系统能够利用的文件系统格式。
由此我么也能够知道,每种操作系统能够使用的文件系统并不相同。举例来说,windows 98 以前的微软操作系统主要利用的文件系统是 FAT(或FAT16),windows 2000 以后的版本使用 NTFS 文件系统。至于 Linux 的正统文件系统则为 Ext2(Linux second extended file,ext2fs)。
传统的磁盘与文件系统之应用中,一个分区槽就是只能够被格式化成为一个文件系统,所以我们可以说一个 filesystem 就是一个 partition。但是由于新技术的利用,例如我们常听到的 LVM(Logical Volume Manager,逻辑卷管理)与软件磁盘阵列(software raid),这些技术可以将一个分区槽格式化为多个文件系统(LVM),也能够将多个分区槽合成一个文件系统(LVM,RAID)。所以说,目前我们在格式化时已经不再说成针对 partition 来格式化了,通常我们可以称呼一个可被挂载的数据为一个文件系统而不是一个分区槽。
我们知道文件除了文件实际内容外,通常还还有非常多的属性,例如 Linux 操作系统的文件权限(rwx)与文件属性(拥有者、群组、时间参数等)。文件系统通常会将这两部分的数据分别存放在不同的区块中,权限与属性放到 inode 节点中,至于实际数据则放到 block 区块中。另外,还有一个超级区块(superblock)会记录整个文件系统的整体信息,包括 inode 与 block 的总量、使用量、剩余量等。
每个 indoe 与 block 都有编号,至于这三个数据的意义可以简略说明如下:
由于每个 inode 与 block 都有编号,而每个文件都会占用一个 inode,inode 内则有文件数据放置的 block 编号。因此,我们可以知道的是,如果能够找到文件的 inode 的话,那么自然就会知道这个文件所放置数据的 block 编号,当然也就能够读出该文件的实际数据了。这是个比较有效率的做法,因为如此一来我们的磁盘就能够在短时间内读取出全部的数据,读写的效能都比较好。
我们将 indoe 与 block 用图解来说明一下,如果下图所示,文件系统先格式化出 inode 与 block,假设某一个文件的属性和权限数据是放置到 inode 4 号,而这个 indoe 记录了文件数据的实际放置点为 2、7、13、15 这四个 block 号码,此时我们的操作系统就能够据此来排列磁盘的阅读顺序,可以一口气将四个 block 内容读出来。那么数据的读取就如同下图中的箭头所指定的模样:
这种数据存取的方法我们称为索引文件系统(indexed allocation)。下图是 FAT 文件系统的数据读取方式,FAT 格式并没有 indoe,所以没办法将文件的所有 block 在一开始就读取出来。每个 block 编号都记录在前一个 block 当中,如下所示:
上图中我们假设文件的数据依序写入 1->7->4->15 这四个 block 中,但这个文件系统没办法一口气就知道四个 block 的编号,得一个一个的将 block 读出后,才会知道下一个 block 在何处。如果同一个文件数据写入的 block 分散的太过厉害时,则我们的磁盘读取头将无法在磁盘转一圈就读到所有的数据,因此磁盘就会多转好几圈才能完整的读取到这个文件的内容。
标准的 Linux 文件系统 Ext2 就是使用 inode 为基础的文件系统。
一般文件系统一开始就将 inode 与 block 规划好了,除非重新格式化(或者利用 resize2fs 等指令变更文件系统大小),否则 inode 与 block 固定后就不再变动。但是仔细考虑一下,如果我们的文件系统高达数百 GB 时,那么将所有的 indoe 与 block 通通放置在一起将是很不明智的决定,因为 inode 与 block 的数量太庞大,不容易管理。
因此 Ext2 文件系统在格式化的时候基本上是区分为多个区块群组(block group)的,每个区块群组都有独立的 inode/block/superblock 系统。Ext2 格式化后如下所示:
在整体的规划当中,文件系统最前面有一个启动扇区(boot sector),这个启动扇区可以安装开机管理程序,这是个非常重要的设计,因为如此一来我们就能够将不同的开机管理程序安装到个别的文件系统最前端,而不用覆盖整个磁盘唯一的 MBR(Master Boot Record,主引导记录,是采用MBR分区表的硬盘的第一个扇区),这样也才能够制作出多重引导的环境。
data block 是用来放置文件内容数据的地方,在 Ext2 文件系统中所支持的 block 大小有 1K,2K,4K 三种。在格式化时 block 的大小就固定了,且每个 block 都有编号,以方便 inode 记录。不过要注意的是,由于 block 大小的差异,会导致该文件系统能够支持的最大磁盘容量与最大单一文件容量并不相同。因为 block 大小而产生的 Ext2 文件系统限制如下:
Block大小 | 1KB | 2KB | 4KB |
---|---|---|---|
最大单一文件限制 | 16GB | 256GB | 2TB |
最大文件系统总容量 | 2TB | 8TB | 16TB |
除此之外,Ext2 文件系统的 block 还有一些基本限制如下:
由于每个 block 仅能容纳一个文件的数据而已,因此如果你的文件都非常小,但是你的 block 在格式化时却选用最大的 4K 时,可能会产生一些容量的浪费。但也不能就此将 block 选为 1K,因为如果 block 较小的话,那么大型文件将会占用数量更多的 block,而 inode 也要记录更多的 block 编号,此时将可能导致文件系统不良的读写效能。事实上,现在的磁盘容量都太大了,基本都选择 4K 的 block 大小。
inode 记录的文件数据至少包含如下内容:
inode 的数量与大小也是在格式化时就已经固定了,除此之外 inode 还有一些特点:
下面分析一下 Ext2 的 inode/block 与文件大小的关系。inode 要记录的数据非常多,但其大小仅有 128B,而 inode 记录一个 block 编号需要 4B。假设一个文件有 400MB 且每个 block 为 4K 时,那么至少也要有十万笔 block 编号的记录,而一个 inode 根本记录不了这么多 block 编码。为此,Ext2 将 inode 记录 block 编号的区域定义为 12个直接,1个间接,1个双间接与1个三间接记录区。下图是 inode 的大概结构图:
上图最左边为 inode 本身(128B),里面有 12 个直接指向 block 编号,这 12 个记录就能够直接取得 block 编号。至于所谓的间接就是再拿一个 block 来当做记录 block 编号的记录区,如果文件太大时,就会使用间接的 block 来记录编号。如上图中间接只是拿一个 block 来记录额外的编号而已。同理,如果文件持续长大,那么就会利用所谓的双间接,第一个 block 仅再指出下一个记录编号的 block 在哪里,实际记录的在第二个 block 当中。以此类推,三间接就是利用第三层 block 来记录编号,即所谓级联。
我们以 1K 大小的 block 来计算一下 inode 最多能够记录多少个 block:
12直接:12 * 1K = 12K
由于是直接指向,所以总共可记录 12 个 block 编号。
一间接:256 * 1K = 256K
每笔 block 编号的记录需要 4B,因此 1K 大小的 block 能够记录 256 个 block 编号。
双间接:256 * 256 * 1K = 256^2K
第一层 block 会指定 256 个第二层,每个第二层可以记录 256 个 block 编号。
三间接:256 * 256 * 256 * 1K = 256^3K
第一层 block 会指定 256 个第二层,每个第二层可以指定 256 个第三层,每个第三层可以指定 256 个 block 编号。
总额:将直接、间接、双间接、三间接加总,得到 12K + 256K + 256256K + 256256*256K = 16G
此时,我们知道当文件系统将 block 格式化为 1K 大小时,能够容纳的最大文件为 16G,比较一下上节文件系统限制表的结果可发现是一致的。但这个方法不能用在 2K,4K 大小的计算中,因为大于 2K 的 block 将会受到 Ext2 文件系统本身的限制,所以计算的结果会不太符合。
inode bitmap 记录使用与未使用的 inode 编号。
新增文件时,需要用到空的 block,从 block bitmap 中可以知道哪些 block 是空的,因此系统能够很快速的找到可使用的空间来处理文件。
同样的,如果你删除某个文件时,那么那些文件原本占用的 block 编号就得要释放出来,此时在 block bitmap 当中相对应到该 block 编号的标志就得要修改称为“未使用中”。
这个区段可以描述每个 block group 的开始与结束的 block 编号,以及说明每个区段(superblock,bitmap,inodemap,data block)分别介于哪一个 block 编号之间。
superblock 是记录整个 filesystem 相关信息的地方,记录的信息主要有:
superblock 因记录了文件系统的基本信息,所以非常重要。每个 block group 都可能含有 superblock,但是我们也说一个文件系统应该仅有一个 superblock。事实上,除了第一个 block group 内会含有 superblock 之外,后续的 block group 不一定含有 superblock,若含有 superblock 则该 superblock 主要是作为第一个 block group 内 superblock 的备份,这样当 第一个 superblock 出问题时,其它 superblock 就可以用来救援。
Linux 系统下,每个文件(不管是一般文件,还是目录文件)都会占用一个 inode,且可依据文件内容的大小来分配多个 block 给该文件使用。目录的内容在记录文件名,一般文件才是实际记录数据内容的地方。接下来,介绍目录与文件在文件系统当中是如何记录数据的。
当我们在 Linux 下的 ext2 文件系统中建立一个目录时,文件系统会分配一个 inode 与至少一块 block 给该目录。其中,inode 记录该目录的相关权限与属性,并记录分配到的 block 编号;而 block 则是记录在这个目录下的文件名与该文件名占用的 inode 编号数据。也就是说目录所占用的 block 内容在记录如下的信息:
inode number | 文件名 |
---|---|
2814749767128867 | alias.sh |
3096224743839528 | array.sh |
… | … |
可以通过ls -li
查看目录内的文件所占用的 inode 编号,如下所示:
canpool@DESKTOP-ODCM7SC:~/projects/canpool/shcanpool/src/utils$ ls -li
total 8
2814749767128867 -rwxrwxrwx 1 canpool canpool 716 Jun 6 23:53 alias.sh
3096224743839528 -rwxrwxrwx 1 canpool canpool 787 Jun 6 23:53 array.sh
2251799813711171 -rwxrwxrwx 1 canpool canpool 167 Jun 6 23:53 declare.sh
2814749767128871 -rwxrwxrwx 1 canpool canpool 788 Jun 6 23:53 dict.sh
2814749767128874 -rwxrwxrwx 1 canpool canpool 458 Jun 6 23:53 method.sh
2814749767128905 -rwxrwxrwx 1 canpool canpool 356 Jun 6 23:53 string.sh
3377699720553796 -rwxrwxrwx 1 canpool canpool 488 Jun 6 23:53 type.sh
由目录记录数据的特点而知,当使用ll
时,出现的目录几乎都是 block 大小的倍数。
当我们在 Linux 下的 ext2 文件系统中建立一个一般时,文件系统会分配一个 inode 与相对于该文件大小的 block 数量给该文件。
例如:假设我的一个 block 为 4K,而我要建立一个 100K 的文件,那么 linux 将分配一个 inode 与 25 个 block 来储存该文件。但同时请注意,由于 inode 仅有 12 个直接指向,因此还要多一个 block 来作为区块号码的记录。
目录的 inode 本省并不记录文件名,文件名的记录是在目录的 block 当中。因此新增/删除/更名文件与目录的写(w)权限有关。因为文件名是记录在目录的 block 当中,因此当要读取某个文件时,就务必会经过目录的 inode 与 block,然后才能够找到那个待读取文件的 inode 编号,最终才会读到正确的文件的 block 内的数据。
由于目录树是由根目录开始读起,因此系统透过挂载的信息可以找到挂载点的 inode 号码,此时就能够得到根目录的 inode 内容,并依据该 inode 读取根目录的 block 内的文件名数据,再一层一层的往下读到正确的文件名。
以读取 /etc/passwd 这个文件为例,讲解系统是如何读取的:
[maminjie@fedora ~]$ ll -di / /etc /etc/passwd
256 dr-xr-xr-x. 1 root root 158 May 5 05:27 /
270 drwxr-xr-x. 1 root root 4606 Jun 6 21:17 /etc
153255 -rw-r--r--. 1 root root 2475 May 31 20:47 /etc/passwd
/ 的 inode
透过挂载点的信息找到 inode 号码为 256 的根目录 inode,且 inode 规范的权限让我们可以读取该 block 的内容(有r与x)。
/ 的 block
经过上个步骤取得 block 号码,并找到该内容中 etc/ 目录的 inode 号码(270)。
etc/ 的 inode
读取 270 号 inode 得知具有 r 与 x 的权限,因此可以读取 etc/ 的 block 内容。
etc/ 的 block
经过上个步骤取得 block 号码,并找到该内容中 passwd 文件的 inode 号码(153255)。
passwd 的 inode
读取 153255 号 inode 得知具有 r 的权限,因此可以读取 passwd 的 block 内容。
passwd 的 block
最后将该 block 内容的数据读出来。
以上只是简单模拟层层读取内容背后的逻辑。
上面仅谈到读取,当新建一个文件时,文件系统的行为是:
一般来说,我们将 inode table 与 data block 称为数据存放区域,其它例如 superblock、block bitmap、inode bitmap 等区段就被称为 metadata(中介数据)。因为 superblock、inode bitmap 及 block bitmap 的数据是经常变动的,每次新增、移除、编辑时都可能会影响到这三个部分的数据,因此才被称为中介数据。
在一般正常的情况下,上述的新增动作当然可以顺利的完成。但是,如果文件在写入文件系统时,因为不知名原因导致系统中断(例如突然断电,系统发生错误等),所写入的数据仅有 inode table 即 data block,最后一个同步更新中介数据的步骤没有完成,此时就会发生 metadata 的内容与实际数据存放区产生不一致的情况。
早期的 ext2 文件系统中,如果发生这个问题,那么系统在重新启动的时候,就会由 superblock 当中记录的 valid bit(是否有挂载)与 filesystem stat(clean与否)等状态来判断是否强制进行数据一致性的检查(检查程序为e2fsck)。
不过,这样的检查很费时,所以后来就出现了日志式文件系统。
为了避免上述提到的文件系统不一致的情况发生,设计者在文件系统中规划出一个区块,该区块专门记录写入或修订文件,如下:
在这样的程序当中,万一数据在记录过程当中发生了问题,那么我们的系统只要去检查日志记录区块,就可以知道哪个文件发生了问题,针对该问题做一致性的检查即可。
目前 ext2 不具备该功能,但是 ext3/ext4 已具备,ext3/ext4 是 ext2 的升级版本,并且可向下兼容 ext2 版本。
每个文件系统都有独立的 inode/block/superblock 等信息,这个文件系统要能够链接到目录树才能被我们使用。将文件系统与目录树结合的动作我们称为挂载。
挂载点一定是目录,该目录为进入该文件系统的入口。因此并不是你有任何文件系统都能使用,必须要挂载到目录树的某个目录后,才能够使用该文件系统。
[maminjie@fedora ~]$ df -hT
Filesystem Type Size Used Avail Use% Mounted on
devtmpfs devtmpfs 4.0M 0 4.0M 0% /dev
tmpfs tmpfs 969M 0 969M 0% /dev/shm
tmpfs tmpfs 388M 1.7M 386M 1% /run
/dev/sda2 btrfs 19G 5.0G 14G 27% /
/dev/sda2 btrfs 19G 5.0G 14G 27% /home
/dev/sda1 ext4 974M 172M 735M 19% /boot
tmpfs tmpfs 969M 212K 968M 1% /tmp
tmpfs tmpfs 194M 144K 194M 1% /run/user/1000
/dev/sr0 iso9660 1.9G 1.9G 0 100% /run/media/maminjie/Fedora-WS-Live-36-1-5
vmhgfs-fuse fuse.vmhgfs-fuse 304G 110G 194G 37% /mnt/hgfs
[maminjie@fedora ~]$ ls -lid / /home /boot
256 dr-xr-xr-x. 1 root root 158 May 5 05:27 /
2 dr-xr-xr-x. 7 root root 4096 Jun 1 04:25 /boot
256 drwxr-xr-x. 1 root root 16 May 31 20:47 /home
通过上面发现 / 和 /home 的 inode 都为 256,因为这两个目录挂载的文件系统相同,且作为文件系统的顶层目录。对于相同文件系统的顶层目录,其 inode 是相同的。
我们知道根目录下的 . 与 … 是相同的东西,因为它们都等效于 /。如果从文件系统的观点来看,同一个文件系统的一个文件占用一个 inode,如果几个文件的 inode 相同,那说明这几个文件内容是一样的,通过查看这三个文件的信息,其 inode 确实也都是一样的,如下所示:
[maminjie@fedora ~]$ ls -lid / /. /..
256 dr-xr-xr-x. 1 root root 158 May 5 05:27 /
256 dr-xr-xr-x. 1 root root 158 May 5 05:27 /.
256 dr-xr-xr-x. 1 root root 158 May 5 05:27 /..
Linux 上的文件系统实际上是由 VFS(Virtual Filesystem Switch,虚拟文件系统)管理的。假设 /dev/sda1 挂载到 / 并使用 ext3 文件系统,/dev/sda2 挂载到 /home 并使用 reiserfs,那么当你读取 /home/canpool/.bashrc 时,并没有特别指定要用的什么文件系统的模块来读取,这就是 VFS 在背后起到的作用。这个 VFS 可以简单用下图说明:
现在我们知道磁盘的整体数据在 superblock 区块中,但是每个文件的容量则在 inode 当中记载。介绍如下两个命令:
简单用法:
df [-ahikHTm] [目录或文件名]
选项与参数:
-a 列出所有的文件系统,包括系统特有的 /proc 等文件系统;
-k 以 KByte 的容量显示各文件系统
—m 以 MByte 的容量显示各文件系统
-h 以人们易读的 GByte,MByte,KByte 等格式自行显示
-H 以 M=1000K 取代 M=1024K 的进位方式
-T 连同该 partition 的 filesystem 名称也列出
-i 不用磁盘容量,而以 inode 的数量来显示
范例一:将系统内所有的 filesystem 列出来
maminjie@fedora ~]$ df
Filesystem 1K-blocks Used Available Use% Mounted on
devtmpfs 4096 0 4096 0% /dev
tmpfs 991364 0 991364 0% /dev/shm
tmpfs 396548 1672 394876 1% /run
/dev/sda2 19921920 5171988 14567052 27% /
/dev/sda2 19921920 5171988 14567052 27% /home
/dev/sda1 996780 175844 752124 19% /boot
tmpfs 991368 212 991156 1% /tmp
tmpfs 198272 148 198124 1% /run/user/1000
/dev/sr0 1970848 1970848 0 100% /run/media/maminjie/Fedora-WS-Live-36-1-5
vmhgfs-fuse 317760508 114861452 202899056 37% /mnt/hgfs
如果 df 没有加任何选项,那么默认会将系统内所有的(不含特殊内存的文件系统与 swap)都以 1 KByte 的容量来列出来。
输出的各列说明如下:
范例二:将容量结果以易读的容量格式显示
[maminjie@fedora ~]$ df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 4.0M 0 4.0M 0% /dev
tmpfs 969M 0 969M 0% /dev/shm
tmpfs 388M 1.7M 386M 1% /run
/dev/sda2 19G 5.0G 14G 27% /
/dev/sda2 19G 5.0G 14G 27% /home
/dev/sda1 974M 172M 735M 19% /boot
tmpfs 969M 212K 968M 1% /tmp
tmpfs 194M 148K 194M 1% /run/user/1000
/dev/sr0 1.9G 1.9G 0 100% /run/media/maminjie/Fedora-WS-Live-36-1-5
vmhgfs-fuse 304G 110G 194G 37% /mnt/hgfs
范例三:将系统内的所有特殊文件格式及名称都列出来
[maminjie@fedora ~]$ df -aT
df: /run/user/1000/doc: Operation not permitted
Filesystem Type 1K-blocks Used Available Use% Mounted on
proc proc 0 0 0 - /proc
sysfs sysfs 0 0 0 - /sys
devtmpfs devtmpfs 4096 0 4096 0% /dev
securityfs securityfs 0 0 0 - /sys/kernel/security
tmpfs tmpfs 991364 0 991364 0% /dev/shm
devpts devpts 0 0 0 - /dev/pts
tmpfs tmpfs 396548 1672 394876 1% /run
cgroup2 cgroup2 0 0 0 - /sys/fs/cgroup
pstore pstore 0 0 0 - /sys/fs/pstore
bpf bpf 0 0 0 - /sys/fs/bpf
/dev/sda2 btrfs 19921920 5171988 14567052 27% /
selinuxfs selinuxfs 0 0 0 - /sys/fs/selinux
systemd-1 - - - - - /proc/sys/fs/binfmt_misc
hugetlbfs hugetlbfs 0 0 0 - /dev/hugepages
mqueue mqueue 0 0 0 - /dev/mqueue
debugfs debugfs 0 0 0 - /sys/kernel/debug
tracefs tracefs 0 0 0 - /sys/kernel/tracing
fusectl fusectl 0 0 0 - /sys/fs/fuse/connections
configfs configfs 0 0 0 - /sys/kernel/config
/dev/sda2 btrfs 19921920 5171988 14567052 27% /home
/dev/sda1 ext4 996780 175844 752124 19% /boot
tmpfs tmpfs 991368 212 991156 1% /tmp
sunrpc rpc_pipefs 0 0 0 - /var/lib/nfs/rpc_pipefs
tmpfs tmpfs 198272 148 198124 1% /run/user/1000
gvfsd-fuse fuse.gvfsd-fuse 0 0 0 - /run/user/1000/gvfs
/dev/sr0 iso9660 1970848 1970848 0 100% /run/media/maminjie/Fedora-WS-Live-36-1-5
vmhgfs-fuse fuse.vmhgfs-fuse 317760508 114861452 202899056 37% /mnt/hgfs
binfmt_misc binfmt_misc 0 0 0 - /proc/sys/fs/binfmt_misc
系统里面其实还有很多特殊的文件系统存在。那些比较特殊的文件系统几乎都是在内存中,例如 /proc 这个挂载点。因此,这些特殊的文件系统都不会占据磁盘空间。
范例四:将 /etc 目录的可用磁盘容量以易读的格式显示
[maminjie@fedora ~]$ df -h /etc
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 19G 5.0G 14G 27% /
在 df 后面加上目录或文件时,df 会自动的分析该目录或文件所在的 partition,并将该 partition 的容量显示出来。
范例五:将目前各个 partition 当中可用的 inode 数量列出
[maminjie@fedora ~]$ df -ih
Filesystem Inodes IUsed IFree IUse% Mounted on
devtmpfs 1.0M 494 1.0M 1% /dev
tmpfs 243K 1 243K 1% /dev/shm
tmpfs 800K 978 800K 1% /run
/dev/sda2 0 0 0 - /
/dev/sda2 0 0 0 - /home
/dev/sda1 64K 449 64K 1% /boot
tmpfs 1.0M 50 1.0M 1% /tmp
tmpfs 49K 145 49K 1% /run/user/1000
/dev/sr0 0 0 0 - /run/media/maminjie/Fedora-WS-Live-36-1-5
vmhgfs-fuse 0 0 0 - /mnt/hgfs
说明:作者的环境是VMware虚拟机,所有比较奇怪,有些 Inodes 列居然是 0。
简单用法:
du [-ahsSkm] 文件或目录名称
选项与参数:
-a 列出所有的文件与目录容量,因为默认仅统计目录下的文件容量而已
-h 以人们易读的 GByte,MByte,KByte 等格式自行显示
-s 列出总量,而不列出每个目录占用的容量
-S 不包括子目录下的总计,与 -s 有点差别
-k 以 KByte 的列出容量
—m 以 MByte 的列出容量
范例一:列出当前目录下的所有文件容量
[maminjie@fedora ~]$ du
...(省略)...
20 ./.cache/gnome-software/fedora-pkgdb-collections
8272 ./.cache/gnome-software/appstream
1268 ./.cache/gnome-software/odrs
4 ./.cache/gnome-software/langpacks
488 ./.cache/gnome-software/flatpak-system-default
...(省略)...
4 ./bin
0 ./work
25848 .
du 不加任何选项是,则分析当前目录下的文件与目录所占用的磁盘空间。但是,实际显示时,仅会显示目录容量(不含文件)。因此,目录有很多文件没有被列出来,所以全部的目录相加不会等于 . 的容量。
此外,输出的数值数据位 1K 大小的容量单位。可以通过du -a
将文件的容量也列出来。
范例二:检查当前目录下每个目录所占用的容量
[maminjie@fedora ~]$ du -sm ./*
1 ./bin
0 ./Desktop
0 ./Documents
0 ./Downloads
0 ./Music
0 ./Pictures
1 ./projects
0 ./Public
0 ./Templates
0 ./Videos
0 ./work
与 df 不一样的是,du 其实会直接到文件系统内去搜寻所有的文件数据。
Linux 下有两种文件链接方式:
简单用法:
ln [-sf] 源文件 目标文件
选项与参数:
-s 如果不见任何参数就进行实体链接,至于 -s 就是符号链接
-f 如果目标文件存在,就主动的将目标文件直接移除再建立
inode 的几个重要信息如下:
也就是说,其实文件名只与目录有关,但是文件内容则与 inode 有关。实体链接只是在某个目录下新增一个文文档名连接到某 inode 编号的关联记录而已。
举个例子来说,假设 a2 是 a1 的实体链接,也就是说这两个文件名链接到同一个 inode,自然这两个文件名的所有相关信息都一样(除了文件名)。实际情况如下:
[maminjie@fedora work]$ echo "1234" > a1
[maminjie@fedora work]$ ll -i a1
825 -rw-rw-r--. 1 maminjie maminjie 5 Jun 12 13:24 a1
[maminjie@fedora work]$ ln a1 a2
[maminjie@fedora work]$ ll -i a1 a2
825 -rw-rw-r--. 2 maminjie maminjie 5 Jun 12 13:24 a1
825 -rw-rw-r--. 2 maminjie maminjie 5 Jun 12 13:24 a2
可以发现这两个文件都链接到 825 这个 inode 号码,所以文件的权限/属性完全一样,而且第二个字段由原来的 1 变成了 2,这个字段称为链接数。读取数据的示意图如下:
上图的意思是,可以通过 1 或 2 的目录之 inode 指定的 block 找到两个不同的文件名,不管使用哪个文件名局可以找到 read 那个 inode 去读取到最终的数据。
一般来说,使用实体链接设定链接文件时,inode 的数目是不会改变的,只是在某个目录下的 block 多写一个关联数据而已。当新增的这笔数据刚好将目录的 block 填满时,就可能会新加一个 block 来记录文件名关联性。
实体链接的限制:不能跨文件系统,不支持链接目录。
符号链接就是建立一个独立的文件,这个文件会让数据的读取执向它链接的那个文件名。
由于只是利用文件来作为指向的动作,所以,当源文件被删除之后,符号链接将打不开,找不到原始文件。
举例,假设 a3 是 a1 的符号链接:
[maminjie@fedora work]$ ln -s a1 a3
[maminjie@fedora work]$ ll -i a1 a3
825 -rw-rw-r--. 2 maminjie maminjie 5 Jun 12 13:24 a1
833 lrwxrwxrwx. 1 maminjie maminjie 2 Jun 12 14:15 a3 -> a1
可以发现两个文件指向不同的 inode 号码,它们是两个独立的文件,而且符号链接文件的重要内容就是它会协商目标文件的文件名。可以发现,链接文件的大小为 2B,因为文件 a1 的名字总共 2 个字符,每个字符占一个字节。操作示意图如下:
通过 3 号 inode 读取到链接的内容仅有文件名,根据文件名链接到正确的目录去取得目标文件的 inode,最终就能够读取到正确的数据。
符号链接建立的文件是一个独立的新文件,所以会占用新的 inode 和 block。
下面介绍几种查看 Linux 系统当前使用的文件系统类型。
1)mount 命令
[maminjie@fedora ~]$ mount
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime,seclabel)
devtmpfs on /dev type devtmpfs (rw,nosuid,seclabel,size=4096k,nr_inodes=1048576,mode=755,inode64)
securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,seclabel,inode64)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,seclabel,gid=5,mode=620,ptmxmode=000)
tmpfs on /run type tmpfs (rw,nosuid,nodev,seclabel,size=396548k,nr_inodes=819200,mode=755,inode64)
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,seclabel,nsdelegate,memory_recursiveprot)
pstore on /sys/fs/pstore type pstore (rw,nosuid,nodev,noexec,relatime,seclabel)
bpf on /sys/fs/bpf type bpf (rw,nosuid,nodev,noexec,relatime,mode=700)
/dev/sda2 on / type btrfs (rw,relatime,seclabel,compress=zstd:1,space_cache=v2,subvolid=257,subvol=/root)
selinuxfs on /sys/fs/selinux type selinuxfs (rw,nosuid,noexec,relatime)
systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=43,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=1623)
hugetlbfs on /dev/hugepages type hugetlbfs (rw,relatime,seclabel,pagesize=2M)
mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime,seclabel)
debugfs on /sys/kernel/debug type debugfs (rw,nosuid,nodev,noexec,relatime,seclabel)
tracefs on /sys/kernel/tracing type tracefs (rw,nosuid,nodev,noexec,relatime,seclabel)
fusectl on /sys/fs/fuse/connections type fusectl (rw,nosuid,nodev,noexec,relatime)
configfs on /sys/kernel/config type configfs (rw,nosuid,nodev,noexec,relatime)
/dev/sda2 on /home type btrfs (rw,relatime,seclabel,compress=zstd:1,space_cache=v2,subvolid=256,subvol=/home)
/dev/sda1 on /boot type ext4 (rw,relatime,seclabel)
tmpfs on /tmp type tmpfs (rw,nosuid,nodev,seclabel,size=991368k,nr_inodes=1048576,inode64)
sunrpc on /var/lib/nfs/rpc_pipefs type rpc_pipefs (rw,relatime)
tmpfs on /run/user/1000 type tmpfs (rw,nosuid,nodev,relatime,seclabel,size=198272k,nr_inodes=49568,mode=700,uid=1000,gid=1000,inode64)
gvfsd-fuse on /run/user/1000/gvfs type fuse.gvfsd-fuse (rw,nosuid,nodev,relatime,user_id=1000,group_id=1000)
/dev/sr0 on /run/media/maminjie/Fedora-WS-Live-36-1-5 type iso9660 (ro,nosuid,nodev,relatime,nojoliet,check=s,map=n,blocksize=2048,uid=1000,gid=1000,dmode=500,fmode=400,iocharset=utf8,uhelper=udisks2)
portal on /run/user/1000/doc type fuse.portal (rw,nosuid,nodev,relatime,user_id=1000,group_id=1000)
vmhgfs-fuse on /mnt/hgfs type fuse.vmhgfs-fuse (rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other)
binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,nosuid,nodev,noexec,relatime)
不加任何参数的 mount 命令,可以列出当前所有文件系统的挂载情况。
2)df 命令
[maminjie@fedora ~]$ df -Th
Filesystem Type Size Used Avail Use% Mounted on
devtmpfs devtmpfs 4.0M 0 4.0M 0% /dev
tmpfs tmpfs 969M 0 969M 0% /dev/shm
tmpfs tmpfs 388M 1.7M 386M 1% /run
/dev/sda2 btrfs 19G 5.0G 14G 27% /
/dev/sda2 btrfs 19G 5.0G 14G 27% /home
/dev/sda1 ext4 974M 172M 735M 19% /boot
tmpfs tmpfs 969M 216K 968M 1% /tmp
tmpfs tmpfs 194M 144K 194M 1% /run/user/1000
/dev/sr0 iso9660 1.9G 1.9G 0 100% /run/media/maminjie/Fedora-WS-Live-36-1-5
vmhgfs-fuse fuse.vmhgfs-fuse 304G 110G 194G 37% /mnt/hgfs
df 的 -T 参数就可以显示文件系统的类型。
3)/etc/fstab 文件
[maminjie@fedora ~]$ cat /etc/fstab
#
# /etc/fstab
# Created by anaconda on Tue May 31 16:16:48 2022
#
# Accessible filesystems, by reference, are maintained under '/dev/disk/'.
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info.
#
# After editing this file, run 'systemctl daemon-reload' to update systemd
# units generated from this file.
#
UUID=89c63ae9-aecb-41dd-ba2f-dc750d94046c / btrfs subvol=root,compress=zstd:1 0 0
UUID=cdb8d723-c331-4809-a62d-4fa54ee5c3b7 /boot ext4 defaults 1 2
UUID=89c63ae9-aecb-41dd-ba2f-dc750d94046c /home btrfs subvol=home,compress=zstd:1 0 0
系统开机时会主动读取 /etc/fstab 这个文件中的内容,根据文件里面的配置挂载磁盘
dumpe2fs 被用来查询 EXT 族文件系统中的 superblock信息。
简单用法:
dumpe2fs [-h] 设备文件名
选项与参数:
-h 仅列出 superblock 的数据,不会列出其它的区段内容
通过上一节我们知道 /dev/sda1 使用的是 ext4 文件系统,通过 dumpe2fs 查询如下:
[maminjie@fedora ~]$ sudo dumpe2fs /dev/sda1
dumpe2fs 1.46.5 (30-Dec-2021)
Filesystem volume name: <none> # 文件系统的名称(不一定会有)
Last mounted on: /boot # 上一次挂载的目录位置
Filesystem UUID: cdb8d723-c331-4809-a62d-4fa54ee5c3b7
Filesystem magic number: 0xEF53 # 上方的 UUID 为 Linux 对设备的定义码
Filesystem revision #: 1 (dynamic) # 下方的 features 为文件系统的特征数据
Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file dir_nlink extra_isize metadata_csum
Filesystem flags: signed_directory_hash
Default mount options: user_xattr acl # 预设在挂载时会主动加上的挂载参数
Filesystem state: clean # 文件系统的状态,clean 是没问题
Errors behavior: Continue
Filesystem OS type: Linux
Inode count: 65536 # inode 的总数
Block count: 262144 # block 的总数
Reserved block count: 13107 # 保留的 block 总数
Overhead clusters: 12949
Free blocks: 205234 # 可用的 block 数量
Free inodes: 65087 # 可用的 inode 数量
First block: 0
Block size: 4096 # 单个 block 的容量大小
Fragment size: 4096
Group descriptor size: 64
Reserved GDT blocks: 127
Blocks per group: 32768
Fragments per group: 32768
Inodes per group: 8192
Inode blocks per group: 512
Flex block group size: 16
Filesystem created: Wed Jun 1 03:51:33 2022
Last mount time: Tue May 31 21:11:04 2022
Last write time: Tue May 31 21:11:04 2022
Mount count: 3
Maximum mount count: -1
Last checked: Wed Jun 1 03:51:33 2022
Check interval: 0 (<none>)
Lifetime writes: 278 MB
Reserved blocks uid: 0 (user root)
Reserved blocks gid: 0 (group root)
First inode: 11
Inode size: 256 # inode 的容量大小
Required extra isize: 32
Desired extra isize: 32
Journal inode: 8
Default directory hash: half_md4
Directory Hash Seed: 201536cc-be9d-478d-b33a-8ee4a8a0b45b
Journal backup: inode blocks
Checksum type: crc32c
Checksum: 0x8646f3be
Journal features: journal_incompat_revoke journal_64bit journal_checksum_v3
Total journal size: 32M # Journal 日志式数据的可供记录总容量
Total journal blocks: 8192
Max transaction length: 8192
Fast commit length: 0
Journal sequence: 0x000002da
Journal start: 1
Journal checksum type: crc32c
Journal checksum: 0xbf8d0b75
Group 0: (Blocks 0-32767) csum 0x6fb8 [ITABLE_ZEROED] # 第一块 block group 位置
Primary superblock at 0, Group descriptors at 1-1 # 主要 superblock
Reserved GDT blocks at 2-128
Block bitmap at 129 (+129), csum 0x4fd8dc24
Inode bitmap at 137 (+137), csum 0x22a4e70b
Inode table at 145-656 (+145) # inode table
28503 free blocks, 7747 free inodes, 12 directories, 7416 unused inodes
Free blocks: 4265-32767 # 底下两行说明剩余的容量
Free inodes: 103, 111-438, 441, 444, 777-8192
Group 1: (Blocks 32768-65535) csum 0x4203 [ITABLE_ZEROED]
Backup superblock at 32768, Group descriptors at 32769-32769
Reserved GDT blocks at 32770-32896
Block bitmap at 130 (bg #0 + 130), csum 0x31275ca2
Inode bitmap at 138 (bg #0 + 138), csum 0x7b1c4465
Inode table at 657-1168 (bg #0 + 657)
32639 free blocks, 8188 free inodes, 4 directories, 8188 unused inodes
Free blocks: 32897-65535
Free inodes: 8197-16384
Group 2: (Blocks 65536-98303) csum 0x56dc [INODE_UNINIT, ITABLE_ZEROED]
... # 后续为更多其它的 block group
dumpe2fs 显示的内容分为两部分:上半部是 superblock 内容,下半部是每个 block group 信息。/dev/sda1 规划的 block 为 4K,第一个 block 编号为 0,且 block group 内的所有信息都以 block 的编号来表示。下面介绍下 Group 0 内的信息:
文件系统与磁盘紧密关联,如果系统里面新增一个磁盘,大概可能会有下面一些动作需要做:
一块磁盘可以被分区成多个分区槽(partition),比如 Windows 上,一颗磁盘可能被分成 C:,D:,E: 等盘符(槽),本节介绍 Linux 下磁盘是如何分区的。
1)命名格式
/dev/sd[a-p]
由于 SATA/USB/SAS 等磁盘接口都是使用 SCSI 模块来驱动的,因此这些接口的磁盘设备文件名都是 /dev/sd[a-p] 的格式。
/dev/vd[a-p]
如果主机为虚拟机,对于 VirtIO 接口的磁盘,磁盘设备文件名采用 /dev/vd[a-p] 的格式。
2)命名顺序
由于 SATA/USB 接口的磁盘没有一定的顺序,命名是根据 Linux 核心侦测到磁盘的顺序进行命名的。
举例:假设 PC 上有两个 SATA 磁盘和一个 USB 磁盘,而主板上有六个 SATA 的插槽。这两个 STAT 磁盘分别安插在主板上的 SATA1,SATA5 插槽上,这三个磁盘在 Linux 中的设备文件名如下:
如果一个磁盘被分区成两个分区槽,那么每个分区槽的设备文件名又是如何命名的呢?
下面主要介绍 MBR (Master Boot Record,主要开机记录区)的方式下的分区命名格式。
MBR 的分区分为:主要分区、延伸分区、逻辑分区。
假设磁盘设备文件名为 /dev/sda,则各分区命名格式如下:
注意:逻辑分区槽的设备名称编号从 5 开始,因为前四个都是保留给主要分区和延伸分区的。
所谓的挂载就是利用一个目录当成进入点,将磁盘分区槽的数据放置在该目录下,也就是说,进入该目录就可以读取该分区槽的意思。这个动作称为挂载,进入点的目录称为挂载点。
由于整个 Linux 系统最重要的是根目录,因此根目录一定需要挂载分区槽的。至于其的目录则可依用户自己的需求来挂载分区槽。以下图作说明:
上图中,假设磁盘被分为两个分区槽,partition 1 挂载到根目录,partition 2 挂载到 /home 目录。也就是说,当数据放在 /home 内的各子目录时,数据是防止到 partition 2 上的,如果不是放在 /home 底下的目录,那么数据就会被放置到 partition 1 上。
目前磁盘分区主要有 MBR 和 GPT 两种格式,这两种格式所使用的分区工具不一样。
lsblk(list block device,列出所有储存装置)命令可以列出系统上的所有磁盘列表。
简单用法:
lsblk [-dfimpt] [device]
选项与参数:
-d 仅列出磁盘本身,并不会列出该磁盘的分区数据
-f 同时列出该磁盘内的文件系统名称
-i 使用 ASCII 的线段输出,不使用复杂的编码(在某些环境下很有用)
-m 同时输出该装置在 /dev 底下的权限数据(rwx 数据)
-p 列出该装置的完成文件名,而不是仅列出最后的名字
-t 列出该磁盘装置的详细数据,包括磁盘队列机制、预读写的数据量大小等
范例一:列出本系统下的所有磁盘与磁盘内的分区信息
[maminjie@fedora home]$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 20G 0 disk
├─sda1 8:1 0 1G 0 part /boot
└─sda2 8:2 0 19G 0 part /home
/
sr0 11:0 1 1.9G 0 rom /run/media/maminjie/Fedora-WS-Live-36-1-5
zram0 252:0 0 1.9G 0 disk [SWAP]
sda 磁盘分了两个区,各列说明如下:
范例二:仅列出 /dev/sda 装置内的所有数据的完整文件名
[maminjie@fedora home]$ lsblk -ip /dev/sda
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
/dev/sda 8:0 0 20G 0 disk
|-/dev/sda1 8:1 0 1G 0 part /boot
`-/dev/sda2 8:2 0 19G 0 part /home
/
范例三:在范例二基础上列出文件系统名称
[maminjie@fedora home]$ lsblk -ipf /dev/sda
NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
/dev/sda
|-/dev/sda1 ext4 1.0 cdb8d723-c331-4809-a62d-4fa54ee5c3b7 734.5M 18% /boot
`-/dev/sda2 btrfs fedora_localhost-live 89c63ae9-aecb-41dd-ba2f-dc750d94046c 13.9G 26% /home
/
-f 除了列出文件系统名称,还列出了文件系统版本、标签、UUID、分区可用容量等。
UUID(Universally Unique Identifier,通用唯一识别码),Linux 会将系统内所有的装置都给予一个独一无二的标识符,这个标识符可以用于挂载或使用装置/文件系统。
[maminjie@fedora home]$ blkid
/dev/sda2: LABEL="fedora_localhost-live" UUID="89c63ae9-aecb-41dd-ba2f-dc750d94046c" UUID_SUB="37b329d1-44eb-4374-bc57-b2e2a81e7e2c" BLOCK_SIZE="4096" TYPE="btrfs" PARTUUID="37619dd2-02"
/dev/sr0: BLOCK_SIZE="2048" UUID="2022-05-04-21-36-58-00" LABEL="Fedora-WS-Live-36-1-5" TYPE="iso9660" PTUUID="7a218b9a" PTTYPE="dos"
[maminjie@fedora home]$ sudo blkid
/dev/sda2: LABEL="fedora_localhost-live" UUID="89c63ae9-aecb-41dd-ba2f-dc750d94046c" UUID_SUB="37b329d1-44eb-4374-bc57-b2e2a81e7e2c" BLOCK_SIZE="4096" TYPE="btrfs" PARTUUID="37619dd2-02"
/dev/sr0: BLOCK_SIZE="2048" UUID="2022-05-04-21-36-58-00" LABEL="Fedora-WS-Live-36-1-5" TYPE="iso9660" PTUUID="7a218b9a" PTTYPE="dos"
/dev/sda1: UUID="cdb8d723-c331-4809-a62d-4fa54ee5c3b7" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="37619dd2-01"
/dev/zram0: LABEL="zram0" UUID="e6136743-240d-4d41-b3f9-1af80e6bbce8" TYPE="swap"
备注:blkid 有用户权限限制。
虽然通过 lsblk 可以知道系统上的所有装置,通过 blkid 可以知道所有的文件系统,但还不清楚磁盘的分区类型,通过 parted 可以列出磁盘的分区表类型和分区信息。
简单用法:
parted device_name print
范例一:列出 /dev/sda 磁盘的相关数据
maminjie@fedora home]$ sudo parted /dev/sda print
Model: VMware, VMware Virtual S (scsi) # 磁盘的模块名称(厂商)
Disk /dev/sda: 21.5GB # 磁盘的总容量
Sector size (logical/physical): 512B/512B # 磁盘的每个逻辑/物理扇区容量
Partition Table: msdos # 分区表的格式(MBR/GPT等),msdos=MBR
Disk Flags:
# 以下是分区数据
Number Start End Size Type File system Flags
1 1049kB 1075MB 1074MB primary ext4 boot
2 1075MB 21.5GB 20.4GB primary btrfs
备注:parted 有用户权限限制。
fdisk 命令用于创建 MBR 分区,gdisk 命令用于创建 GPT 分区。由于作者当前系统的磁盘是采用 MBR 分区的,下面主要介绍 fdisk 的操作,当然作者并不想真正的创建分区,只是介绍下 fdisk 的用法。
[maminjie@fedora home]$ fdisk -h
Usage:
fdisk [options] <disk> change partition table
fdisk [options] -l [<disk>...] list partition table(s)
Display or manipulate a disk partition table.
Options:
-b, --sector-size <size> physical and logical sector size
-B, --protect-boot don't erase bootbits when creating a new label
-c, --compatibility[=] mode is ' dos' or 'nondos' (default)
-L, --color[=] colorize output (auto, always or never)
colors are enabled by default
-l, --list display partitions and exit
-x, --list-details like --list but with more details
-n, --noauto-pt don' t create default partition table on empty devices
-o, --output <list> output columns
-t, --type <type> recognize specified partition table type only
-u, --units[=<unit>] display units: 'cylinders' or 'sectors' (default)
-s, --getsz display device size in 512-byte sectors [DEPRECATED]
--bytes print SIZE in bytes rather than in human readable format
--lock[=<mode>] use exclusive device lock (yes, no or nonblock)
-w, --wipe <mode> wipe signatures (auto, always or never)
-W, --wipe-partitions <mode> wipe signatures from new partitions (auto, always or never)
-C, --cylinders <number> specify the number of cylinders
-H, --heads <number> specify the number of heads
-S, --sectors <number> specify the number of sectors per track
-h, --help display this help
-V, --version display version
Available output columns:
gpt: Device Start End Sectors Size Type Type-UUID Attrs Name UUID
dos: Device Start End Sectors Cylinders Size Type Id Attrs Boot End-C/H/S Start-C/H/S
bsd: Slice Start End Sectors Cylinders Size Type Bsize Cpg Fsize
sgi: Device Start End Sectors Cylinders Size Type Id Attrs
sun: Device Start End Sectors Cylinders Size Type Id Flags
For more details see fdisk(8).
简单用法:fdisk 设备名称
范例一:以 /dev/sda 为例介绍 fdisk 的操作
maminjie@fedora home]$ sudo fdisk /dev/sda
[sudo] password for maminjie:
Welcome to fdisk (util-linux 2.38-rc1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
This disk is currently in use - repartitioning is probably a bad idea.
It's recommended to umount all file systems, and swapoff all swap
partitions on this disk.
Command (m for help): m
Help:
DOS (MBR)
a toggle a bootable flag
b edit nested BSD disklabel
c toggle the dos compatibility flag
Generic
d delete a partition
F list free unpartitioned space
l list known partition types
n add a new partition
p print the partition table
t change a partition type
v verify the partition table
i print information about a partition
Misc
m print this menu
u change display/entry units
x extra functionality (experts only)
Script
I load disk layout from sfdisk script file
O dump disk layout to sfdisk script file
Save & Exit
w write table to disk and exit
q quit without saving changes
Create a new label
g create a new empty GPT partition table
G create a new empty SGI (IRIX) partition table
o create a new empty DOS partition table
s create a new empty Sun partition table
Command (m for help):
fdisk 不加任何选项将进入一个交互界面,所以不需要专门记 fdisk 的命令。
上面已经提示,当前磁盘正在使用,操作是危险的。
fdisk 只用需要 root 权限,此外,需要注意的是,装置名不能加上数字,这里是指整个磁盘装置,而不是某个分区。
1)p 打印磁盘分区表
Command (m for help): p
Disk /dev/sda: 20 GiB, 21474836480 bytes, 41943040 sectors
Disk model: VMware Virtual S
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x37619dd2
Device Boot Start End Sectors Size Id Type
/dev/sda1 * 2048 2099199 2097152 1G 83 Linux
/dev/sda2 2099200 41943039 39843840 19G 83 Linux
Command (m for help):
上半部分显示整体磁盘的状态,当前磁盘共有 20GB 左右的容量,共有 41943040 个扇区,每个扇区容量为 512 bytes。
下半部分显示每个分区的一些信息:
从上面可知,整个磁盘已经不可以进行额外的分区了,最大扇区数为 41943040,最大号码 41943039 已经使用了。另外,新分区通常选用上一个分区的结束扇区号码数加 1 作为起始扇区号码。
2)w/q 退出操作
使用 fdisk 进行一些操作后,按下 q 所有的动作都不会生效,按下 w 所有的动作才生效,所以谨慎使用 w。
3)n 添加分区
Command (m for help): n
All space for primary partitions is in use.
Command (m for help):
由于所有空间都被用完了,所以没办法添加新的分区。读者如需要新增分区,可以根据命令提示或参考其它资料进行操作。
4)d 删除分区
Command (m for help): p
Disk /dev/sda: 20 GiB, 21474836480 bytes, 41943040 sectors
Disk model: VMware Virtual S
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x37619dd2
Device Boot Start End Sectors Size Id Type
/dev/sda1 * 2048 2099199 2097152 1G 83 Linux
/dev/sda2 2099200 41943039 39843840 19G 83 Linux
Command (m for help): d
Partition number (1,2, default 2):
Partition 2 has been deleted.
Command (m for help): p
Disk /dev/sda: 20 GiB, 21474836480 bytes, 41943040 sectors
Disk model: VMware Virtual S
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x37619dd2
Device Boot Start End Sectors Size Id Type
/dev/sda1 * 2048 2099199 2097152 1G 83 Linux
Command (m for help): q
输入 d 后选择要删除的分区编号,如果不选择将默认选择 default 提示的编号,这里作者不是真的删除,所以最后按 q 退出。
文件 /proc/partitions 记录系统的扇区信息。
[maminjie@fedora home]$ cat /proc/partitions
major minor #blocks name
11 0 1970848 sr0
8 0 20971520 sda
8 1 1048576 sda1
8 2 19921920 sda2
252 0 1982464 zram0
在分区新增或删除时,因为 Linux 此时还在使用这个磁盘,为了担心系统出问题,所以分区表并没有被更新。有两种方式来更新:重启、partprobe。
[maminjie@fedora home]$ partprobe -h
Usage: partprobe [OPTION] [DEVICE]...
Inform the operating system about partition table changes.
-d, --dry-run do not actually inform the operating system
-s, --summary print a summary of contents
-h, --help display this help and exit
-v, --version output version information and exit
When no DEVICE is given, probe all partitions.
Report bugs to <[email protected]>.
一般使用 partprobe -s 更跟新分区表。
分区完后,就需要进行文件系统的格式化,格式化的指令是 mkfs(make filesystem),这个指令其实是个综合的指令,比如:mkdfs.xfs,mkfs.ext4 等。如下所示:
[maminjie@fedora home]$ mkfs
mkfs mkfs.cramfs mkfs.ext2 mkfs.ext4 mkfs.hfsplus mkfs.msdos mkfs.vfat
mkfs.btrfs mkfs.exfat mkfs.ext3 mkfs.fat mkfs.minix mkfs.ntfs mkfs.xfs
输入 mkfs,然后连续按两次[tab],显示出当前支持 mkfs 命令。
[maminjie@fedora home]$ mkfs -h
Usage:
mkfs [options] [-t <type>] [fs-options] <device> [<size>]
Make a Linux filesystem.
Options:
-t, --type=<type> filesystem type; when unspecified, ext2 is used
fs-options parameters for the real filesystem builder
<device> path to the device to be used
<size> number of blocks to be used on the device
-V, --verbose explain what is being done;
specifying -V more than once will cause a dry-run
-h, --help display this help
-V, --version display version
For more details see mkfs(8).
mkfs -t ext4 相当于 mkfs.ext4。
简单用法:
mkfs.ext4 [-b block-size] [-L volume-label] 设备名称
选项与参数:
-b 设定 block 的大小,有 1K,2K,4K 的容量
-L 后面接这个装置的标签名称(lsblk -f 可以显示出标签)
其它选项:
[maminjie@fedora home]$ mkfs.ext4 -h
mkfs.ext4: invalid option -- 'h'
Usage: mkfs.ext4 [-c|-l filename] [-b block-size] [-C cluster-size]
[-i bytes-per-inode] [-I inode-size] [-J journal-options]
[-G flex-group-size] [-N number-of-inodes] [-d root-directory]
[-m reserved-blocks-percentage] [-o creator-os]
[-g blocks-per-group] [-L volume-label] [-M last-mounted-directory]
[-O feature[,...]] [-r fs-revision] [-E extended-option[,...]]
[-t fs-type] [-T usage-type ] [-U UUID] [-e errors_behavior][-z undo_file]
[-jnqvDFSV] device [blocks-count]
由于系统在运行时,可能会出现一些硬件或电源灯问题,从而导致文件系统发生错乱,下面主要介绍 ext4 文件系统的检测命令。检测的指令是 fsck(filesystem check),这个指令也是个综合的指令,比如:fsck.xfs,fsck.ext4 等。如下所示:
maminjie@fedora home]$ fsck
fsck fsck.cramfs fsck.ext2 fsck.ext4 fsck.hfs fsck.minix fsck.ntfs fsck.xfs
fsck.btrfs fsck.exfat fsck.ext3 fsck.fat fsck.hfsplus fsck.msdos fsck.vfat
[maminjie@fedora home]$ fsck -h
fsck from util-linux 2.38-rc1
fsck.ext4: invalid option -- 'h'
Usage: fsck.ext4 [-panyrcdfktvDFV] [-b superblock] [-B blocksize]
[-l|-L bad_blocks_file] [-C fd] [-j external_journal]
[-E extended-options] [-z undo_file] device
Emergency help:
-p Automatic repair (no questions)
-n Make no changes to the filesystem
-y Assume "yes" to all questions
-c Check for bad blocks and add them to the badblock list
-f Force checking even if filesystem is marked clean
-v Be verbose
-b superblock Use alternative superblock
-B blocksize Force blocksize when looking for superblock
-j external_journal Set location of the external journal
-l bad_blocks_file Add to badblocks list
-L bad_blocks_file Set badblocks list
-z undo_file Create an undo file
简单用法:
fsck.ext4 [-pf] [-b superblock] 设备名称
选项与参数:
-p 当文件系统在修复时,若有需要回复 y 的动作时,
自动回复 y 来继续进行修复动作。
-f 强制检查!一般来说,如果 fsck 没有发现任何 unclean 的标志,
不会主动进入检查,加上 -f 就强制进行检查。
-D 针对文件系统下的目录进行优化配置
-b 后面接 superblock 的位置!一般来说这个选项用不到。
但是如果你的 superblock 因故损毁时,通过这个参数即可
利用文件系统内备份的 superblock 来尝试救援。一般来说,
superblock 备份在:
- 1K block 放在 8193
- 2K block 放在 16384
- 4K block 放在 32768
其它选项:
[maminjie@fedora home]$ fsck.ext4 -h
fsck.ext4: invalid option -- 'h'
Usage: fsck.ext4 [-panyrcdfktvDFV] [-b superblock] [-B blocksize]
[-l|-L bad_blocks_file] [-C fd] [-j external_journal]
[-E extended-options] [-z undo_file] device
Emergency help:
-p Automatic repair (no questions)
-n Make no changes to the filesystem
-y Assume "yes" to all questions
-c Check for bad blocks and add them to the badblock list
-f Force checking even if filesystem is marked clean
-v Be verbose
-b superblock Use alternative superblock
-B blocksize Force blocksize when looking for superblock
-j external_journal Set location of the external journal
-l bad_blocks_file Add to badblocks list
-L bad_blocks_file Set badblocks list
-z undo_file Create an undo file
在进行挂载前,需要确定几件事:
如果要挂载的目录并不是空的,那么挂载了文件系统之后,原目录中的内容会暂时消失,等到文件系统被卸载之后,原目录才又可见。
简单用法:
mount -a
mount [-l]
mount [-t 文件系统类型] LABEL='' 挂载点
mount [-t 文件系统类型] UUID='' 挂载点
mount [-t 文件系统类型] 设备文件名 挂载点
选项与参数:
-a 依照配置文件 /etc/fstab 的数据将所有未挂载的磁盘都挂载上来
-l 单纯的输入 mount 会显示目前挂载的信息,加上 -l 可列出 label 名称
-t 可以加上文件系统类型来指定预挂载的类型。常见的 Linux 支持的类型有:
xfs,ext3,ext4,reiserfs,vfat,iso9660(光盘格式),
nfs,cifs,smbfs(后三种为网络文件系统)
-n 在默认的情况下,系统会将实际挂载的情况实时写入 /etc/mtab 中,供其它程序使用。
但在某些情况下(例如单人维护模式)为了避免问题会刻意不写入,此时就使用 -n 选项。
-o 后面可以接一些挂载时额外加上的选项,比如账号、密码、读写权限等:
async, sync: 此文件系统是否使用同步写入(sync)或异步(async)的内存机制。
atime, noatime:是否修订文件的读取时间(atime)。为了性能,某些时刻会使用 noatime
ro, rw: 挂载文件系统称为只读(ro)或可擦写(rw)
auto, noauto: 允许此文件系统被以 mount -a 自动挂载
dev, nodev: 是否允许此文件系统上可建立设备文件,dev 为允许
suid, nosuid: 是否允许此文件系统含有 suid/sgid 的文件格式
exec, noexec: 是否允许此文件系统上含有可行二进制文件
user, nouser: 是否允许此文件系统让任何使用者执行 mount。一般来说,
mount 仅有 root 可以进行,但加上 user 参数,则可让
一般 user 也能够对此 partititon 进行 mount。
defaults: 默认值为:rw,suid,dev,exec,auto,nouser,async
remount: 重新挂载。这在系统出错或重新更新参数时,很有用。
mount 的更多用法如下:
[maminjie@fedora work]$ mount -h
Usage:
mount [-lhV]
mount -a [options]
mount [options] [--source] <source> | [--target] <directory>
mount [options] <source> <directory>
mount <operation> <mountpoint> [<target>]
Mount a filesystem.
Options:
-a, --all mount all filesystems mentioned in fstab
-c, --no-canonicalize don't canonicalize paths
-f, --fake dry run; skip the mount(2) syscall
-F, --fork fork off for each device (use with -a)
-T, --fstab alternative file to /etc/fstab
-i, --internal-only don' t call the mount.<type> helpers
-l, --show-labels show also filesystem labels
-m, --mkdir[=<mode>] alias to '-o X-mount.mkdir[=]'
-n, --no-mtab don't write to /etc/mtab
--options-mode <mode>
what to do with options loaded from fstab
--options-source <source>
mount options source
--options-source-force
force use of options from fstab/mtab
-o, --options <list> comma-separated list of mount options
-O, --test-opts <list> limit the set of filesystems (use with -a)
-r, --read-only mount the filesystem read-only (same as -o ro)
-t, --types <list> limit the set of filesystem types
--source <src> explicitly specifies source (path, label, uuid)
--target <target> explicitly specifies mountpoint
--target-prefix <path>
specifies path used for all mountpoints
-v, --verbose say what is being done
-w, --rw, --read-write mount the filesystem read-write (default)
-N, --namespace <ns> perform mount in another namespace
-h, --help display this help
-V, --version display version
Source:
-L, --label <label> synonym for LABEL=<label>
-U, --uuid <uuid> synonym for UUID=<uuid>
LABEL=<label> specifies device by filesystem label
UUID=<uuid> specifies device by filesystem UUID
PARTLABEL=<label> specifies device by partition label
PARTUUID=<uuid> specifies device by partition UUID
ID=<id> specifies device by udev hardware ID
<device> specifies device by path
<directory> mountpoint for bind mounts (see --bind/rbind)
<file> regular file for loopdev setup
Operations:
-B, --bind mount a subtree somewhere else (same as -o bind)
-M, --move move a subtree to some other place
-R, --rbind mount a subtree and all submounts somewhere else
--make-shared mark a subtree as shared
--make-slave mark a subtree as slave
--make-private mark a subtree as private
--make-unbindable mark a subtree as unbindable
--make-rshared recursively mark a whole subtree as shared
--make-rslave recursively mark a whole subtree as slave
--make-rprivate recursively mark a whole subtree as private
--make-runbindable recursively mark a whole subtree as unbindable
For more details see mount(8).
mount 命令不加 -t 选项,系统会自动分析最恰当的文件系统来尝试挂载需要的设备。
由于文件系统机会都有 superblock,所以 Linux 通过分析 superblock 搭配 Linux 自己的驱动程序去测试挂载,如果测试成功,就立刻自动的使用该类型的文件系统进行挂载。主要参考下面两个文件:
[maminjie@fedora work]$ cat /etc/filesystems
ext4
ext3
ext2
nodev proc
nodev devpts
iso9660
vfat
hfs
hfsplus
*
Linux 支持的文件系统的驱动程序都写在如下的目录中:
[maminjie@fedora work]$ ls /lib/modules/$(uname -r)/kernel/fs
9p cachefiles ecryptfs fat hfsplus minix nilfs2 overlayfs squashfs vboxsf
affs ceph erofs fscache isofs netfs nls pstore sysv xfs
afs cifs exfat fuse jffs2 nfs ntfs3 reiserfs ubifs zonefs
befs coda ext4 gfs2 jfs nfs_common ocfs2 romfs udf
binfmt_misc.ko.xz dlm f2fs hfs lockd nfsd orangefs smbfs_common ufs
整个目录树最重要的地方就是根目录了,所以根目录根本就不能被卸载。如果你的挂载参数要改变,或者是根目录出现只读状态时,如何重新挂载呢?
除了重启(reboot),也可以用下面的方法:
范例:将 / 重新挂载,并加入参数为 rw 与 auto
mount -o remount,rw,auto /
也可以利用 mount 来将某个目录挂载到另外一个目录上。这并不是挂载文件系统,而是额外挂载某个目录的方法!
[maminjie@fedora work]$ mkdir d1 d2
[maminjie@fedora work]$ ls -lid d1 d2
880 drwxrwxr-x. 1 maminjie maminjie 0 Jun 13 23:05 d1
881 drwxrwxr-x. 1 maminjie maminjie 0 Jun 13 23:05 d2
[maminjie@fedora work]$ sudo mount --bind d2 d1
mount: (hint) your fstab has been modified, but systemd still uses
the old version; use 'systemctl daemon-reload' to reload.
[maminjie@fedora work]$ ls -lid d1 d2
881 drwxrwxr-x. 1 maminjie maminjie 0 Jun 13 23:05 d1
881 drwxrwxr-x. 1 maminjie maminjie 0 Jun 13 23:05 d2
[maminjie@fedora work]$ umount d1
umount: /home/maminjie/work/d1: must be superuser to unmount.
[maminjie@fedora work]$ sudo umount d1
[maminjie@fedora work]$ ls -lid d1 d2
880 drwxrwxr-x. 1 maminjie maminjie 0 Jun 13 23:05 d1
881 drwxrwxr-x. 1 maminjie maminjie 0 Jun 13 23:05 d2
实际上,mount --bind 还可以绑定两个文件。
我们在开机后,进入 Linux 系统后,在执行 df 查看磁盘容量的时候,看到的一些设备已经被挂载了,而我们并没有去执行 mount 命令。实际上,这些都是在开机时自动挂载的。
在将开机挂载前,先来看看系统挂载的一些限制:
若希望文件系统在开机时就挂载好,在 /etc/fstab 文件中配置就可以了。fstab(filesystem table)
[maminjie@fedora work]$ cat /etc/fstab
#
# /etc/fstab
# Created by anaconda on Tue May 31 16:16:48 2022
#
# Accessible filesystems, by reference, are maintained under '/dev/disk/'.
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info.
#
# After editing this file, run 'systemctl daemon-reload' to update systemd
# units generated from this file.
#
UUID=89c63ae9-aecb-41dd-ba2f-dc750d94046c / btrfs subvol=root,compress=zstd:1 0 0
UUID=cdb8d723-c331-4809-a62d-4fa54ee5c3b7 /boot ext4 defaults 1 2
UUID=89c63ae9-aecb-41dd-ba2f-dc750d94046c /home btrfs subvol=home,compress=zstd:1 0 0
该文件的内容共有六个字段,概括如下:
[设备/UUID等] [挂载点] [文件系统] [文件系系统参数] [dump] [fsck]
各字段介绍如下:
1)第一列:磁盘设备文件名/UUID/LABEL
这个字段可以的数据主要有三个:
2)第二列:挂载点(mount point)
目录名
3)第三列:磁盘分区槽的文件系统
手动执行 mount 挂载时可以让系统自动测试挂载,这里必须写明确文件系统,包括:xfs,ext4,vfat,btrfs,nfs 等。
4)第四列:文件系统参数
同 mount 命令中的参数
5)第五列:是否被 dump 备份命令使用
dump 是一个用来做备份的命令,可以直接写 0
6)第六列:是否以 fsck 检测扇区
?
/etc/fstab 是开机是的配置文件,实际文件系统的挂载时记录到 /etc/mtab 与 /proc/mounts 这两个文件中。每次更改文件系统的挂载,也会同时更改这两个文件。
万一在 /etc/fstab 中输入的数据错误,导致无法顺利开机,在进入单人维护模式时,/ 将是只读的,也就无法修改 /etc/fstab,也无法更新 /etc/mtab。可以使用下面方法重新挂载根目录:
mount -n -o remount,rw /
mkdir /xx/yy
mount -o loop /xxx/yyy.iso /xx/yy
访问目录 /xx/yy 就可以访问光盘或者DVD中的文件了。
可以制作一个大文件,然后将这个文件格式化后挂载。这个可以解决很多系统的分区不良情况。
举例来说,如果当初在分区时,你只分出一个根目录,假设没有多余的容量可以进行额外的分区了。偏偏根目录很大,这时就可以用这种方式,感觉就像多了一个分区槽。
dd 命令可以用来建立空的文件,用法如下:
[maminjie@fedora work]$ dd if=/dev/zero of=/home/maminjie/work/loopdev bs=1M count=128
128+0 records in
128+0 records out
134217728 bytes (134 MB, 128 MiB) copied, 1.53093 s, 87.7 MB/s
[maminjie@fedora work]$ ll -h loopdev
-rw-rw-r--. 1 maminjie maminjie 128M Jun 14 23:04 loopdev
上面命令的简单意义如下:
通过 mkfs 系列命令对文件进行格式化,比如将文件格式化为 ext4 文件系统:
[maminjie@fedora work]$ mkfs.ext4 loopdev
mke2fs 1.46.5 (30-Dec-2021)
Discarding device blocks: done
Creating filesystem with 131072 1k blocks and 32768 inodes
Filesystem UUID: 29b4eef4-7016-4b0b-8f69-63d0cdf797a9
Superblock backups stored on blocks:
8193, 24577, 40961, 57345, 73729
Allocating group tables: done
Writing inode tables: done
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done
[maminjie@fedora work]$ blkid loopdev
loopdev: UUID="29b4eef4-7016-4b0b-8f69-63d0cdf797a9" BLOCK_SIZE="1024" TYPE="ext4"
利用 mount -o loop 进行挂载,如下:
[maminjie@fedora work]$ mkdir testfs
[maminjie@fedora work]$ sudo mount -o loop loopdev testfs
mount: (hint) your fstab has been modified, but systemd still uses
the old version; use 'systemctl daemon-reload' to reload.
[maminjie@fedora work]$ df testfs
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/loop0 117203 14 108015 1% /home/maminjie/work/testfs
[maminjie@fedora work]$ sudo umount testfs/
[maminjie@fedora work]$ df testfs
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda2 19921920 5364872 14377032 28% /home
umount 将设备文件卸载
简单用法:
umount [-fn] 设备命名或挂载点
选项与参数:
-f 强制卸载!可用在类似网络文件系统(NFS)无法读取到的情况下
-l 立即卸载文件系统,比 -f 还强
-n 不更新 /etc/mtab 情况下卸载
umount 的更多用法如下:
[maminjie@fedora work]$ umount -h
Usage:
umount [-hV]
umount -a [options]
umount [options] <source> | <directory>
Unmount filesystems.
Options:
-a, --all unmount all filesystems
-A, --all-targets unmount all mountpoints for the given device in the
current namespace
-c, --no-canonicalize don't canonicalize paths
-d, --detach-loop if mounted loop device, also free this loop device
--fake dry run; skip the umount(2) syscall
-f, --force force unmount (in case of an unreachable NFS system)
-i, --internal-only don't call the umount.<type> helpers
-n, --no-mtab don't write to /etc/mtab
-l, --lazy detach the filesystem now, clean up things later
-O, --test-opts limit the set of filesystems (use with -a)
-R, --recursive recursively unmount a target with all its children
-r, --read-only in case unmounting fails, try to remount read-only
-t, --types limit the set of filesystem types
-v, --verbose say what is being done
-q, --quiet suppress '
not mounted' error messages
-N, --namespace <ns> perform umount in another namespace
-h, --help display this help
-V, --version display version
For more details see umount(8).
某些时刻,你可能希望修改一下目前文件系统的一些相关信息。举例来说,你可能要修改 Label name,或者是 journal 的参数,或者其它磁盘/文件系统运作是的相关参数。
在 Linux 底下所有的设备都以文件来代表,但是那个文件如何代表该设备呢?实际上是通过文件的 major 与 minor 数值来替代的。
/dev/sda 的相关设备代码如下:
[maminjie@fedora work]$ ll /dev/sda*
brw-rw----. 1 root disk 8, 0 Jun 13 20:32 /dev/sda
brw-rw----. 1 root disk 8, 1 Jun 13 20:32 /dev/sda1
brw-rw----. 1 root disk 8, 2 Jun 13 20:32 /dev/sda2
上面的 8 位主要设备代码(Major),而 0-2 为次要设备代码(Minor)。常见的磁盘文件名的设备代码如下所示:
磁盘文件名 | Major | Minor |
---|---|---|
/dev/sda | 8 | 0-15 |
/dev/sdb | 8 | 16-31 |
/dev/loop0 | 7 | 0 |
/dev/lopp1 | 7 | 1 |
mknod 命令可用来修订 Major 和 Minor。
简单用法:
mknod 设备文件名 [bcp] [Major] [Minor]
选项与参数:
设备种类:
b 设定设备名称成为一个周边存储设备文件,比如磁盘等
c 设定设备名称成为一个周边输入设备文件,比如鼠标/键盘等
p 设定设备名称成为一个 FIFO 文件
Major 主要设备代码
Minor 次要设备代码
mknod 的更多用法如下:
maminjie@fedora work]$ mknod --help
Usage: mknod [OPTION]... NAME TYPE [MAJOR MINOR]
Create the special file NAME of the given TYPE.
Mandatory arguments to long options are mandatory for short options too.
-m, --mode=MODE set file permission bits to MODE, not a=rw - umask
-Z set the SELinux security context to default type
--context[=CTX] like -Z, or if CTX is specified then set the SELinux
or SMACK security context to CTX
--help display this help and exit
--version output version information and exit
Both MAJOR and MINOR must be specified when TYPE is b, c, or u, and they
must be omitted when TYPE is p. If MAJOR or MINOR begins with 0x or 0X,
it is interpreted as hexadecimal; otherwise, if it begins with 0, as octal;
otherwise, as decimal. TYPE may be:
b create a block (buffered) special file
c, u create a character (unbuffered) special file
p create a FIFO
NOTE: your shell may have its own version of mknod, which usually supersedes
the version described here. Please refer to your shell's documentation
for details about the options it supports.
GNU coreutils online help:
Full documentation
or available locally via: info ' (coreutils) mknod invocation'
tune2fs 可以修改 ext4 的 label name 和 UUID
简单用法:
tune2fs [-l] [-L label] [-U uuid] 设备文件名
选项与参数:
-l 类似 dumpe2fs -h 的功能,将 superblock 内的数据读出来
-L 修改 label name
-U 修改 UUID
范例一:列出 /dev/sda1 的 label name
[maminjie@fedora work]$ sudo tune2fs -l /dev/sda1 | grep name
Filesystem volume name: <none>
没有命名,可以通过sudo tune2fs -L LABELNAME /dev/sda1
修改 label name。
内存置换空间(swap,交换区),可以用在内存不足的时候,为了让后续的程序可以顺利的运作,将内存中暂不使用的程序与数据挪到 swap 中,让内存空出来给需要执行的程序加载。
现在由于内存都很大,因此在个人机上,不设定 swap 问题也不大。但对于服务器,最好还是预留一些 swap 来缓冲一下系统的内存用量,至少是备而不用。
如果系统已经创建好了,发现没有创建 swap,不要慌,可以用如下的方式创建 swap:
mkswap 设备文件名
将分区槽格式化成 swap 格式swapon 设备文件名
启动 swap 设备swapoff 设备文件名
停止 swap 设备mkswap 文件名
将文件格式化成 swap 格式swapon 文件名
启动 swap 设备swapoff 文件名
停止 swap 设备Linux 文件系统原理