Skip to content
此页的大纲

类 Unix 文件系统

在GNU/Linux和其他类Unix操作系统中,文件被组织到目录中。所有的文件和目录排放在以“/”为根的巨大的树里。叫它树是因为如果你画出文件系统,它看起来就像一棵树,但是它是颠倒过来的。

如果你在上文中已经学习安装了软件包 tree 你可以用以下命令来了解下文件树:

Shell
cd /
tree -L 3

这会展示出/目录下的文件树,深度为3

这些文件和目录可以分散在多个设备中。mount(8)用于把某个设备上找到的文件系统附着到巨大的文件树上。相反的,umount(8)把它再次分离。在最近的 Linux 内核里,mount(8)带某些参数时可以把文件树的一部分绑定到另外的地方,或者可以把文件系统挂载为共享的、私有的、从设备、或不可绑定的。对每个文件系统支持的挂载选项可以在/usr/share/doc/linux-doc-*/Documentation/filesystems/找到。

Unix系统上叫做目录,某些其他系统上叫做文件夹。请同样留意,在任何Unix系统上,没有的驱动器的概念,例如“A:”。这只有一个文件系统,并且所有东西都包含在内。这相对于 Windows 来说是一个巨大的优点。

Unix 文件基础

下面是一些 Unix 文件基础。

  • 文件名是 区分大小写 的。也就是说, "MYFILE" 和 "MyFile" 是不同的文件。

  • 根目录意味着文件系统的根,简单的称为“/”,不要把它跟 root 用户的家目录“/root”混淆了。

  • 每个目录都有一个名字,它可以包含任意字母或除了/以外的符号。根目录是个特例。它的名字是/(称作“斜线”或“根目录”),并且它不能被重命名。

  • 每个文件或目录都被指定一个全限定文件名,绝对文件名,或路径,按顺序给出必须经过的目录从而到达相应目录。这三个术语是同义的。

  • 所有的全限定文件名以“/”目录开始,并且在每个目录或文件名之间有一个“/”。第一个“/”是最顶层目录,其他的“/”用于分隔跟着的子目录。直到到达最后的入口,即实际文件的名称。这些话可能会令人困惑。用下面这个全限定文件名作为例子:“/usr/share/keytables/us.map.gz”。不过,人们也把它的基名“us.map.gz”单独作为文件名。

  • 根目录有很多分支,例如“/etc/”和“/usr/”。这些子目录依次分出更多的子目录,例如“/etc/init.d/”和“/usr/local/”。这整体叫做“目录树”。你可以把一个绝对文件名想象成从“/”这棵树的基到某个分支(一个文件)的结尾的一条路径。你也听到人们谈论目录树,就好像它是一个包含所有直系后代的“家庭”树的一个图,这个图叫做根目录(“/”):因此子目录有父目录,并且一条路径显示了一个文件完整的祖先。也有相对路径从其他地方开始,而不是从根目录。 你应该还记得目录“../”指向父目录。这个术语也适用于其他类似目录的结构,如分层数据结构。

  • 对于一个物理设备, 是没有一个特定的目录路径名来对应的组成部分. 这不同于RT-11, CP/M,OpenVMS,MS-DOS,AmigaOS, 以及微软的Windows,这些系统存在一个路径包含了一个设备名字,比如"C:"。(尽管如此, 路径条目确实存在引用了物理设备作为正常的文件系统的一部分. 参考第 1.2.2 节 “文件系统深入解析”。)

虽然你可以在文件名中使用任意的字幕或者符号, 但是在实际情况下这样做是一个坏主意. 最好避免使用一些在命令行里面含有特殊意义的字符, 比如空格, 制表符, 换行符, 和其它的特殊字符: { } ( ) [ ] ' " \ / >< | ; ! #&^ * % @ $. 如果你想有一个区分度良好的命名, 比较好的选择是利用 时期, 连字符和下划线. 你也可以每个单词的首字母大写, 这叫大驼峰命名法, 比如这样 "LikeThis`". 经验丰富的Linux用户会趋向于在文件名中不使用空格.

这个 "root" 可能既表示 "超级用户root" 又表示 " 根目录"(/root) . 应该根据上下文确定它的用法

你应该记住以下的一些标准作为开始学习的步骤.

