最近的工作环境是在Linux下的,在工作了两天之后发现对Linux的文件系统不清楚的话很多命令行该去哪里,该怎么开始找都变得十分困难,从而体现出了文件系统的重要性,不求能理解很多,只求遇上能够有所印象。
图片示例_跨文件系统的文件操作
Linux 中允许众多不同的文件系统共存,如 ext2, ext3, vfat 等。通过使用同一套文件 I/O 系统 调用即可对 Linux 中的任意文件进行操作而无需考虑其所在的具体文件系统格式;更进一步,对文件的 操作可以跨文件系统而执行。如图 1 所示,我们可以使用 cp 命令从 vfat 文件系统格式的硬盘拷贝数据到 ext3 文件系统格式的硬盘;而这样的操作涉及到两个不同的文件系统。
“一切皆是文件”是 Unix/Linux 的基本哲学之一。不仅普通的文件,目录、字符设备、块设备、 套接字等在 Unix/Linux 中都是以文件被对待;它们虽然类型不同,但是对其提供的却是同一套操作界面。**对于Linux系统来说一切皆为文件,如果不是文件的,就是进程**
图片示例_一切皆是文件

虚拟文件系统正是实现上述两点 Linux 特性的关键所在。虚拟文件系统(Virtual File System, 简称 VFS), 是 Linux 内核中的一个软件层,用于给用户空间的程序提供文件系统接口;同时,它也提供了内核中的一个 抽象功能,允许不同的文件系统共存。系统中所有的文件系统不但依赖 VFS 共存,而且也依靠 VFS 协同工作。

为了能够支持各种实际文件系统,VFS 定义了所有文件系统都支持的基本的、概念上的接口和数据 结构;同时实际文件系统也提供 VFS 所期望的抽象接口和数据结构,将自身的诸如文件、目录等概念在形式 上与VFS的定义保持一致。换句话说,一个实际的文件系统想要被 Linux 支持,就必须提供一个符合VFS标准 的接口,才能与 VFS 协同工作。实际文件系统在统一的接口和数据结构下隐藏了具体的实现细节,所以在VFS 层和内核的其他部分看来,所有文件系统都是相同的。图3显示了VFS在内核中与实际的文件系统的协同关系。

图片示例_VFS在内核中与其他的内核模块的协同关系

我们已经知道,正是由于在内核中引入了VFS,跨文件系统的文件操作才能实现,“一切皆是文件” 的口号才能承诺。而为什么引入了VFS,就能实现这两个特性呢?在接下来,我们将以这样的一个思路来切入 文章的正题:我们将先简要介绍下用以描述VFS模型的一些数据结构,总结出这些数据结构相互间的关系;然后 选择两个具有代表性的文件I/O操作sys_open()和sys_read()来详细说明内核是如何借助VFS和具体的文件系统打 交道以实现跨文件系统的文件操作和承诺“一切皆是文件”的口号。

 VFS 一些基本概念

从本质上讲,文件系统是特殊的数据分层存储结构,它包含文件、目录和相关的控制信息。为了描述 这个结构,Linux引入了一些基本概念:
文件 一组在逻辑上具有完整意义的信息项的系列。在Linux中,除了普通文件,其他诸如目录、设备、套接字等 也以文件被对待。总之,“一切皆文件”。
目录
目录好比一个文件夹,用来容纳相关文件。因为目录可以包含子目录,所以目录是可以层层嵌套,形成 文件路径。在Linux中,目录也是以一种特殊文件被对待的,所以用于文件的操作同样也可以用在目录上。
目录项 在一个文件路径中,路径中的每一部分都被称为目录项;如路径/home/source/helloworld.c中,目录 /, home, source和文件 helloworld.c都是一个目录项。
索引节点 用于存储文件的元数据的一个数据结构。文件的元数据,也就是文件的相关信息,和文件本身是两个不同 的概念。它包含的是诸如文件的大小、拥有者、创建时间、磁盘位置等和文件相关的信息。
超级块 用于存储文件系统的控制信息的数据结构。描述文件系统的状态、文件系统类型、大小、区块数、索引节 点数等,存放于磁盘的特定扇区中。

如上的几个概念在磁盘中的位置关系如图4所示。

关于文件系统的三个易混淆的概念:

创建 以某种方式格式化磁盘的过程就是在其之上建立一个文件系统的过程。创建文现系统时,会在磁盘的特定位置写入 关于该文件系统的控制信息。

