在 Linux 中,最直观、最可见的部分就是 文件系统(file system)
。下面我们就来一起探讨一下关于 Linux 中国的文件系统,系统调用以及文件系统实现背后的原理和思想。这些思想中有一些来源于 MULTICS,现在已经被 Windows 等其他操作系统使用。Linux 的设计理念就是 小的就是好的(Small is Beautiful)
。虽然 Linux 只是使用了最简单的机制和少量的系统调用,但是 Linux 却提供了强大而优雅的文件系统。
Linux 文件系统基本概念
Linux 在最初的设计是 MINIX1 文件系统,它只支持 14 字节的文件名,它的最大文件只支持到 64 MB。在 MINIX 1 之后的文件系统是 ext 文件系统。ext 系统相较于 MINIX 1 来说,在支持字节大小和文件大小上均有很大提升,但是 ext 的速度仍没有 MINIX 1 快,于是,ext 2 被开发出来,它能够支持长文件名和大文件,而且具有比 MINIX 1 更好的性能。这使他成为 Linux 的主要文件系统。只不过 Linux 会使用 VFS
曾支持多种文件系统。在 Linux 链接时,用户可以动态的将不同的文件系统挂载倒 VFS 上。
Linux 中的文件是一个任意长度的字节序列,Linux 中的文件可以包含任意信息,比如 ASCII 码、二进制文件和其他类型的文件是不加区分的。
为了方便起见,文件可以被组织在一个目录中,目录存储成文件的形式在很大程度上可以作为文件处理。目录可以有子目录,这样形成有层次的文件系统,Linux 系统下面的根目录是 /
,它通常包含了多个子目录。字符 /
还用于对目录名进行区分,例如 「/usr/cxuan」 表示的就是根目录下面的 usr 目录,其中有一个叫做 cxuan 的子目录。
下面我们介绍一下 Linux 系统根目录下面的目录名
/bin
,它是重要的二进制应用程序,包含二进制文件,系统的所有用户使用的命令都在这里/boot
,启动包含引导加载程序的相关文件/dev
,包含设备文件,终端文件,USB 或者连接到系统的任何设备/etc
,配置文件,启动脚本等,包含所有程序所需要的配置文件,也包含了启动/停止单个应用程序的启动和关闭 shell 脚本/home
,本地主要路径,所有用户用 home 目录存储个人信息/lib
,系统库文件,包含支持位于 /bin 和 /sbin 下的二进制库文件/lost+found
,在根目录下提供一个遗失+查找系统,必须在 root 用户下才能查看当前目录下的内容/media
,挂载可移动介质/mnt
,挂载文件系统/opt
,提供一个可选的应用程序安装目录/proc
,特殊的动态目录,用于维护系统信息和状态,包括当前运行中进程信息/root
,root 用户的主要目录文件夹/sbin
,重要的二进制系统文件/tmp
, 系统和用户创建的临时文件,系统重启时,这个目录下的文件都会被删除/usr
,包含绝大多数用户都能访问的应用程序和文件/var
,经常变化的文件,诸如日志文件或数据库等
在 Linux 中,有两种路径,一种是 绝对路径(absolute path)
,绝对路径告诉你从根目录下查找文件,绝对路径的缺点是太长而且不太方便。还有一种是 相对路径(relative path)
,相对路径所在的目录也叫做工作目录(working directory)
。
如果 /usr/local/books
是工作目录,那么 shell 命令
cp books books-replica
就表示的是相对路径,而
cp /usr/local/books/books /usr/local/books/books-replica
则表示的是绝对路径。
在 Linux 中经常出现一个用户使用另一个用户的文件或者使用文件树结构中的文件。两个用户共享同一个文件,这个文件位于某个用户的目录结构中,另一个用户需要使用这个文件时,必须通过绝对路径才能引用到他。如果绝对路径很长,那么每次输入起来会变的非常麻烦,所以 Linux 提供了一种 链接(link)
机制。
举个例子,下面是一个使用链接之前的图
以上所示,比如有两个工作账户 jianshe 和 cxuan,jianshe 想要使用 cxuan 账户下的 A 目录,那么它可能会输入/usr/cxuan/A
,这是一种未使用链接之后的图。
使用链接后的示意如下
现在,jianshe 可以创建一个链接来使用 cxuan 下面的目录了。‘
当一个目录被创建出来后,有两个目录项也同时被创建出来,它们就是 .
和 ..
,前者代表工作目录自身,后者代表该目录的父目录,也就是该目录所在的目录。这样一来,在 /usr/jianshe 中访问 cxuan 中的目录就是 ../cxuan/xxx
Linux 文件系统不区分磁盘的,这是什么意思呢?一般来说,一个磁盘中的文件系统相互之间保持独立,如果一个文件系统目录想要访问另一个磁盘中的文件系统,在 Windows 中你可以像下面这样。
两个文件系统分别在不同的磁盘中,彼此保持独立。
而在 Linux 中,是支持挂载
的,它允许一个磁盘挂在到另外一个磁盘上,那么上面的关系会变成下面这样
挂在之后,两个文件系统就不再需要关心文件系统在哪个磁盘上了,两个文件系统彼此可见。
Linux 文件系统的另外一个特性是支持 加锁(locking)
。在一些应用中会出现两个或者更多的进程同时使用同一个文件的情况,这样很可能会导致竞争条件(race condition)
。一种解决方法是对其进行加不同粒度的锁,就是为了防止某一个进程只修改某一行记录从而导致整个文件都不能使用的情况。
POSIX 提供了一种灵活的、不同粒度级别的锁机制,允许一个进程使用一个不可分割的操作对一个字节或者整个文件进行加锁。加锁机制要求尝试加锁的进程指定其 「要加锁的文件,开始位置以及要加锁的字节」
Linux 系统提供了两种锁:「共享锁和互斥锁」。如果文件的一部分已经加上了共享锁,那么再加排他锁是不会成功的;如果文件系统的一部分已经被加了互斥锁,那么在互斥锁解除之前的任何加锁都不会成功。为了成功加锁、请求加锁的部分的所有字节都必须是可用的。
在加锁阶段,进程需要设计好加锁失败后的情况,也就是判断加锁失败后是否选择阻塞,如果选择阻塞式,那么当已经加锁的进程中的锁被删除时,这个进程会解除阻塞并替换锁。如果进程选择非阻塞式的,那么就不会替换这个锁,会立刻从系统调用中返回,标记状态码表示是否加锁成功,然后进程会选择下一个时间再次尝试。
加锁区域是可以重叠的。下面我们演示了三种不同条件的加锁区域。
如上图所示,A