目录含义
/系统的根
/rootroot用户的家目录
/home存放普通用户的主目录
/bin常用命令脚本所在目录
/sbin常用命令脚本 需要super权限所在目录
/lib系统开机所需基本动态链接共享库,类似Windows中dll文件的存放位置
/etc系统以及软件配置文件存放位置
/usr用户程序安装位置
/usr/local软件安装好放在这个目录
/boot系统启动文件存放位置
/proc系统对内存的映射访问此目录可以获得系统信息
/sys文件系统sysfs目录
/tmp存放临时文件
/dev硬件以文件形式存储
/dev用户临时挂载其他文件系统

文件系统深入解析

按照UNIX系统的传统,deepin GNU / Linux 的文件系统是在物理数据存储设备诸如磁盘或其他存储设备上,与硬件设备的交互,如控制台和远程串口终端都是以统一的方式呈现在 “/dev/” 下面。

每个文件、目录、命名管道(一种两个程序间共享数据的方法)或 GNU/Linux 系统上的物理设备都有一个叫做 inode的数据结构,描述了其相关属性,例如拥有它的用户(所有者),它属于的组,最后一次访问时间,等等。把所有东西都表示在文件系统中的想法是来源于 Unix,现代的 Linux 内核则将这个思路进行了扩展。现在,甚至有关计算机上正在运行的进程的信息都可以在文件系统中找到。

这个对物理实体和内部进程的统一和抽象是非常强大的,因为这允许我们用同样的命令对许多完全不同的设备进行同样的操作。甚至可以通过向链接到运行进程的特殊文件写入数据来改变内核的运行方式。

如果您需要识别文件树和物理实体之间的对应关系,请尝试不带参数运行mount

文件系统权限

类Unix系统的文件系统权限被定义给三类受影响的用户。

  • 拥有这个文件的用户(u)

  • 这个文件所属组的其他用户(g)

  • 所有其余的用户(o),同样称为“世界”和“所有人”

对文件来说,每个对应权限允许下列动作。

  • 可读(r)权限允许所有者检查文件的内容。

  • 可写(w)权限允许所有者修改文件内容。

  • 可执行(x)权限允许所有者把文件当做一个命令运行。

对于目录来说,每个对应权限允许下列动作。

  • 可读(r)权限允许所有者列出目录内的内容。

  • 可写(w)权限允许所有者添加或删除目录里面的文件。

  • 可执行(x)权限允许所有者访问目录里的文件。

在这里,一个目录的可执行权限意味着不仅允许读目录里的文件,还允许显示他们的属性,例如大小和修改时间。ls用于显示文件和目录的权限信息(更多)。当运行时带有-l选项,它将按给定顺序显示下列信息。

  • 文件类型(第一个字母)

  • 文件的访问权限(9个字符,三个字符组成一组按照用户、组、其他的顺序表示)

  • 链接到文件的硬链接数

  • 文件所有者的用户名

  • 这个文件所属的组名

  • 以字符(字节)为单位的文件大小

  • 文件的日期和时间(mtime)

  • 文件的名字

字符 说明

-普通文件
d目录
l符号链接
c字符设备节点
b块设备节点
p命名管道
s套接字

chown(1)用于 root 账户修改文件的所有者。chgrp(1)用于文件的所有者或 root 账户修改文件所属的组。chmod(1)用于文件的所有者或 root 账户修改文件和文件夹的访问权限。操作一个foo文件的基本语法如下 。

Shell
sudo chown newowner foo
sudo chgrp newgroup foo
sudo chmod  [ugoa][+-=][rwxXst][,...] foo

例如,你可以按照下面使一个目录树被用户foo所有,并共享给组bar

Shell
cd /some/location/
sudo chown -R foo:bar .
sudo chmod -R ug+rwX,o=rX .

有三个更加特殊的权限位。

  • Set-User-ID(SUID)位(s或S替换用户的x)

  • Set-Group-ID(SGID)位(s或S替换组的x)

  • 粘滞位(t或T替代其他用户的x)

如果“ls -l”对这些位的输出是大写字母,则表示这些输出下面的执行位未设置