注册 向内核报到,声明自己能被内核支持。一般在编译内核的时侯注册;也可以加载模块的方式手动注册。注册过程实 际上是将表示各实际文件系统的数据结构struct file_system_type 实例化。

安装 也就是我们熟悉的mount操作,将文件系统加入到Linux的根文件系统的目录树结构上;这样文件系统才能被访

如上的几个概念在磁盘中的位置关系如图4所示。

图4. 磁盘与文件系统
图片示例_磁盘与文件系统.jpg

2 VFS数据结构

VFS依靠四个主要的数据结构和一些辅助的数据结构来描述其结构信息,这些数据结构表现得就像是对象; 每个主要对象中都包含由操作函数表构成的操作对象,这些操作对象描述了内核针对这几个主要的对象可以进行的操作。

超级块对象

存储一个已安装的文件系统的控制信息,代表一个已安装的文件系统;每次一个实际的文件系统被安装时, 内核会从磁盘的特定位置读取一些控制信息来填充内存中的超级块对象。一个安装实例和一个超级块对象一一对应。 超级块通过其结构中的一个域s_type记录它所属的文件系统类型。

根据第三部分追踪源代码的需要,以下是对该超级块结构的部分相关成员域的描述,(如下同):

超级块

struct super_block { //超级块数据结构
        struct list_head s_list;                /*指向超级块链表的指针*/
        ……
        struct file_system_type  *s_type;       /*文件系统类型*/
       struct super_operations  *s_op;         /*超级块方法*/
        ……
        struct list_head         s_instances;   /*该类型文件系统*/
        ……
};

struct super_operations { //超级块方法
        ……
        //该函数在给定的超级块下创建并初始化一个新的索引节点对象
        struct inode *(*alloc_inode)(struct super_block *sb);
       ……
        //该函数从磁盘上读取索引节点,并动态填充内存中对应的索引节点对象的剩余部分
        void (*read_inode) (struct inode *);
       ……
};

索引节点对象

索引节点对象存储了文件的相关信息,代表了存储设备上的一个实际的物理文件。当一个 文件首次被访问时,内核会在内存中组装相应的索引节点对象,以便向内核提供对一个文件进行操 作时所必需的全部信息;这些信息一部分存储在磁盘特定位置,另外一部分是在加载时动态填充的。

清单2. 索引节点

struct inode {//索引节点结构
      ……
      struct inode_operations  *i_op;     /*索引节点操作表*/
     struct file_operations   *i_fop;  /*该索引节点对应文件的文件操作集*/
     struct super_block       *i_sb;     /*相关的超级块*/
     ……
};

struct inode_operations { //索引节点方法
     ……
     //该函数为dentry对象所对应的文件创建一个新的索引节点,主要是由open()系统调用来调用
     int (*create) (struct inode *,struct dentry *,int, struct nameidata *);

     //在特定目录中寻找dentry对象所对应的索引节点
     struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
     ……
};

 

目录项对象

引入目录项的概念主要是出于方便查找文件的目的。一个路径的各个组成部分,不管是目录还是 普通的文件,都是一个目录项对象。如,在路径/home/source/test.c中,目录 /, home, source和文件 test.c都对应一个目录项对象。不同于前面的两个对象,目录项对象没有对应的磁盘数据结构,VFS在遍 历路径名的过程中现场将它们逐个地解析成目录项对象。

清单3. 目录项

struct dentry {//目录项结构
     ……
     struct inode *d_inode;           /*相关的索引节点*/
    struct dentry *d_parent;         /*父目录的目录项对象*/
    struct qstr d_name;              /*目录项的名字*/
    ……
     struct list_head d_subdirs;      /*子目录*/
    ……
     struct dentry_operations *d_op;  /*目录项操作表*/
    struct super_block *d_sb;        /*文件超级块*/
    ……
};

struct dentry_operations {
    //判断目录项是否有效;
    int (*d_revalidate)(struct dentry *, struct nameidata *);
    //为目录项生成散列值;
    int (*d_hash) (struct dentry *, struct qstr *);
    ……
};

 

文件对象

