zoukankan      html  css  js  c++  java
  • TLPI读书笔记第14章-系统编程概念2

    14.7 单根目录节点及挂载点

    与其他 UNIX 系统一样, Linux 上所有文件系统中的文件都位于单根目录树下,树根就是根目录“ /”。其他的文件系统都挂载在根目录之下,被视为整个目录层级的子树( subtree)。 超级用户可使用如下命令来挂载文件系统: mount device diriectory

    这条命令会将名为 device 的文件系统挂接到目录层级中由 directory 所指定的目录,即文 件系统的挂载点。可使用 unmount 命令卸载文件系统,然后在另一个挂载点再次挂载文件系 统,从而改变文件系统的挂载点

    不带任何参数来执行 mount 命令,可以列出当前已挂载的文件系统

    14.8 文件系统的挂载和卸载

    系统调用 mount()和 umount()运行特权级进程(CAP_SYS_ADMIN)以挂载或卸载文件系统。大多数 UNIX 实现都提供了这两个系统调用。 在讨论这两个系统调用之前,需要先了解以下 3 个文件,其中包含了当前已挂载或可挂载的文件系统信息。

    1.通过 Linux 专有的虚拟文件/proc/mounts,可查看当前已挂载文件系统的列表。/proc/mounts 是内核数据结构的接口,因此总是包含已挂载文件系统的精确信息。

    2.mount(8) 和 umount(8) 命 令 会自 动维 护 /etc/mtab 文 件, 该文 件 所包 含的 信 息与/proc/mounts 的内容相类似,只是略微详细一些。特别是, etc/mtab 包含了传递给mount(8)的文件系统专有选项,这并未在/proc/mounts 中出现。但是,因为系统调用mount()和 umount()并不更新/etc/mtab,如果某些挂载或卸载了设备的应用程序没有更新该文件,那么/etc/mtab 可能会变得不准确。

    3./etc/fstab(由系统管理员手工维护)包含了对系统支持的所有文件系统的描述,该文件可供 mount(8)、umount(8)以及 fsck(8)所用

    /proc/mounts、 /etc/mtab 和/etc/fstab 的格式相同,请参考 fstab(5)手册页。以下示例摘自/proc/mounts 中的一条记录

    这条记录包含了 6 个字段。 1. 已挂载设备名。 2. 设备的挂载点。 3. 文件系统类型。 4. 挂载标志。上例的 rw 表示以可读写方式挂载文件系统。 5. 一个数字, dump(8)会使用其来控制对文件系统的备份操作。只有/etc/fstab 文件才会用到该字段和第 6 个字段,在/proc/mounts 和/etc/mtab 中,该字段总是为 0。 6. 一个数字,在系统引导时,用于控制 fsck(8)对文件系统的检查顺序。getfsent(3)和 getmntent(3)手册页记录了用于从上述文件中读取记录的函数

    14.8.1 挂载文件系统
    #include<sys/mount.h>
    int mount(const char *source,const char *target,const char *fstype,unsiged long mountflags,const void *data);

    头两个参数分别命名为 source 和 target,其原因在于,除了将磁盘文件系统挂载到一目录下之外, mount()还可以执行其他任务。 参数 fstype 是一字符串,用来标识设备所含文件系统的类型,比如, ext4 或 btrfs。 参数 mountflags 为一位掩码,通过对表 14-1 中所示的 0 个或多个标志进行或( OR)操作而得出,稍后将做详细介绍

    mount()的最后一个参数 data 是一个指向信息缓冲区的指针,对其信息的解释则取决于文件系统。就大多数文件系统而言,该参数是一字符串,包含了以逗号分隔的选项设置。在mount(8)手册页中,有这些选项的完整列表。若未见之于 mount(8)手册页,请查找相关文件系统的文档。 mountflags 参数是标志的位掩码,用来修改 mount()操作。在 mountflags 中,可以指定 0到多个如下标志:

    MS_BIND(始于 Linux 2.4) 用来建立绑定挂载。 14.9.4 节将描述这一特性。如果指定了该标志,那么 mount()会忽略fstype、 data 参数,以及 mountflags 中除 MS_REC 之外的标志(见后续描述)。

    MS_DIRSYNC(始于 Linux 2.6) 用来同步更新路径。该标志的效果类似于 open()的 O_SYNC 标志(参见 13.3 节),但只针对路径。后面介绍的 MS_SYNCHRONOUS 提供了 MS_DIRSYNC 功能的超集,可同时同步更新文件和目录。 采用 MS_DIRSYNC 标志的应用程序在确保同步更新目录(比如, open(pathname,O_CREAT)、 rename()、 link()、 unlink()、 symlink()以及 mkdir())的同时,还无需消耗同步更新文件所带来的成本。 FS_DIRSYNC_FL 标志的用途与之相近,其区别在于可将 MS_DIRSYNC应用于单个目录。此外,在 Linux 上,针对指代目录的文件描述符调用 fsync(),可对目标目录进行更新。 (SUSv3 并未对 fsync()的这一 Linux 专有行为加以规范。 )

    MS_MANDLOCK

    允许对该文件系统中的文件强行锁定记录。第 55 章将描述记录锁定。

    MS_MOVE 将由 source 指定的现有挂载点移到由 target 指定的新位置,整个动作为一原子操作,不可分割。这与 mount(8)命令的--move 选项相对应。实际上,这等同于卸载子树,并将其重新装载到另一位置,只是卸载子树的时点并无意义。 source 参数为一字符串,其内容应与前一个mount()调用中的 target 相同。一旦使用了这一标志,那么 mount()将忽略 fstype、 data 参数 以及 mountflags 中的其他标志。

    MS_NOATIME 针对该文件系统中的文件,不更新其最后访问时间。与下面将要介绍的 MS_NODIRATIME 标志一样,使用该标志意在消除额外的磁盘访问,避免在每次访问文件时都去更新文件 i 节点。 对某些应用程序来说,维护这一时间戳意义不大,而放弃这一做法还能显著提升性能。 MS_NOATIME 标志与 FS_NOATIME_FL 标志(见 15.5 节)目的相似,区别在于可将FS_NOATIME_FL 标志应用于单个文件。 此外, Linux 还可以运用 open()的 O_NOATIME 标志,来提供类似功能,可以针对打开的单个文件来选择这一行为(参见 4.3.1 节)。

    MS_NODEV 不允许访问此文件系统上的块设备和字符设备。设计这一特性的目的是为了保障系统安全,规避如下情况:假设用户插入了可移动磁盘,而磁盘中又包含了可随意访问系统的设备 专有文件。

    MS_NODIRATIME 不更新此文件系统中目录的最后访问时间(该标志提供了 MS_NOATIME 标志的部分功能, MS_NOATIME 标志不会对所有文件类型的最后访问时间进行更新)。

    MS_NOEXEC 不允许在此文件系统上执行程序(或脚本)。该标志用于文件系统包含非 Linux 可执行文件的场景。

    MS_NOSUID 禁用此文件系统上的 set-user-ID 和 set-group-ID 程序。这属于安全特性,意在防止用户从 可移动磁盘上运行 set-user-ID 和 set-group-ID 程序。

    MS_RDONLY 以只读方式挂载文件系统,在此文件系统上既不能创建文件,也不能修改现有文件。

    MS_REC(始于 Linux 2.4.11) 该标志与其他标志(比如, MS_BIND)结合使用,以递归方式将挂载动作施之于子树下的所有挂载。

    MS_RELATIME (始于 Linux 2.6.20) 在此文件系统中,只有当文件最后访问时间戳的当前值小于或等于最后一次修改或状态变更的时间戳时,才对其进行更新。这不但吸取了 MS_NOATIME 性能上的一些优点,而且还可应用于如下场景:程序能了解到,自上次更新以来,有无读取过文件。自 Linux 2.6.30 以来,系统会默认提供 MS_RELATIME 的行为(除非明确指定了 MS_NOATIME 标志),要获取传统行为,必须使用 MS_STRICTATIME 标志。此外,只要文件最后访问时间戳距今超过 24小时,即便其大于最近修改和状态改变时间戳,系统仍会更新该文件的最后访问时间戳。(该 标志对于监控目录的系统程序来说极为有用,可以了解最近有无对文件进行过访问。 )

    MS_REMOUNT 针对已挂载的文件系统,改变其 mountflag(装备标记)和 data(数据)使用该标志时, source 和 target 参数应该与最初用于 mount()系统调用的参数相同,而对 fstype 参数则予以忽略。使用该标志可以避免对磁盘进行卸载和重新挂载,在某些场合中,这是不可能做到的。

    比方说,如果有进程打开了文件系统上的文件,或进程的当 前工作目录位于文件系统之内(对 root 文件系统来说,情况总是如此),就无法卸载相应的文件系统。

    使用 MS_REMOUNT 的另一场景是 tmpfs(基于内存的)文件系统,一旦卸载了这一文件系统, 其内容便会丢失。 并非所有的 mountflag 都是可修改的, 具体信息请参考 mount(2)手册页。 MS_STRICTATIME(始于 Linux 2.6.30) 只要访问此文件系统上的文件,就总是更新文件的最后访问时间戳。 Linux 2.6.30 之前,这是系统 的 默认行为 。 只要 定义 了 MS_STRICTATIME ,即使在 mountflag 中定义了MS_NOATIME 和 MS_RELATIME,也会将其忽略。 MS_SYNCHRONOUS 对文件系统上的所有文件和目录保持同步更新。 (对文件来说,就如同总是以 O_SYNC标志调用 open()来打开文件一样。 )

    14.8.2 卸载文件系统: umount()和 umount2()
    #include<sys/mount.h>
    int unmount(const char *target);
    int unmount2(const char *target ,int flags);

    target 参数指定待卸载文件系统的挂载点

    无法卸载正在使用中的( busy)文件系统,意即这一文件系统上有文件被打开,或者进程的当前工作目录驻留在此文件系统下。针对使用中的文件系统调用 umount(),系统会返回EBUSY 错误。 系统调用 umount2()是 umount()的扩展版。通过 flags 参数, umount2()可对卸载操作施以更精密的控制

    这一标志位掩码参数由下列 0 个或多个值相或( OR)而成。 MNT_DETACH(始于 Linux 2.4.11) 执行 lazy 卸载。对挂载点加以标记,一方面允许已使用了挂载点的进程得以继续使用,同时禁止任何其他进程对该挂载点发起新的访问。当所有进程不再使用访问点时,系统会卸载相应的文件系统。 MNT_EXPIRE (始于 Linux 2.6.8) 将挂载点标记为到期( expired)。若首次调用 umount2()时指定了该标志,且挂载点处于空闲状态,则该调用将以失败告终,并返回 EAGAIN 错误,同时将挂载点标记为到期。(如果挂载点处于在用状态, 那么调用也将失败, 并返回 EBUSY 错误, 但不会将挂载点标记为到期。 ) 只要无任何后续进程发起对挂载点的访问,该挂载点便会一直保持到期状态。再度调用umount2()时,如指定 MNT_EXPIRE 标志,将卸载到期的挂载点。这就提供了一种机制,以卸载在某段时间内未用的文件系统。该标志不能与 MNT_DETACH 或 MNT_FORCE 标志一并使用。 MNT_FORCE 即便文件系统(只对 NFS 挂载有效)处于在用状态,依然将其强行卸载。采用这一选项可能会造成数据丢失。 UMOUNT_NOFOLLOW(始于 Linux 2.6.34) 若 target 为符号链接,则不对其进行解引用。该标志专为某些 set-user-ID-root 程序而设计—此类程序允许非特权级用户执行卸载操作,意在避免安全性问题的发生(例如,若 target为符号链接,且被改变以指向另外的位置

    14.9 高级挂载特性

    14.9.1 在多个挂载点挂载文件系统

    内核版本 2.4 之前,一个文件系统只能挂载于单个挂载点。从内核版本 2.4 开始,可以将一个文件系统挂载于文件系统内的多个位置。由于每个挂载点下的目录子树内容都相同,在一个挂载点下对目录子树所做的改变,同样可见诸于其他挂载点

    14.9.2 多次挂载同一挂载点

    在内核版本 2.4 之前,一个挂载点只能使用一次。从内核 2.4 开始, Linux 允许针对同一挂载点执行多次挂载。每次新挂载都会隐藏之前可见于挂载点下的目录子树。卸载最后一次挂载时,挂载点下上次挂载的内容会再次显示

    在现有且在用的挂载点上执行新的挂载操作是此类堆叠挂载的用法之一。持有打开文件描述符的进程、建立 chroot 监禁区( jail)的进程,以及工作目录位于老挂载点之下的进程将继续在旧有挂载下运行, 而针对挂载点发起新访问的进程将使用新挂载。 结合 MNT_DETACH 标志下的 unmount 操作,则无需将文件系统置为单用户模式,即可为其提供平滑迁移。

    14.9.3 基于每次挂载的挂载标志

    在内核 2.4 版本以前,文件系统和挂载点之间是一一对应的关系。由于从 Linux 2.4 开始,这一特征不再适用,故而 14.8.1 节所述的某些 mountflag 标志值可以基于每次挂载来设置。这包括 MS_NOATIME (始于 Linux 2.6.16)、 MS_NODEV、 MS_NODIRATIME (始于 Linux 2.6.16)、MS_NOEXEC、 MS_NOSUID、 MS_RDONLY (始于 Linux 2.6.26),以及 MS_RELATIME。以下 shell 会话演示了使用 MS_NOEXEC 标志的效果

    14.9.4 绑定挂载

    始于内核版本 2.4, Linux 支持了创建绑定挂载。绑定挂载(由使用 MS_BIND 标志的 mount()调用来创建)是指在文件系统目录层级的另一处挂载目录或文件。这将导致文件或目录在两处同时可见。

    绑定挂载有些类似于硬链接,但存在两个方面的差异。 1.绑定挂载可以跨越多个文件系统挂载点,甚至不拘于 chroot 监禁区( jail)。 2.可针对目录执行绑定挂载。 可使用 mount(8)的 bind 选项,在 shell 中创建绑定挂载,如下面几个例子所示。 第一个例子在另一处绑定挂载了一个目录,并展示了在一处目录中所创建的文件,对另一处目录同样可见。

    绑定挂载的应用场景之一是创建 chroot 监禁区( jail)(参见 18.12 节)。在监禁区下,无需将各种标准目录(诸如/lib)复制过来,为这些路径创建绑定挂载(可能是以只读方式)即可轻而易举地解决问题。

    14.9.5 递归绑定挂载

    默认情况下, 如果使用 MS_BIND 为某个目录创建了绑定挂载,那么只会将该目录挂载到新位置。假设源目录下还存在子挂载( submount),则不会将这些子挂载复制到挂载 target 之下。 Linux 2.4.11 添加了 MS_REC 标志,若与 MS_BIND 相或( OR)并作为标志参数的一部分传入 mount(),则会将子挂载复制到挂载目标下,此之谓递归绑定挂载。

    采用 mount(8)命令所提供的--rbind 选项,可在 shell 中完成相同任务,参见如下 shell 会话。首先创建了一个目录树(src1),并将其挂载在 top 之下。 top 目录树下( top/sub),包括了一个子挂载(src2)

    14.10 虚拟内存文件系统tmpfs

    到目前为止,本章已论及的所有文件系统均驻留在磁盘之上。然而, Linux 同样支持驻留于内存中的虚拟文件系统。对应用程序来说,此类文件系统看起来与任何其他文件系统别无二致—可施以相同操作( open()、 read()、 write()、 link()、 mkdir()等)。

    不过,二者之间还是存在一个重要差别:由于不涉及磁盘访问,虚拟文件系统的文件操作速度极快。 在 Linux 上,已经开发出了林林总总基于内存的文件系统。迄今为止,其中最为复杂的则非 tmpfs 文件系统莫属,该系统在 Linux 2.4 中首度出现。较之于其他基于内存的文件系统,其独特之处在于它属于虚拟内存文件系统。这意味着,该文件系统不但使用 RAM,而且在RAM 耗尽的情况下,还会利用交换空间。 (虽然此处描述的 tmpfs 文件系统为 Linux 所专有,但大多数 UNIX 实现都提供某种形式的基于内存的文件系统。 ) tmpfs 文件系统是一个 Linux 内核的可选组件,通过 CONFIG_TMPFS 选项加以配置。 要创建 tmpfs 文件系统,请使用如下形式的命令:

    mount -t tmpfs source target

    其中“ source”可以是任意名称,其唯一的意义是在/proc/mounts 中“抛头露面”,并通过mount 和 df 命令显示出来。与往常一样, target 是该文件系统的挂载点。请注意,无需使用mkfs 预先创建一个文件系统,内核会将此视为 mount()系统调用的一部分自动加以执行。 作为使用 tmpfs 的例子之一,可采用堆叠挂载(无需顾忌/tmp 目录目前是否处于在用状态),创建一 tmpfs 文件系统并将其挂载至/tmp,如下所示:

    mount -t tmpfs newtmp  /tmp

    有时,会使用如上命令(或/etc/fstab 中的等价条目)来改善应用程序(比如,编译器)的性能,此类应用程序因创建临时性文件而频繁使用/tmp 目录。

    默认情况下,允许将 tmpfs 文件系统的大小提高至 RAM 容量的一半,但在创建文件系统或之后重新挂载时, 可使用 mount 的 size=nbytes 选项为该文件系统的大小设置不同的上限值。

    ( tmpfs 文件系统仅会根据其当前所持有的文件来消耗内存和交换空间。 )一旦卸载 tmpfs 文件系统,或者遭遇系统崩溃,那么该文件系统中的所有数据都将丢失,“ tmpfs”正是得名于此。

    除了用于用户应用程序以外, tmpfs 文件系统还有以下两个特殊用途

    由内核内部挂载的隐形 tmpfs 文件系统,用于实现 System V 共享内存和共享匿名内存映射 挂载于/dev/shm 的 tmpfs 文件系统, 为 glibc 用以实现 POSIX 共享内存和 POSIX 信号量

    14.11 获得与文件系统有关的信息: statvfs()

    statvfs()和 fstatvfs()库函数能够获得与已挂载文件系统有关的信息

    #include<sys/statvfs.h>
    int statvfs(const char *pathname,struct statvfs *statvfsbuf);
    int fstatvfs(int fd,struct statvfs *statvfsbuf);

    两者之间唯一的区别在于其标识文件系统的方式。 statvfs()需使用 pathname 来指定文件系 统中任一文件的名称。而 fstatvfs()则需使用打开文件描述符 fd,来指代文件系统中的任一文 件。二者均返回一个 statvfs 结构,属于由 statvfsbuf 所指向的缓冲区,其中包含了关乎文件系 统的信息。 statvfs 结构的形式如下

    struct statvfs{
        unsiged long f_bsize; /*块大小*/
        unsiged long f_frsize;/*块大小*/
        
        fsblkcnt_t f_blocks;  /*总块数目*/
        fsblkcnt_t f_bfree;   /*空闲块数*/
        fsblkcnt_t f_bavail;  /*可用块数*/
        
        fsfilcnt_t f_files;   /*i-node节点数*/
        fsfilcnt_t f_ffree;   /*空闲i-node节点数*/
        fsfilcnt_t f_favail;  /*可用i-node节点数*/
        
        unsiged long f_fsid;  /*文件系统ID*/
        unsiged long f_flag;  /*mount flag*/
        unsiged long f_namemax;/*当前文件系统最大文件名长度*/
    };

    上述注释已然清晰地描述出 statvfs 结构中大多数字段的用途。对其中一些字段,这里还要深入交代几句。 fsblkcnt_t 和 fsfilcnt_t 数据类型是由 SUSv3 所定义的整型。 对绝大多数 Linux 文件系统而言, f_bsize 和 f_frsize 的取值是相同的。然而,某些文件系统支持块片段的概念,在无需使用完整数据块的情况下,可在文件尾部分配较小的存储单元,从而避免因分配完整块而导致的空间浪费。在此类文件系统上, f_frsize和 f_bsize 分别为块片段和整个块的大小。 许多原生 UNIX 和 Linux 文件系统,都支持为超级用户预留一部分文件系统块,如此一来,即便在文件系统空间耗尽的情况下,超级用户仍可以登录系统解决故障。 如果文件系统中确有预留块,那么 statvfs 结构中 f_bfree 和 f_bavail 字段间的差值则为预留块数。 f_flag 字段是一个位掩码标志,用于挂载文件系统。也就是说,该字段所包含的信息类似于传入 mount(2)的 mountflags 参数。然而,该字段所使用的标志位在命名时均冠以 ST,这不同于 mountflags 中冠以 MS的命名手法。 SUSv3 仅规范了 ST_RDONLY和 ST_NOSUID 常量,而 glibc 实现则支持与 MS_系列(参见 mount()中对 mountflags参数的描述)相对应的全系列常量。 某些 UNIX 实现会使用 f_fsid 字段来返回文件系统的唯一标识符,比方说,根据文件系统所驻留设备的标识符来取值。对大多数 UNIX 实现来说,该字段为 0。

    函数与系统调用

    SUSv3 规范了 statvfs()和 fstatvfs()。对于 Linux二者均位于与其颇为相似的 statfs()和 fstatfs()系统调用之上。 以下列出函数与系统调用间的主要区别。 statvfs()和 fstatvfs()函数均返回 f_flag 字段, 内含关于文件系统的挂载标志信息。( glibc实现通过扫描/proc/mounts 或/etc/mtab 来获取上述信息。 ) statfs()和 fstatfs()系统调用返回 f_type 字段, 内含文件系统类型,比如ext2

  • 相关阅读:
    linux 删除乱码文件
    snprintf用法
    面试时经常问到的非技术性问题
    vector查找元素
    new 和delete
    python安装
    UIPickerView详解
    设置文本框左边显示的View
    字符串的分割??
    VC++异常捕获??
  • 原文地址:https://www.cnblogs.com/wangbin2188/p/14647654.html
Copyright © 2011-2022 走看看