给一个可执行文件设置 Set-User-ID 位将允许一个用户以他自己的ID运行这个可执行文件(例如 root 用户)。类似的,给一个可执行文件设置了Set-Group-ID 位将允许一个用户以文件所属组的 ID 运行该文件。(例如 root 组)。由于这些设置可能导致安全风险,设置它们为可用的时候需要格外留意。

在一个目录上设置“Set-Group-ID”将打开类 BSD的文件创建计划,所有在目录里面创建的文件将属于目录所属的

给一个目录设置“粘滞位”将保护该目录内的文件不被其所有者之外的一个用户删除。为了保护一个在像“/tmp”这样所有人可写或同组可写的目录下文件内容的安全,不仅要去除可写权限,还要给其所在目录设置粘滞位。否则,该文件可以被任意对其所在目录有写权限的用户删除并创建一个同名的新文件。

这里有一点有趣的文件权限例子。

Shell
$ ls -l /etc/passwd /etc/shadow /dev/ppp /usr/sbin/exim4
crw------T 1 root root   108, 0 Oct 16 20:57 /dev/ppp
-rw-r--r-- 1 root root     2761 Aug 30 10:38 /etc/passwd
-rw-r----- 1 root shadow   1695 Aug 30 10:38 /etc/shadow
-rwsr-xr-x 1 root root   973824 Sep 23 20:04 /usr/sbin/exim4
$ ls -ld /tmp /var/tmp /usr/local /var/mail /usr/src
drwxrwxrwt 14 root root  20480 Oct 16 21:25 /tmp
drwxrwsr-x 10 root staff  4096 Sep 29 22:50 /usr/local
drwxr-xr-x 10 root root   4096 Oct 11 00:28 /usr/src
drwxrwsr-x  2 root mail   4096 Oct 15 21:40 /var/mail
drwxrwxrwt  3 root root   4096 Oct 16 21:20 /var/tmp

chmod(1)有另一种数值模式来描述文件权限。这种数字模式使用3到4位八进制(底为8)数

数字说明
第一个可选数字Set-User-ID (=4), Set-Group-ID (=2) 和 粘滞位 (=1) 之和
第二个数字用户的可读 (=4), 可写 (=2)和 可执行 (=1) 权限之和
第三个数字组权限同上
第四个数字位其他用户权限同上

这听起来很复杂实际上相当简单。如果你把“ls -l”命令输出的前几列(2-10),看成以二进制(底为2)表示文件的权限(“-”看成0,“rwx”看成1),你应该可以理解用数字模式值的最后3位数字对文件权限的八进制表示。

尝试下列例子

Shell
touch foo bar # 生产两个文件,分别为foo和bar
chmod u=rw,go=r foo # 将foo的用户给予读写权限 属组和其他给予只读权限
chmod 644 bar
ls -l foo bar
-rw-r--r-- 1 penguin penguin 0 Oct 16 21:39 bar
-rw-r--r-- 1 penguin penguin 0 Oct 16 21:35 foo