文件对象是已打开的文件在内存中的表示,主要用于建立进程和磁盘上的文件的对应关系。它由sys_open() 现场创建,由sys_close()销毁。文件对象和物理文件的关系有点像进程和程序的关系一样。当我们站在用户空间来看 待VFS,我们像是只需与文件对象打交道,而无须关心超级块,索引节点或目录项。因为多个进程可以同时打开和操作 同一个文件,所以同一个文件也可能存在多个对应的文件对象。文件对象仅仅在进程观点上代表已经打开的文件,它 反过来指向目录项对象(反过来指向索引节点)。一个文件对应的文件对象可能不是惟一的,但是其对应的索引节点和 目录项对象无疑是惟一的。

文件对象

struct file {
    ……
     struct list_head        f_list;        /*文件对象链表*/
    struct dentry          *f_dentry;       /*相关目录项对象*/
    struct vfsmount        *f_vfsmnt;       /*相关的安装文件系统*/
    struct file_operations *f_op;           /*文件操作表*/
    ……
};

struct file_operations {
    ……
    //文件读操作
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ……
    //文件写操作
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ……
    int (*readdir) (struct file *, void *, filldir_t);
    ……
    //文件打开操作
    int (*open) (struct inode *, struct file *);
    ……
};

 

其他VFS对象

和文件系统相关

根据文件系统所在的物理介质和数据在物理介质上的组织方式来区分不同的文件系统类型的。 file_system_type结构用于描述具体的文件系统的类型信息。被Linux支持的文件系统,都有且仅有一 个file_system_type结构而不管它有零个或多个实例被安装到系统中。

而与此对应的是每当一个文件系统被实际安装,就有一个vfsmount结构体被创建,这个结构体对应一个安装点。

和文件系统相关

struct file_system_type {
        const char *name;                /*文件系统的名字*/
        struct subsystem subsys;         /*sysfs子系统对象*/
        int fs_flags;                    /*文件系统类型标志*/

        /*在文件系统被安装时,从磁盘中读取超级块,在内存中组装超级块对象*/
        struct super_block *(*get_sb) (struct file_system_type*, 
                                        int, const char*, void *);
        
        void (*kill_sb) (struct super_block *);  /*终止访问超级块*/            
        struct module *owner;                    /*文件系统模块*/
        struct file_system_type * next;          /*链表中的下一个文件系统类型*/
        struct list_head fs_supers;              /*具有同一种文件系统类型的超级块对象链表*/
};

struct vfsmount
{
        struct list_head mnt_hash;               /*散列表*/
        struct vfsmount *mnt_parent;             /*父文件系统*/
        struct dentry *mnt_mountpoint;           /*安装点的目录项对象*/
        struct dentry *mnt_root;                 /*该文件系统的根目录项对象*/
        struct super_block *mnt_sb;              /*该文件系统的超级块*/
        struct list_head mnt_mounts;             /*子文件系统链表*/
        struct list_head mnt_child;              /*子文件系统链表*/
        atomic_t mnt_count;                      /*使用计数*/
        int mnt_flags;                           /*安装标志*/
        char *mnt_devname;                       /*设备文件名*/
        struct list_head mnt_list;               /*描述符链表*/
        struct list_head mnt_fslink;             /*具体文件系统的到期列表*/
        struct namespace *mnt_namespace;         /*相关的名字空间*/
};

 

和进程相关

清单6. 打开的文件集

struct files_struct {//打开的文件集
         atomic_t count;              /*结构的使用计数*/
        ……
         int max_fds;                 /*文件对象数的上限*/
        int max_fdset;               /*文件描述符的上限*/
        int next_fd;                 /*下一个文件描述符*/
        struct file ** fd;           /*全部文件对象数组*/
        ……
 };

struct fs_struct {//建立进程与文件系统的关系
         atomic_t count;              /*结构的使用计数*/
        rwlock_t lock;               /*保护该结构体的锁*/
        int umask;                  /*默认的文件访问权限*/
        struct dentry * root;        /*根目录的目录项对象*/
        struct dentry * pwd;         /*当前工作目录的目录项对象*/
        struct dentry * altroot;    /*可供选择的根目录的目录项对象*/
        struct vfsmount * rootmnt;   /*根目录的安装点对象*/
        struct vfsmount * pwdmnt;    /*pwd的安装点对象*/
        struct vfsmount * altrootmnt;/*可供选择的根目录的安装点对象*/
};

 

和路径查找相关

清单7. 辅助查找

