在Debian类Unix操作系统中,文件系统是一种层次化的结构,以根目录(”/”)作为起始点。这个结构就像一棵倒置的树,所有的文件和目录都从根目录开始,向下分支。这种设计使得文件的组织和管理变得有序且易于理解。
这些文件和目录可以分散在多个设备中。mount(8)用于把某个设备上找到的文件系统附着到巨大的文件树上。相反的,umount(8)把它再次分离。在最近的 Linux 内核里,mount(8)带某些参数时可以把文件树的一部分绑定到另外的地方,或者可以把文件系统挂载为共享的、私有的、从设备、或不可绑定的。对每个文件系统支持的挂载选项可以在/usr/share/doc/linux-doc-*/Documentation/filesystems/找到。
Unix系统上叫做目录,某些其他系统上叫做文件夹。请同样留意,在任何Unix系统上,没有的驱动器的概念,例如“A:”。这只有一个文件系统,并且所有东西都包含在内。这相对于 Windows 来说是一个巨大的优点。
一、Unix文件基础
下面是一些 Unix 文件基础。
1、文件名是 区分大小写 的。也就是说, “MYFILE” 和 “MyFile” 是不同的文件。
2、根目录意味着文件系统的根,简单的称为“/”,不要把它跟 root 用户的家目录“/root”混淆了。
3、每个目录都有一个名字,它可以包含任意字母或除了/”以外的符号。根目录是个特例。它的名字是“/”(称作“斜线”或“根目录”),并且它不能被重命名。
4、每个文件或目录都被指定一个全限定文件名,绝对文件名,或路径,按顺序给出必须经过的目录从而到达相应目录。这三个术语是同义的。
5、所有的全限定文件名以“/”目录开始,并且在每个目录或文件名之间有一个“/”。第一个“/”是最顶层目录,其他的“/”用于分隔跟着的子目录。直到到达最后的入口,即实际文件的名称。这些话可能会令人困惑。用下面这个全限定文件名作为例子:“/usr/share/keytables/us.map.gz”。不过,人们也把它的基名“us.map.gz”单独作为文件名。
6、根目录有很多分支,例如“/etc/”和“/usr/”。这些子目录依次分出更多的子目录,例如“/etc/init.d/”和“/usr/local/”。这整体叫做“目录树”。可以把一个绝对文件名想象成从“/”这棵树的基到某个分支(一个文件)的结尾的一条路径。也听到人们谈论目录树,就好像它是一个包含所有直系后代的“家庭”树的一个图,这个图叫做根目录(“/”):因此子目录有父目录,并且一条路径显示了一个文件完整的祖先。也有相对路径从其他地方开始,而不是从根目录。 应该还记得目录“../”指向父目录。这个术语也适用于其他类似目录的结构,如分层数据结构。
7、对于一个物理设备, 是没有一个特定的目录路径名来对应的组成部分. 这不同于RT-11, CP/M,OpenVMS,MS-DOS,AmigaOS, 以及微软的Windows,这些系统存在一个路径包含了一个设备名字,比如”C:\”。(尽管如此, 路径条目确实存在引用了物理设备作为正常的文件系统的一部分。)
注意:
- 虽然可以在文件名中使用任意的字幕或者符号, 但是在实际情况下这样做是一个坏主意. 最好避免使用一些在命令行里面含有特殊意义的字符, 比如空格, 制表符, 换行符, 和其它的特殊字符: { } ( ) [ ] ‘ ` ” \ / >< | ; ! #&^ * % @ $。如果想有一个区分度良好的命名, 比较好的选择是利用时期, 连字符和下划线. 也可以每个单词的首字母大写, 这叫大驼峰命名法, 比如这样 “LikeThis”. 经验丰富的Linux用户会趋向于在文件名中不使用空格;
- 这个 “root” 可能既表示 “超级用户root” 又表示 ” 根目录”(/root) . 应该根据上下文确定它的用法;
- 单词path不仅表示包含全限定文件名, 也可能表示命令搜索的路径. 通常路径真实的意思是需要通过上下文来明确。
关于文件层次的最佳详细实践在文件系统层次标准(“/usr/share/doc/debian-policy/fhs/fhs-2.3.txt.gz” 和 hier (7)). 应该记住以下的一些标准作为开始学习的步骤。
重要目录的用途列表:
目录 | 目录用途 |
/ | 根目录 |
/etc/ | 系统范围的配置文件 |
/var/log/ | 系统日志文件 |
/home/ | 所有非特权用户的用户目录 |
二、文件系统
按照UNIX系统的传统,Debian GNU / Linux 的文件系统是在物理数据存储设备诸如磁盘或其他存储设备上,与硬件设备的交互,如控制台和远程串口终端都是以统一的方式呈现在 “/ dev /” 下面。
每个文件、目录、命名管道(一种两个程序间共享数据的方法)或 Debian GNU/Linux 系统上的物理设备都有一个叫做 inode的数据结构,描述了其相关属性,例如拥有它的用户(所有者),它属于的组,最后一次访问时间,等等。把所有东西都表示在文件系统中的想法是来源于 Unix,现代的 Linux 内核则将这个思路进行了扩展。现在,甚至有关计算机上正在运行的进程的信息都可以在文件系统中找到。
这个对物理实体和内部进程的统一和抽象是非常强大的,因为这允许我们用同样的命令对许多完全不同的设备进行同样的操作。甚至可以通过向链接到运行进程的特殊文件写入数据来改变内核的运行方式。如果需要识别文件树和物理实体之间的对应关系,请尝试不带参数运行mount(8)。
三、文件系统权限
类Unix系统的文件系统权限被定义给三类受影响的用户:
- 拥有这个文件的用户(u);
- 这个文件所属组的其他用户(g);
- 所有其余的用户(o),同样称为“世界”和“所有人”。
对文件来说,每个对应权限允许下列动作:
- 可读(r)权限允许所有者检查文件的内容;
- 可写(w)权限允许所有者修改文件内容;
- 可执行(x)权限允许所有者把文件当做一个命令运行。
对于目录来说,每个对应权限允许下列动作:
- 可读(r)权限允许所有者列出目录内的内容;
- 可写(w)权限允许所有者添加或删除目录里面的文件;
- 可执行(x)权限允许所有者访问目录里的文件。
在这里,一个目录的可执行权限意味着不仅允许读目录里的文件,还允许显示他们的属性,例如大小和修改时间。
ls(1)用于显示文件和目录的权限信息(更多)。当运行时带有“-l”选项,它将按给定顺序显示下列信息。
- 文件类型(第一个字母);
- 文件的访问权限(9个字符,三个字符组成一组按照用户、组、其他的顺序表示);
- 链接到文件的硬链接数;
- 文件所有者的用户名;
- 这个文件所属的组名;
- 以字符(字节)为单位的文件大小;
- 文件的日期和时间(mtime);
- 文件的名字。
“ls -l”输出的第一个字符列表:
字符 | 说明 |
– | 普通文件 |
d | 目录 |
l | 符号链接 |
c | 字符设备节点 |
b | 块设备节点 |
p | 命名管道 |
s | 套接字 |
chown(1)用于 root 账户修改文件的所有者。chgrp(1)用于文件的所有者或 root 账户修改文件所属的组。chmod(1)用于文件的所有者或 root 账户修改文件和文件夹的访问权限。操作一个foo文件的基本语法如下 。
# chown newowner foo # chgrp newgroup foo # chmod [ugoa][+-=][rwxXst][,...] foo
例如,可以按照下面使一个目录树被用户foo所有,并共享给组bar。
# cd /some/location/ # chown -R foo:bar . # 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”这样所有人可写或同组可写的目录下文件内容的安全,不仅要去除可写权限,还要给其所在目录设置粘滞位。否则,该文件可以被任意对其所在目录有写权限的用户删除并创建一个同名的新文件。
这里有一点有趣的文件权限例子。
$ 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)数。
chmod(1) 命令文件权限的数字模式:
数字 | 说明 |
第一个可选数字 | Set-User-ID (=4), Set-Group-ID (=2) 和 粘滞位 (=1) 之和 |
第二个数字 | 用户的可读 (=4), 可写 (=2)和 可执行 (=1) 权限之和 |
第三个数字 | 组权限同上 |
第四个数字位 | 其他用户权限同上 |
这听起来很复杂实际上相当简单。如果把“ls -l”命令输出的前几列(2-10),看成以二进制(底为2)表示文件的权限(“-”看成0,“rwx”看成1),应该可以理解用数字模式值的最后3位数字对文件权限的八进制表示。
可以尝试下列例子
$ touch foo bar $ chmod u=rw,go=r 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 的限制。
(file permissions) = (requested file permissions) & ~(umask value)
umask值举例如下表:
umask值 | 创建的文件权限 | 创建的目录权限 | 用法 |
22 | -rw-r–r– | -rwxr-xr-x | 仅所属用户可写 |
2 | -rw-rw-r– | -rwxrwxr-x | 仅所属组可写 |
Debian 默认使用用户私人组(UPG)。每当一个新用户添加到系统的时候都会创建一个UPG。UPG 的名字和创建它的用户相同,这个用户是这个UPG的唯一成员。自从每个用户都有自己的私人组之后,把umask设置成0002变得更安全了。(在某些 Unix 变体中,把所有普通用户设置到一个叫users的组是非常常见的做法,在这种情况下,出于安全考虑把umask设为0022是一个好主意)
通过把 “umask 002” 写入 ~/.bashrc 文件打开UPG。
五、一组用户权限
请确保在重启或执行类似操作前保存未保存的更改。要将组权限应用于特定用户,需要使用 “sudo vigr “和 “sudo vigr -s “分别对 /etc/group 和 /etc/gshadow 进行设置,使该用户成为组的成员。重启后需要登录(或运行 “kill -TERM-1″)以启用新的组配置。或者,可以通过添加一行 “auth optional pam_group.so”到 “/etc/pam.d/common-auth” 以及配置 “/etc/security/group.conf” ,使得在身份验证过程动态添加用户到组。(参见第 4 章 认证和访问控制。)
在 Debian 系统中,硬件设备是另一种文件。如果从一个用户账户访问某些设备出现问题,例如CD-ROM和USB记忆棒,需要使这个用户成为相关组的成员。
一些著名的由系统提供的组允许其成员不需要 root 权限访问某些特定的文件和设备。
关于文件访问的由系统提供的著名组列表如下:
组 | 可访问文件和设备的描述 |
dialout | 完全及直接的访问串口端口(“/dev/ttyS[0-3]”) |
dip | 有限的访问串口,创建到信任点的拨号 IP 连接 |
cdrom | CD-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) 文件以外却很少用到。
使用 touch(1) 命令修改已存在文件的时间戳。对于时间戳,在非英语区域(“fr_FR.UTF-8”),ls 命令输出本地化字符串。
$ 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 0 oct. 16 21:35 foo
七、链接
有两种方法把一个文件 “foo” 链接到一个不同的文件名 “bar”。
1、硬链接
对现有文件重复名称;
“ln foo bar”。
2、符号链接或 symlink
通过名字指向另一个文件的特殊文件;
“ln -s foo bar”。
请参考下面的示例,rm 命令结果中链接数的变化和细微的差别。
$ 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 的一个非常有影响力的创新。
尝试下列例子
$ 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”来描述。
设备文件有两种类型:
1、字符设备
- 每次访问一个字符;
- 一个字符等于一个字节;
- 如键盘、串口…。
2、块设备
- 通过更大的单元–块,进行访问;
- 一个块>一个字节;
- 如硬盘等…。
可以读写块设备文件,尽管该文件可能包含二进制数据,读取后显示出无法理解的乱码。向文件写入数据,有时可以帮助定位硬件连接故障。比如,可以将文本文件导入打印机设备“/dev/lp0”,或者将调制解调命令发送到合适的串口“/dev/ttyS0”。但是,除非这些操作都小心完成,否则可能会导致一场大灾难。所以要特别小心。
常规访问打印机,使用lp(1)。
设备的节点数可以通过执行ls(1)得到,如下所示。
$ 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 数据重定向联合使用。
十二、procfs和sysfs
procfs和sysfs两个伪文件系统,分别加载于”/proc”和”/sys”之上,将内核中的数据结构暴露给用户空间。或者说,这些条目是虚拟的,他们打开了深入了解操作系统运行的方便之门。
目录”/proc”为每个正在运行的进程提供了一个子目录,目录的名字就是进程标识符(PID)。需要读取进程信息的系统工具,如ps(),可以从这个目录结构获得信息。
“/proc/sys”之下的目录,包含了可以更改某些内核运行参数的接口。(也可以使用专门的sysctl()命令修改,或者使用其预加载/配置文件”/etc/sysctl.conf”。)
当人们看到这个特别大的文件”/proc/kcore”时,常常会惊慌失措。这个文件于的电脑内存大小相差不多。它被用来调试内核。它是一个虚拟文件,指向系统内存,所以不必担心它的大小。
“/sys”以下的目录包含了内核输出的数据结构,它们的属性,以及它们之间的链接。它同时也包含了改变某些内核运行时参数的接口。
十三、tmpfs
tmpfs是一个临时文件系统,它的文件都保存在虚拟内存中。必要时,位于内存页缓存的tmpfs数据可能被交换到硬盘中的交换分区。
系统启动早期阶段,”/run”目录挂载为tmpfs。这样即使”/”挂载为只读,它也是可以被写入的。它为过渡态文件提供了新的存储空间,同时也替代了Filesystem Hierarchy Standar2.3版中说明的目录位置:
- “/var/run” → “/run”;
- “/var/lock” → “/run/lock”;
- “/dev/shm” → “/run/shm”。