如果你需要在 shell 脚本中访问“ls -l”显示的信息,你需要使用相关命令,如test(1),stat(1)和readlink(1)。shell 内置命令,如“[”或“test”,可能也会用到。

控制新建文件的权限:umask

什么权限将应用到新建文件受 shell 内置命令 umask 的限制。参见dash(1),bash(1),和内建命令(7)。

Shell
 (file permissions) = (requested file permissions) & ~(umask value)
umask值创建的文件权限创建的目录权限用法
0022-rw-r--r---rwxr-xr-x仅所属用户可写
0002-rw-rw-r---rwxrwxr-x仅所属组可写

deepin 默认使用用户私人组(UPG)。每当一个新用户添加到系统的时候都会创建一个UPG。UPG 的名字和创建它的用户相同,这个用户是这个UPG的唯一成员。自从每个用户都有自己的私人组之后,把umask设置成0002变得更安全了。(在某些 Unix 变体中,把所有普通用户设置到一个叫**users**的组是非常常见的做法,在这种情况下,出于安全考虑把umask设为0022是一个好主意)

一组用户的权限(组)

为了使组权限应用到一个特定用户,这个用户需要通过使用 “sudo vigr” 编辑 /etc/group 以及使用 “sudo vigr -s” 编辑 /etc/gshadow 成为该 组的成员。你需要注销之后重新登录(或运行 “exec newgrp”)以启用新 的组配置。

或者,你可以通过添加一行 “auth optional pam_group.so”到 “/etc/ pam.d/common-auth” 以及配置 “/etc/security/group.conf” ,使得在身 份验证过程动态添加用户到组。(参见第 4 章认证和访问控制。)

在 deepin 系统中,硬件设备是另一种文件。如果你从一个用户账户访问 某些设备出现问题,例如CD-ROM和U盘,你需要使这个用户成为相关 组的成员。 一些著名的由系统提供的组允许其成员不需要 root 权限访问某些特定的 文件和设备。

可访问文件和设备的描述
dialout完全及直接的访问串口端口(“/dev/ttyS[0-3]”)
dip有限的访问串口,创建到信任点的拨号 IP 连接
cdromCD-ROM, DVD+/-RW 驱动器
audio音频设备
video视频设备
scanner扫描仪
adm系统监控日志
staff一些用于初级管理工作的目录:“/usr/local”,“/home”

你需要属于 dialout 组才能重配置调制解调器、拨号到任意地方,等等。 但如果root 用户在 “/etc/ppp/peers/” 为受信任点创建了预定义配置文 件的话,你只需要属于dip 组,就可以创建拨号 IP来连接到那些受信任的 点上,需使用的命令行工具包括 pppd(8)、pon(1)以及poff(1)。

某些著名的由系统提供的组允许它们的成员不带 root 权限运行特定的命 令。

可访问命令
sudo不带它们的密码运行 sudo
lpadmin执行命令以从打印机数据库添加、修改、移除打印机

由系统提供的用户和组的完整列表,参见由 base-passwd包提供的/usr/ share/doc/base-passwd/users-and-groups.html中,当前版本的“用户和 组”。 用户和组系统的管理命令,参见passwd(5),group(5),shadow(5), newgrp(1),vipw(8),vigr(8),以及pam_group(8)。

时间戳

GNU/Linux 文件有三种类型的时间戳。

类型含义(历史上 Unix 的定义)
mtime文件修改时间(ls -1)
ctime文件状态修改时间 (ls -lc)
atime文件最后被访问的时间 (ls -lu)

ctime 不是文件创建时间。

atime在 GNU/Linux 系统上的真实值可能和历史上 Unix 的定义有所不同。

  • 覆盖一个文件,将会改变该文件所有的 mtime, ctime, 和 atime 属性。

  • 改变文件的所有者或者权限,将改变文件的 ctime 和 atime 属性。

  • 在历史上的 Unix 系统中,读取一个文件将改变文件的 atime 属性。

  • 读一个文件,将改变文件的 atime属性;在 GNU/Linux 系统上,这仅发生在其文件系统使用“strictatime”参数挂载的情况下。

  • 如果 GNU/Linux 系统的文件系统使用 "relatime" 选项挂载,第一次读文件,或者随后读文件,将改变该文件的 atime 属性. (从 Linux 2.6.30 开始的默认行为)

  • 如果 GNU/Linux 系统的文件系统使用 "noatime" 挂载,则读一个文件,不会改变这个文件的 atime 属性。

为了在正常的使用场景中能够提升文件系统的读取效率,新增了 "noatime" 和 "relatime" 这两个加载选项。如使用了 "strictatime" 选项,即使简单的文件读操作都伴随着更新 atime 属性这个耗时的写操作。但是 atime 属性除了 mbox(5) 文件以外却很少用到。详情请看 mount(8)

使用 touch(1) 命令修改已存在文件的时间戳。

对于时间戳,在非英语区域(“fr_FR.UTF-8”),ls 命令输出本地化字符串

Shell
$ LANG=C  ls -l foo
-rw-rw-r-- 1 penguin penguin 0 Oct 16 21:35 foo
$ LANG=en_US.UTF-8  ls -l foo
-rw-rw-r-- 1 penguin penguin 0 Oct 16 21:35 foo
$ LANG=fr_FR.UTF-8  ls -l foo
-rw-rw-r-- 1 penguin penguin 16 21:35 foo

参考第 9.3.4 节 “定制时间和日期的显示” 自定义 “ls -l” 输出 。

链接

有两种方法把一个文件 “foo” 链接到一个不同的文件名 “bar”。

硬链接

  • 对现有文件重复名称

  • ln foo bar

  • 符号链接或 symlink

    • 通过名字指向另一个文件的特殊文件

    • ln -s foo bar

请参阅下面的示例,rm 命令结果中链接数的变化和细微的差别。

Shell
$ umask 002
$ echo "Original Content" > foo
$ ls -li foo
1449840 -rw-rw-r-- 1 penguin penguin 17 Oct 16 21:42 foo
$ ln foo bar     # hard link
$ ln -s foo baz  # symlink
$ ls -li foo bar baz
1449840 -rw-rw-r-- 2 penguin penguin 17 Oct 16 21:42 bar
1450180 lrwxrwxrwx 1 penguin penguin  3 Oct 16 21:47 baz -> foo
1449840 -rw-rw-r-- 2 penguin penguin 17 Oct 16 21:42 foo
$ rm foo
$ echo "New Content" > foo
$ ls -li foo bar baz
1449840 -rw-rw-r-- 1 penguin penguin 17 Oct 16 21:42 bar
1450180 lrwxrwxrwx 1 penguin penguin  3 Oct 16 21:47 baz -> foo
1450183 -rw-rw-r-- 1 penguin penguin 12 Oct 16 21:48 foo
$ cat bar
Original Content
$ cat baz
New Content

硬链接可以在同一个文件系统内创建,并共用同一个 inode 号,由ls(1)带 “-i”选项显示。

符号链接总是名义上具有“rwxrwxrwx”的文件访问权限,如上面例子所示,实际的有效访问权限由它所指向的文件确定。

除非你有非常好的理由,否则不要创建一个复杂的符号链接或硬链接通常是个好主意。符号链接的逻辑组合可能导致文件系统噩梦般的无限循环。

通常使用符号链接比使用硬链接更合适,除非你有一个好理由使用硬链接。

.”目录链接到它所在的目录,因此任何新建目录的链接数从2开始。“..”目录链接到父目录,因此目录的链接数随着新的子目录的创建而增加。

如果你刚从 Windows 迁移到Linux,你很快将清楚 Unix 的文件名链接相较于 Windows 最相近的“快捷方式”是多么精心设计的。由于它是在文件系统中实现的,应用无法看到链接文件跟原始文件之间的区别。在硬链接这种情况,这真的是毫无差别。

命名管道(先进先出)

命名管道是一个像管道一样的文件。你把内容放进了文件,它从另一端出来。因此,它被称为FIFO,即先进先出:你从管道这端先放进去的东西会从另一端先出来。

如果对一个命名管道进行写入操作,写入的过程不会被终止,直到写入的信息从管道中被读取出来。读取过程将会持续到没有信息可以读取为止。管道的大小始终是零,它不存储数据,它只是连接两个过程,像 shell 提供的 1| 2"语法功能一样。然而,一旦管道有了名称,这两个进程就可以不必在同一个命令行,甚至由同一个用户运行。管道是 UNIX 的一个非常有影响力的创新。

尝试下列例子

Shell
$ cd; mkfifo mypipe
$ echo "hello" >mypipe & # put into background
[1] 8022
$ ls -l mypipe
prw-rw-r-- 1 penguin penguin 0 Oct 16 21:49 mypipe
$ cat mypipe
hello
[1]+  Done                    echo "hello" >mypipe
$ ls mypipe
mypipe
$ rm mypipe

套接字

套接字被广泛应用于所有的互联网通信,数据库和操作系统本身。它类似于命名管道(FIFO)并且允许进程之间甚至不同计算机之间进行信息交换。对于套接字,这些进程不需要在同一时间运行,也不需要是同一个父进程的子进程。它是进程间通信(IPC)的一个节点。信息的交换可能会通过网络发生在不同主机之间。最常见的两种是 互联网套接字UNIX域套接字

通过 "netstat -an" 命令可以很方便的查看系统已经打开了哪些套接字。

设备文件

设备文件包括系统的物理设备和虚拟设备,如硬盘、显卡、显示屏、键盘。虚拟设备的一个例子是控制台,用“/dev/console”来描述。

设备文件有两种类型。

  • 字符设备

    • 每次访问一个字符

    • 一个字符等于一个字节

    • 如键盘、串口…

  • 块设备

    • 通过更大的单元–块,进行访问

    • 一个块>一个字节

    • 如硬盘等…

你可以读写块设备文件,尽管该文件可能包含二进制数据,读取后显示出无法理解的乱码。向文件写入数据,有时可以帮助定位硬件连接故障。比如,你可以将文本文件导入打印机设备“/dev/lp0”,或者将调制解调命令发送到合适的串口“/dev/ttyS0”。但是,除非这些操作都小心完成,否则可能会导致一场大灾难。所以要特别小心。

常规访问打印机,使用lp(1)。

设备的节点数可以通过执行ls(1)得到,如下所示。

Shell
$ ls -l /dev/sda /dev/sr0 /dev/ttyS0 /dev/zero
brw-rw---T  1 root disk     8,  0 Oct 16 20:57 /dev/sda
brw-rw---T+ 1 root cdrom   11,  0 Oct 16 21:53 /dev/sr0
crw-rw---T  1 root dialout  4, 64 Oct 16 20:57 /dev/ttyS0
crw-rw-rw-  1 root root     1,  5 Oct 16 20:57 /dev/zero
  • "/dev/sda"的主设备号是8,次设备号是0。它可以被disk群组的用户读写。

  • "/dev/sr0"的主设备号是11,次设备号是0。它可以被cdrom群组的用户读写。

  • "/dev/ttyS0"的主设备号是4,次设备号是64。它可以被dailout群组的用户读写。

  • "/dev/zero"的主设备号是1,次设备号是5。它可以被任意用户读写。

在现代Linux系统中,处在"/dev"之下的文件系统会自动被udev()机制填充。

特殊设备文件

还有一些特殊的设备文件。

设备文件操作响应描述
/dev/null读取返回“文件结尾字符(EOF)“
/dev/null写入无返回(一个无底的数据转存深渊)
/dev/zero读取返回"\0空字符"(与ASCII中的数字0不同)
/dev/random读取从真随机数产生器返回一个随机字符,供应真熵(缓慢)
/dev/urandom读取从能够安全加密的伪随机数产生器返回一个随机字符
/dev/full写入返回磁盘已满(ENOSPC)错误

这些特别设备文件经常和 shell 数据重定向联合使用(参考第 1.5.8 节 “典型的顺序命令和 shell 重定向”)。

procfs 和 sysfs

procfssysfs两个伪文件系统,分别加载于"/proc"和"/sys"之上,将内核中的数据结构暴露给用户空间。或者说,这些条目是虚拟的,他们打开了深入了解操作系统运行的方便之门。

目录"/proc"为每个正在运行的进程提供了一个子目录,目录的名字就是进程标识符(PID)。需要读取进程信息的系统工具,如ps(),可以从这个目录结构获得信息。

"/proc/sys"之下的目录,包含了可以更改某些内核运行参数的接口。(你也可以使用专门的sysctl()命令修改,或者使用其预加载/配置文件"/etc/sysctl.conf"。)

当人们看到这个特别大的文件"/proc/kcore"时,常常会惊慌失措。这个文件于你的的电脑内存大小相差不多。它被用来调试内核。它是一个虚拟文件,指向系统内存,所以不必担心它的大小。

"/sys"以下的目录包含了内核输出的数据结构,它们的属性,以及它们之间的链接。它同时也包含了改变某些内核运行时参数的接口。

参考"proc.txt(.gz)","sysfs.txt(.gz)",以及其他相关的Linux内核文档("/usr/share/doc/linux-doc-*/Documentation/filesystems/*"),这些文件由linux-doc-*软件包提供。

tmpfs

tmpfs是一个临时文件系统,它的文件都保存在虚拟内存中。必要时,位于内存页缓存的tmpfs数据可能被交换到硬盘中的交换分区

系统启动早期阶段,"/run"目录挂载为tmpfs。这样即使"/"挂载为只读,它也是可以被写入的。它为过渡态文件提供了新的存储空间,同时也替代了Filesystem Hierarchy Standar2.3版中说明的目录位置:

  • "/var/run" → "/run"

  • "/var/lock" → "/run/lock"

  • "/dev/shm" → "/run/shm"

参考"tmpfs.txt(.gz)", 文件位于Linux内核文档("/usr/share/doc/linux-doc-*/Documentation/filesystems/*")目录之下,由软件包linux-doc-*提供。