struct nameidata {
        struct dentry  *dentry;     /*目录项对象的地址*/
        struct vfsmount  *mnt;      /*安装点的数据*/
        struct qstr  last;          /*路径中的最后一个component*/
        unsigned int  flags;        /*查找标识*/
        int  last_type;             /*路径中的最后一个component的类型*/
        unsigned  depth;            /*当前symbolic link的嵌套深度,不能大于6*/
        char   *saved_names[MAX_NESTED_LINKS + 1];/
                                    /*和嵌套symbolic link 相关的pathname*/
        union {
            struct open_intent open; /*说明文件该如何访问*/
        } intent;   /*专用数据*/
};

 

2.2.6 对象间的联系

如上的数据结构并不是孤立存在的。正是通过它们的有机联系,VFS才能正常工作。如下的几张图是对它们之间的联系的描述。

如图5所示,被Linux支持的文件系统,都有且仅有一个file_system_type结构而不管它有零个或多个实例被安装到系统 中。每安装一个文件系统,就对应有一个超级块和安装点。超级块通过它的一个域s_type指向其对应的具体的文件系统类型。具体的 文件系统通过file_system_type中的一个域fs_supers链接具有同一种文件类型的超级块。同一种文件系统类型的超级块通过域s_instances链 接。

图5. 超级块、安装点和具体的文件系统的关系
图片示例_超级块,安装点和具体的文件系统的关系

从图6可知:进程通过task_struct中的一个域files_struct files来了解它当前所打开的文件对象;而我们通常所说的文件 描述符其实是进程打开的文件对象数组的索引值。文件对象通过域f_dentry找到它对应的dentry对象,再由dentry对象的域d_inode找 到它对应的索引结点,这样就建立了文件对象与实际的物理文件的关联。最后,还有一点很重要的是, 文件对象所对应的文件操作函数 列表是通过索引结点的域i_fop得到的。图6对第三部分源码的理解起到很大的作用。

图6. 进程与超级块、文件、索引结点、目录项的关系
图片示例_进程与超级块,文件,索引结点,目录项的关系
3 基于VFS的文件I/O

到目前为止,文章主要都是从理论上来讲述VFS的运行机制;接下来我们将深入源代码层中,通过阐述两个具有代表性的系统 调用sys_open()和sys_read()来更好地理解VFS向具体文件系统提供的接口机制。由于本文更关注的是文件操作的整个流程体制,所以我 们在追踪源代码时,对一些细节性的处理不予关心。又由于篇幅所限,只列出相关代码。本文中的源代码来自于linux-2.6.17内核版本。

在深入sys_open()和sys_read()之前,我们先概览下调用sys_read()的上下文。图7描述了从用户空间的read()调用到数据从 磁盘读出的整个流程。当在用户应用程序调用文件I/O read()操作时,系统调用sys_read()被激发,sys_read()找到文件所在的具体文件 系统,把控制权传给该文件系统,最后由具体文件系统与物理介质交互,从介质中读出数据。

图7. 从物理介质读数据的过程
图片示例_从物理介质读数据的过程

3.1 sys_open()

sys_open()系统调用打开或创建一个文件,成功返回该文件的文件描述符。图8是sys_open()实现代码中主要的函数调用关系图。

图8. sys_open函数调用关系图
图片示例_sys_open函数调用关系图

由于sys_open()的代码量大,函数调用关系复杂,以下主要是对该函数做整体的解析;而对其中的一些关键点,则列出其关键代码。

a. 从sys_open()的函数调用关系图可以看到,sys_open()在做了一些简单的参数检验后,就把接力棒传给do_sys_open():

1)、首先,get_unused_fd()得到一个可用的文件描述符;通过该函数,可知文件描述符实质是进程打开文件列表中对应某个文件对象的索引值;

2)、接着,do_filp_open()打开文件,返回一个file对象,代表由该进程打开的一个文件;进程通过这样的一个数据结构对物理文件进行读写操作。

3)、最后,fd_install()建立文件描述符与file对象的联系,以后进程对文件的读写都是通过操纵该文件描述符而进行。

b. do_filp_open()用于打开文件,返回一个file对象;而打开之前需要先找到该文件:

1)、open_namei()用于根据文件路径名查找文件,借助一个持有路径信息的数据结构nameidata而进行;

2)、查找结束后将填充有路径信息的nameidata返回给接下来的函数nameidata_to_filp()从而得到最终的file对象;当达到目的后,nameidata这个数据结构将会马上被释放。

c.open_namei()用于查找一个文件:

1)、path_lookup_open()实现文件的查找功能;要打开的文件若不存在,还需要有一个新建的过程,则调用 path_lookup_create(),后者和前者封装的是同一个实际的路径查找函数,只是参数不一样,使它们在处理细节上有所偏差;

2)、当是以新建文件的方式打开文件时,即设置了O_CREAT标识时需要创建一个新的索引节点,代表创建一个文件。在vfs_create()里的一句 核心语句dir->i_op->create(dir, dentry, mode, nd)可知它调用了具体的文件系统所提供的创建索引节点的方法。注意:这边的索引节点的概念,还只是位于内存之中,它和磁盘上的物理的索引节点的关系就像 位于内存中和位于磁盘中的文件一样。此时新建的索引节点还不能完全标志一个物理文件的成功创建,只有当把索引节点回写到磁盘上才是一个物理文件的真正创 建。想想我们以新建的方式打开一个文件,对其读写但最终没有保存而关闭,则位于内存中的索引节点会经历从新建到消失的过程,而磁盘却始终不知道有人曾经想 过创建一个文件,这是因为索引节点没有回写的缘故。

3)、path_to_nameidata()填充nameidata数据结构;

4)、may_open()检查是否可以打开该文件;一些文件如链接文件和只有写权限的目录是不能被打开的,先检查 nd->dentry->inode所指的文件是否是这一类文件,是的话则错误返回。还有一些文件是不能以TRUNC的方式打开的,若 nd->dentry->inode所指的文件属于这一类,则显式地关闭TRUNC标志位。接着如果有以TRUNC方式打开文件的,则更新 nd->dentry->inode的信息

3.1.1__path_lookup_intent_open()

不管是path_lookup_open()还是path_lookup_create()最终都是调用 __path_lookup_intent_open()来实现查找文件的功能。 查找时,在遍历路径的过程中,会逐层地将各个路径组成部分解析成目录项对象,如果此目录项对象在目录项缓存中,则直接从缓存中获得;如果该目录项在缓存中 不存在,则进行一次实际的读盘操作,从磁盘中读取该目录项所对应的索引节点。得到索引节点后,则建立索引节点与该目录项的联系。如此循环,直到最终找到目 标文件对应的目录项,也就找到了索引节点,而由索引节点找到对应的超级块对象就可知道该文件所在的文件系统的类型。 从磁盘中读取该目录项所对应的索引节点;这将引发VFS和实际的文件系统的一次交互。从前面的VFS理论介绍可知,读索引节点方法是由超级块来提供的。而 当安装一个实际的文件系统时,在内存中创建的超级块的信息是由一个实际文件系统的相关信息来填充的,这里的相关信息就包括了实际文件系统所定义的超级块的 操作函数列表,当然也就包括了读索引节点的具体执行方式。 当继续追踪一个实际文件系统ext3的ext3_read_inode()时,可发现这个函数很重要的一个工作就是为不同的文件类型设置不同的索引节点操 作函数表和文件操作函数表。

清单8. ext3_read_inode

    void ext3_read_inode(struct inode * inode)
    {
       ……
       //是普通文件         
       if (S_ISREG(inode->i_mode)) {
          inode->i_op = &ext3_file_inode_operations;
          inode->i_fop = &ext3_file_operations;
          ext3_set_aops(inode);
       } else if (S_ISDIR(inode->i_mode)) {
          //是目录文件
           inode->i_op = &ext3_dir_inode_operations;
           inode->i_fop = &ext3_dir_operations;
        } else if (S_ISLNK(inode->i_mode)) {
           // 是连接文件 
           ……
        } else { 
           // 如果以上三种情况都排除了,则是设备驱动
          //这里的设备还包括套结字、FIFO等伪设备 
          …… 
}

 

nameidata_to_filp子函数:__dentry_open

这是VFS与实际的文件系统联系的一个关键点。从上述小节分析中可知,调用实际文件系统读取索引节点的方法读取索引节点时,实际文件系统会根据文件 的不同类型赋予索引节点不同的文件操作函数集,如普通文件有普通文件对应的一套操作函数,设备文件有设备文件对应的一套操作函数。这样当把对应的索引节点 的文件操作函数集赋予文件对象,以后对该文件进行操作时,比如读操作,VFS虽然对各种不同文件都是执行同一个read()操作界面,但是真正读时,内核 却知道怎么区分对待不同的文件类型。

清单9. __dentry_open

    static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
     int flags, struct file *f,
     int (*open)(struct inode *, struct file *))
    {
        struct inode *inode;
        ……
        //整个函数的工作在于填充一个file对象
        ……
         f->f_mapping = inode->i_mapping;  
        f->f_dentry = dentry;
        f->f_vfsmnt = mnt;
        f->f_pos = 0;  
        //将对应的索引节点的文件操作函数集赋予文件对象的操作列表
        <span class="boldcode">f->f_op = fops_get(inode->i_fop); </span>
        ……  
        //若文件自己定义了open操作,则执行这个特定的open操作。
        if (!open && f->f_op)
           open = f->f_op->open; 
        if (open) {
           error = open(inode, f);
           if (error)
              goto cleanup_all;
        ……
        return f;
}

 

3.2 sys_read()

sys_read()系统调用用于从已打开的文件读取数据。如read成功,则返回读到的字节数。如已到达文件的尾端,则返回0。图9是sys_read()实现代码中的函数调用关系图。

图9. sys_read函数调用关系图
图片示例_sys_read函数调用关系图

对文件进行读操作时,需要先打开它。从3.1小结可知,打开一个文件时,会在内存组装一个文件对象,希望对该文件执行的操作方法已在文件对象设置好。所以 对文件进行读操作时,VFS在做了一些简单的转换后(由文件描述符得到其对应的文件对象;其核心思想是返回 current->files->fd[fd]所指向的文件对象),就可以通过语句 file->f_op->read(file, buf, count, pos)轻松调用实际文件系统的相应方法对文件进行读操作了。

4 解决问题

跨文件系统的文件操作的基本原理

到此,我们也就能够解释在Linux中为什么能够跨文件系统地操作文件了。举个例子,将vfat格式的磁盘上的一个文件a.txt拷贝到ext3格式的磁 盘上,命名为b.txt。这包含两个过程,对a.txt进行读操作,对b.txt进行写操作。读写操作前,需要先打开文件。由前面的分析可知,打开文件 时,VFS会知道该文件对应的文件系统格式,以后操作该文件时,VFS会调用其对应的实际文件系统的操作方法。所以,VFS调用vfat的读文件方法将 a.txt的数据读入内存;在将a.txt在内存中的数据映射到b.txt对应的内存空间后,VFS调用ext3的写文件方法将b.txt写入磁盘;从而 实现了最终的跨文件系统的复制操作。

“一切皆是文件”的实现根本

不论是普通的文件,还是特殊的目录、设备等,VFS都将它们同等看待成文件,通过同一套文件操作界面来对它们进行操作。操作文件时需先打开;打开文件 时,VFS会知道该文件对应的文件系统格式;当VFS把控制权传给实际的文件系统时,实际的文件系统再做出具体区分,对不同的文件类型执行不同的操作。这 也就是“一切皆是文件”的根本所在。

总结

VFS即虚拟文件系统是Linux文件系统中的一个抽象软件层;因为它的支持,众多不同的实际文件系统才能在Linux中共存,跨文件系统操作才能实现。 VFS借助它四个主要的数据结构即超级块、索引节点、目录项和文件对象以及一些辅助的数据结构,向Linux中不管是普通的文件还是目录、设备、套接字等 都提供同样的操作界面,如打开、读写、关闭等。只有当把控制权传给实际的文件系统时,实际的文件系统才会做出区分,对不同的文件类型执行不同的操作。由此 可见,正是有了VFS的存在,跨文件系统操作才能执行,Unix/Linux中的“一切皆是文件”的口号才能够得以实现。

 

文件系统

目录 描述
/ 主层次 的根,也是整个文件系统层次结构的根目录
/bin 存放在单用户模式可用的必要命令二进制文件,所有用户都可用,如 cat、ls、cp等等
/boot 存放引导加载程序文件,例如kernels、initrd等
/dev 存放必要的设备文件,例如/dev/null
/etc 存放主机特定的系统级配置文件。其实这里有个关于它名字本身意义上的的争议。在贝尔实验室的UNIX实施文档的早期版本中,/etc表示是“其他(etcetera)目录”,因为从历史上看,这个目录是存放各种不属于其他目录的文件(然而,文件系统目录标准 FSH 限定 /etc 用于存放静态配置文件,这里不该存有二进制文件)。早期文档出版后,这个目录名又重新定义成不同的形式。近期的解释中包含着诸如“可编辑文本配置”或者“额外的工具箱”这样的重定义
/etc/opt 存储着新增包的配置文件 /opt/.
/etc/sgml 存放配置文件,比如 catalogs,用于那些处理SGML(译者注:标准通用标记语言)的软件的配置文件
/etc/X11 X Window 系统11版本的的配置文件
/etc/xml 配置文件,比如catalogs,用于那些处理XML(译者注:可扩展标记语言)的软件的配置文件
/home 用户的主目录,包括保存的文件,个人配置,等等
/lib /bin/ 和 /sbin/中的二进制文件的必需的库文件<
/lib<架构位数> 备用格式的必要的库文件。 这样的目录是可选的,但如果他们存在的话肯定是有需要用到它们的程序
/media 可移动的多媒体(如CD-ROMs)的挂载点。(出现于 FHS-2.3)
/mnt 临时挂载的文件系统
/opt 可选的应用程序软件包
/proc 以文件形式提供进程以及内核信息的虚拟文件系统,在Linux中,对应进程文件系统(procfs )的挂载点
/root 根用户的主目录
/sbin 必要的系统级二进制文件,比如, init, ip, mount
/srv 系统提供的站点特定数据
/tmp 临时文件 (另见 /var/tmp). 通常在系统重启后删除
/usr 二级层级存储用户的只读数据; 包含(多)用户主要的公共文件以及应用程序
/usr/bin 非必要的命令二进制文件 (在单用户模式中不需要用到的);用于所有用户
/usr/include 标准的包含文件
/usr/lib 库文件,用于/usr/bin/ 和 /usr/sbin/中的二进制文件
/usr/lib<架构位数> 备用格式库(可选的)
/usr/local 三级层次 用于本地数据,具体到该主机上的。通常会有下一个子目录, 比如, bin/, lib/, share/.
/usr/local/sbin 非必要系统的二进制文件,比如用于不同网络服务的守护进程
/usr/share 架构无关的 (共享) 数据.
/usr/src 源代码,比如内核源文件以及与它相关的头文件
/usr/X11R6 X Window系统,版本号:11,发行版本:6
/var 各式各样的(Variable)文件,一些随着系统常规操作而持续改变的文件就放在这里,比如日志文件,脱机文件,还有临时的电子邮件文件
/var/cache 应用程序缓存数据. 这些数据是由耗时的I/O(输入/输出)的或者是运算本地生成的结果。这些应用程序是可以重新生成或者恢复数据的。当没有数据丢失的时候,可以删除缓存文件
/var/lib 状态信息。这些信息随着程序的运行而不停地改变,比如,数据库,软件包系统的元数据等等
/var/lock 锁文件。这些文件用于跟踪正在使用的资源
/var/log 日志文件。包含各种日志。
/var/mail 内含用户邮箱的相关文件
/var/opt 来自附加包的各种数据都会存储在 /var/opt/.
/var/run 存放当前系统上次启动以来的相关信息,例如当前登入的用户以及当前运行的daemons(守护进程).
/var/spool 该spool主要用于存放将要被处理的任务,比如打印队列以及邮件外发队列
/var/mail 过时的位置,用于放置用户邮箱文件
/var/tmp 存放重启后保留的临时文件

文件类型

大多数文件仅仅是普通文件,他们被称为regular文件;他们包含普通数据,比如,文本、可执行文件、或者程序、程序的输入或输出等等

  • 虽然你可以认为“在Linux中,一切你看到的皆为文件”这个观点相当保险,但这里仍有着一些例外。
  • 目录:由其他文件组成的文件。
  • 特殊文件:用于输入和输出的途径。大多数特殊文件都储存在/dev中,我们将会在后面讨论这个问题。
  • 链接文件:让文件或者目录出现在系统文件树结构上多个地方的机制。我们将详细地讨论这个链接文件。
  • (域)套接字:特殊的文件类型,和TCP/IP协议中的套接字有点像,提供进程间网络通讯,并受文件系统的访问控制机制保护。
  • 命名管道 : 或多或少有点像sockets(套接字),提供一个进程间的通信机制,而不用网络套接字协议。

 

参考于:
http://blog.csdn.net/heikefangxian23/article/details/51579971
https://linux.cn/article-6132-1.html
http://blog.csdn.net/eleven_xiy/article/details/71249365