Linux 文件系统模块的注册

{name = 0xda714268 “cramfs”, fs_flags = 1,
  get_sb = 0xda713150 <cramfs_get_sb>,
  kill_sb = 0xc0205bb0 <kill_block_super>, owner = 0xda714320,
next = 0x0,
  fs_supers = {next = 0x0, prev = 0x0}, s_lock_key = {<No data
fields>},
  s_umount_key = {<No data fields>}, i_lock_key = {<No data
fields>},
  i_mutex_key = {<No data fields>}, i_mutex_dir_key = {<No
data fields>},
  i_alloc_sem_key = {<No data fields>}}

安装根文件系统式系统初始化的关键部分。Linux内核允许根文件系统放在很多不同的地方,比如硬盘分区、软盘、通过NFS共享的远程文件系统以及保存在ramdisk中。内核要在变量ROOT_DEV中寻找包含根文件系统的磁盘主设备号。当编译内核时,或者像最初的启动装入程序传递一个合适的“root”选项时,根文件系统可以被指定为/dev目录下的一个设备文件。

rootfs是一种特定的ramfs的实例,它一直存在于系统中,不能卸载。大部分其他的文件系统安装于rootfs之上。

    超级块在每个每个文件系统的根目录上,是针对特定的文件系统的数据结构,用于描述和维护每个文件系统的状态。不同的文件系统可以通过超级块形成对外的统一抽象。

附上一个图:

[cpp]

在介绍根文件系统挂载之前先介绍一些基础知识

虚拟文件系统的安装

    以上只是完成了文件系统的注册,为了真正的能够使用文件进行读、写,需要将注册的文件进行安装。除了根文件系统是在内核导引阶段被安装之外,其他文件或者由初始化脚本安装或者由用户使用mount命令安装在已经安装的文件系统的某一个目录上。也就是说根系统的安装是在系统的初始化阶段进行安装的,此时对应的目录的为’/’,该目录为其他文件系统的安装提供了落脚点。以后所有的文件系统都需要安装在’/’或者其子目录下,从而逐渐在内存中建立起VFS目录树。建立VFS目录树的本质就是初始化、并建立一系列的vfsmount、dentry、inode和super_block,并将他们通过指针链接起来。

    每个文件系统都有自己的根目录,安装文件系统的那个目录称为安装点。

    在众多的文件系统中,我们重点说明一下VFS中重要的一个文件系统—rootfs,也称为根系统。该文件系统是VFS存在的基础,它为其他文件系统提供了一个作为初始安装点的空目录。以后安装的文件系统都是挂载在该文件系统提供的空目录下的。rootfs的注册过程是内核初始化的一个不可缺少的阶段。其对应的file_system_type为

        static struct file_system_type rootfs_fs_type =  {

                .name = “rootfs”,

                .mount          =    rootfs_mount,

                .kill_sb          =      kill_litter_super,

        };

    然后,系统通过调用init_mount_tree安装该目录。

    关于根文件系统的安装和普通文件系统的安装,还是要参考文章末尾的比较好的一篇博客。这里只是说一下几个结构体建立的顺序。

    1.首先建立安装点的结构体vfsmount,并初始化其部分成员变量;

    2.通过file_system_type中的get_sb指向的例程建立相应的超级块,并将超级块挂在到file_system_type中的fs_super域,形成该种文件类型的一个链表;

    3.建立inode结构体,并初始化i_op和i_fop两个域,用于指向针对该文件的操作函数,i_sb域指向超级块(如果是同一个文件系统,则不需要重新建立超级块,此时指向根目录下的超级块即可);

    4.建立目录项的结构体dentry,并将d_inode域指向刚建立的inode;并将d_sb指向刚建立的超级块(如果是同一个文件系统,则不需要重新建立超级块,此时指向根目录下的超级块即可);

    前面说过每种文件类型有对应的struct 
file_system_type,无论存在该种文件类型的多少个实例化实例,都会有且仅有一个file_system_type结构体。但是安装文件系统则不同,管它有零个或者是多个实例安装到系统中,每当一个文件系统被安装就会实例化一个vfsmount结构体实例。其代表该文件系统的一个安装实例,也代表了该文件系统的一个安装点。vfsmout对应的结构结构体如下。

        struct vfsmount  {

                        struct list_head mnt_hash;

                        struct vfsmount *mnt_header;

                        //挂载点目录;

                        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;

                        //所有挂载系统通过mnt_list形成一个链表;

                        struct list_head mnt_list;

        };

    所有的vfs挂载点通过mnt_list双链表挂在于namespace->list链表中,如图3所示。该mnt命名空间可以通过任意进程获取,之所以能通过任何进程获取该namespace,是因为在系统建立VFS目录树的时候,在init_mount_tree函数中的最后两步会对系统的第一个进程init_task建立其进程数据块中的namespace域。所以所有以后由该进程派生出来的进程都先天的继承该域,因此该域可以通过任意的进程获取。current始终指向当前的进程,所以可以获取。在init_mount_tree函数中建立namespace域的过程如下:

        namespace  =  kmalloc(sizeof(*namespace),  GFP_KERNEL);

        list_add(&mnt->list,  &namespace->list); 
//mnt是由do_kern_mount返回的值;

        namespace->root  =  mnt;

        init_task.namespace =  namespace;

        for_each_task(p)  {

                get_namespace(namespace);

                p->namespace =  namespace;

        }

        set_fs_pwd(current->fs,  namespace->root, 
namespace->root->mnt_root);

        set_fs_root(current->fs,  namespace->root, 
namespace->root->mnt_root);

    最后两行就是将do_kern_mount()函数建立的mnt和dentry信息记录在当前进程的fs的结构中。

   
子vfsmount挂载点通过结构挂载于父vfsmount的mnt_child链表中,并且mnt_parent直接执行父亲fs的vfsmount结构,从而形成层次结构;

    vfs的super_block指向实际挂载的文件系统对应的超级块;

图片 1

图 3 挂载文件系统文件列表

    关于linux下虚拟文件系统的注册和安装更详细的讲解,可以参考下面这篇博文。

    https://www.ibm.com/developerworks/cn/linux/l-vfs/

   
关于虚拟文件系统中各个主要的数据结构之间如何通过指针相互关联起来,建立起VFS系统的大致的结构框架,可以参考下面这篇博文。

    

   
在这篇文章中,坐着将VFS系统中各个主要的数据结构之间的关系用图的描述出来,对于了解各个主要的数据结构的各个域具有非常好的帮助作用。

{name = 0xc06c0033 “rootfs”, fs_flags = 0,
  get_sb = 0xc02b7240 <rootfs_get_sb>,
  kill_sb = 0xc0205c50 <kill_litter_super>, owner = 0x0, next =
0xc074fc00,
  fs_supers = {next = 0xd78046a0, prev = 0xd78046a0},
  s_lock_key = {<No data fields>}, s_umount_key = {<No data
fields>},
  i_lock_key = {<No data fields>}, i_mutex_key = {<No data
fields>},
  i_mutex_dir_key = {<No data fields>}, i_alloc_sem_key =
{<No data fields>}}

  1. static struct backing_dev_info ramfs_backing_dev_info = {  
  2.     .name       = “ramfs”,  
  3.     .ra_pages   = 0,    /* No readahead */  
  4.     .capabilities   = BDI_CAP_NO_ACCT_AND_WRITEBACK |  
  5.               BDI_CAP_MAP_DIRECT | BDI_CAP_MAP_COPY |  
  6.               BDI_CAP_READ_MAP | BDI_CAP_WRITE_MAP | BDI_CAP_EXEC_MAP,  
  7. };  

01struct vfsmount *

    很明显,VFS屏蔽了底层物理文件系统的实现细节,只对外提供一组操作系统中文件的统一入口。因此,即使各个底层物理文件系统之间的差别再大,对于用户而言也是透明的。每种底层的物理文件系统只需对VFS系统提供各自的接口,供VFS系统调用即可。

图片 2

  1. int get_sb_nodev(struct file_system_type *fs_type,  
  2.     int flags, void *data,  
  3.     int (*fill_super)(struct super_block *, void *, int),  
  4.     struct vfsmount *mnt)  
  5. {  
  6.     int error;  
  7.     /*获得sb结构*/  
  8.     struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL);  
  9.   
  10.     if (IS_ERR(s))  
  11.         return PTR_ERR(s);  
  12.   
  13.     s->s_flags = flags;  
  14.     /*这里实际调用ramfs_fill_super,对sb结构的属性进行设置*/  
  15.     error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);  
  16.     if (error) {  
  17.         deactivate_locked_super(s);  
  18.         return error;  
  19.     }  
  20.     s->s_flags |= MS_ACTIVE;  
  21.     simple_set_mnt(mnt, s);/*设置mnt和sb关联*/  
  22.     return 0;  
  23. }  

30}

虚拟文件系统层

VFS作为文件系统的接口的根层,记录当前支持的文件系统和挂载的文件系统。

   
初始化file_system_type结构体中的fs_supers双向链表,后来会看到它会与该种文件系统类型的super_block结构体的s_instances关联。

相关阅读:

第3行得到文件系统的类型,这里是rootfs,当然也会有其它的文件系统,比如proc,pipefs等

    linux对于最近经常使用的inode文件和dentry目录项,操作系统内核各为其维护一个缓存,用于快速实现最近经常使用的文件和目录项的查找。

register_filesystem (fs=0xda7142e0) at fs/filesystems.c:74

  1. /*获得根目录的sb*/  
  2. static int rootfs_get_sb(struct file_system_type *fs_type,  
  3.     int flags, const char *dev_name, void *data, struct vfsmount *mnt)  
  4. {  
  5.     return get_sb_nodev(fs_type, flags|MS_NOUSER, data, ramfs_fill_super,  
  6.                 mnt);  
  7. }  

32   if (error)

    目录项(dentry)用于实现文件名称和inode之间的映射,dentry同时维护目录和文件之间的关系,从而支持在文件系统中移动。

图片 3

start_kernel()->vfs_caches_init()->mnt_init()->init_rootfs()

08    mnt = vfs_kern_mount(type, flags, name, data);

图片 4

根据next = 0xc0751460   p *(struct file_system_type *)0xc0751460

安装根文件系统分为两个阶段:

13

图 1 VFS系统结构图

文件系统模块的注册应该就这么简单,注册完成后,就可以mount并使用该文件系统了。

图片 5

第11行挂载点哈希表分配空间

    文件(inode)用于表示文件系统中的每个文件或者目录,管理文件系统中的每个对象。inode其实包含了管理文件系统中每个对象所需要的所有元数据(包含可以在该对象上执行的操作),但是inode不包含该文件的文件名。inode是通过dentry结构体的d_inode指针指向的。

先看下file_systems的内容:

  1. static struct file_system_type rootfs_fs_type = {  
  2.     .name       = “rootfs”,  
  3.     .get_sb     = rootfs_get_sb,  
  4.     .kill_sb    = kill_litter_super,  
  5. };  

我的开发板上的u-boot传送的参数为noinitrd root=/dev/mtdblock3
init=/linuxrc console=ttySAC0,115200 mem=64M。

    如前所述,linux站在高度抽象的角度看待本系统中的文件系统和针对文件系统的相关操作。linux使用超级块、文件、目录项和安装点四个概念管理系统的所有文件及目录。

用cramfs.ko做的实验,断点放在init_cramfs_fs(void)函数的rv =
register_filesystem(&cramfs_fs_type);行,执行register_filesystem函数:

[cpp]

第6行命明空间信号量的初始化

   
与VFS相关的所有数据结构只存在于物理内存中。所以系统每次初始化的时候都需要首先在内存中建立起一颗VFS的目录树(在linux中成为namespace),实际上便是在物理内存中建立相应的数据结构。
VFS目录树在linux的文件系统模块中是个很重要的概念,不能与实际文件系统相混淆。

先提一下,实际注册文件系统的时候是将file_system_type放在以file_systems为表头的一个链表中。

根文件系统定义如下

14}

    buffer
cache在这里的作用就是缓冲区高速缓存,用于减小对实际物理存储设备之间的请求次数,加快应用程序的响应速度。内核使用LRU(最近最少使用算法)管理缓冲区中缓存。当缓冲区中的脏数据达到一定的比例、距离上次一周期刷新的时间间隔超过一定的数值时,内核自动唤醒flusher后台进程刷新内存中的脏数据回实际的后备存储空间。具体的介绍可以参见《linux内核设计与实现》页高速缓存和页回写一章。

rootfs的注册还在sysfs之后。

http://www.linuxidc.com/Linux/2012-02/53771.htm

17

    linux中的虚拟文件系统(Virtual File System,
VFS)是一种采用面向对象编程策略(尽管书写操作系统的C语言本身不支持面向对象编程,但是思想还是可以借鉴的),是对该操作系统所支持的所有实际物理文件系统的一种抽象,用于提供给用户进程或者系统调用操作当前系统中所有文件统一的接口,而不用关心当前操作系统中存在哪几种实际的物理文件系统。也可以这么说VFS实际提供了面向对象编程中的一种接口(或者抽象类),而所有的实际物理文件系统需要实现接口所定义的接口(或者使用抽象类中的缺省实现)。

1、INIT_LIST_HEAD(&fs->fs_supers);

下面看看他的两个函数

第14行为页目录缓存的初始化

虚拟文件系统的注册

    VFS的文件系统可以静态或者动态的添加到系统中。如果是静态的方式添加,那么需要在编译内核的时候确定内核所支持的文件系统类型,并在系统初始化的时候通过内嵌的函数调用在VFS中进行注册。如果是动态的添加,则把相应的文件系统当做一个模块,利用模块的加载和卸载特征向注册表登记类型或者从注册表中注销掉该系统类型,此时需要调用int
register_filesystem(struct file_system_type
*fs)。这里需要引用一个file_system_type结构体,该结构体包含了一个文件系统的名称以及一个指向相应的VFS超级块读取例程的地址,具体结构体如下所示。所有的已经注册的file_system_type将会形成一个注册链表,如图2所示。无论是可加载的模块还是静态注册模块,都是用register_filesystem进行注册。

        struct file_system_type {

                        //文件系统的名字;

                        const char *name; 

                        //文件系统的标志,bitmap;         

                        int fs_flags;

                        //在安装文件时,调用get_sb()从磁盘中读取超级块;

                        int (*get_sb)(struct file_system_rype *, 
int,  const char* ,  void *,  struct vfsmount *);

                        //卸载文件系统时,调用该函数完成一些清理工作;

                        void (*kill_sb)  (struct super_block *);

                        //模块的拥有者,如果是静态编译进内核的,那么该字段为NULL;

                        struct module *owner;

                        //形成文件系统类型链表;

                        struct file_system_type_type *next;

                        //同一种文件类型的超级块形成一个链表,fs_supers是这个链表的头;也就是说该域指向所有使用该文件类型的超级块;

                        struct list_head  fs_supers;

                        //相关的锁;

                        struct lock_class_key s_lock_key;

                        struct lock_class_key s_umount_key;

        };

图片 6

图 2 file_system_type形成的链表

   
在图2中的file_systems指针是定义在fs/filesystems.c文件中的全局变量,指向第一个file_system_type。其定义如下:static
struct file_system_type
*file_systems。如果要对文件系统形成的链表进行写操作(加载新的文件类型或者卸载文件类型,那么应该加锁)。如下所示。

        //寻找特定的文件系统;

        static struct file_system_type **find_filesystem(const char
*name, unsigned len)

        {

                        struct file_system_type **p;

                        for (p=&file_systems; *p; p=&(*p)->next)

                               if (strlen((*p)->name) == len && 
strncmp((*p)->name, name, len) == 0)

                                       break;

                        return p;

        }

        //加载新的文件系统到系统内核;

        int register_filesystem(struct file_system_type * fs)

        {

                       int res = 0;

                       struct file_system_type ** p;

                       BUG_ON(strchr(fs->name, ‘.’));

                       if (fs->next)

                               return -EBUSY;

                       //加锁;

                      write_lock(&file_systems_lock);

                      p = find_filesystem(fs->name,
strlen(fs->name));

                      if (*p)

                             res = -EBUSY;

                      else

                            *p = fs;

                     //释放锁

                     write_unlock(&file_systems_lock);

                     return res;

        }

        //从内核中卸载文件系统

        int unregister_filesystem(struct file_system_type * fs)

        {

                     struct file_system_type ** tmp;

                     //加锁;

                     write_lock(&file_systems_lock);

                     tmp = &file_systems;

                     while (*tmp) {

                                  if (fs == *tmp) {

                                              *tmp = fs->next;

                                               fs->next = NULL;

                                              
write_unlock(&file_systems_lock);

                                              synchronize_rcu();

                                              return 0;

                                  }

                   tmp = &(*tmp)->next;

                   }

                   //释放锁;

                   write_unlock(&file_systems_lock);

                   return -EINVAL;

        }

register_filesystem函数具体做了什么?主要是两点:

  1. /** 
  2.  *  sget    –   find or create a superblock 
  3.  *  @type:  filesystem type superblock should belong to 
  4.  *  @test:  comparison callback 
  5.  *  @set:   setup callback 
  6.  *  @data:  argument to each of them 
  7.  */  
  8.  /*查找或创建一个sb结构*/  
  9. struct super_block *sget(struct file_system_type *type,  
  10.             int (*test)(struct super_block *,void *),  
  11.             int (*set)(struct super_block *,void *),  
  12.             void *data)  
  13. {  
  14.     struct super_block *s = NULL;  
  15.     struct super_block *old;  
  16.     int err;  
  17.   
  18. retry:  
  19.     spin_lock(&sb_lock);  
  20.     if (test) {  
  21.         list_for_each_entry(old, &type->fs_supers, s_instances) {  
  22.             if (!test(old, data))  
  23.                 continue;  
  24.             if (!grab_super(old))  
  25.                 goto retry;  
  26.             if (s) {  
  27.                 up_write(&s->s_umount);  
  28.                 destroy_super(s);  
  29.             }  
  30.             return old;  
  31.         }  
  32.     }  
  33.     if (!s) {/*如果找不到sb,从内存中申请一个*/  
  34.         spin_unlock(&sb_lock);  
  35.         s = alloc_super(type);  
  36.         if (!s)  
  37.             return ERR_PTR(-ENOMEM);  
  38.         goto retry;  
  39.     }  
  40.           
  41.     err = set(s, data);  
  42.     if (err) {  
  43.         spin_unlock(&sb_lock);  
  44.         up_write(&s->s_umount);  
  45.         destroy_super(s);  
  46.         return ERR_PTR(err);  
  47.     }  
  48.     /*初始化得到的sb结构*/  
  49.     s->s_type = type;  
  50.     strlcpy(s->s_id, type->name, sizeof(s->s_id));  
  51.     /*加入链表尾*/  
  52.     list_add_tail(&s->s_list, &super_blocks);  
  53.     list_add(&s->s_instances, &type->fs_supers);  
  54.     spin_unlock(&sb_lock);  
  55.     get_filesystem(type);  
  56.     return s;  
  57. }  

41    dput(mnt->mnt_root);

    在用户应用程序中或者通过C标准库提供的操作文件的接口或者直接使用系统调用操作文件系统时,使用的是VFS提供的一种抽象接口。而系统调用就像是一种切换器,是进程从用户空间进入内核空间的入口,之后由内核代表用户进程运行在该进程的系统空间的堆栈中(在创建进程的时候,内核会在内核空间为每个进程创建两个内存页(每页4K或者8K)或者一个系统页、一个中断页)。

(gdb) p *file_systems
$8 = {name = 0xc06cf3f4 “sysfs”, fs_flags = 0,
  get_sb = 0xc0259190 <sysfs_get_sb>, kill_sb = 0xc0205c00
<kill_anon_super>,
  owner = 0x0, next = 0xc0751460, fs_supers = {next = 0xd78044a0,
    prev = 0xd78044a0}, s_lock_key = {<No data fields>},
  s_umount_key = {<No data fields>}, i_lock_key = {<No data
fields>},
  i_mutex_key = {<No data fields>}, i_mutex_dir_key = {<No
data fields>},
  i_alloc_sem_key = {<No data fields>}}

[cpp]

25    fs_kobj = kobject_create_and_add(“fs”, NULL);

    linux中文件系统的组织和系统调用之间的结构图如下:

   
在find_filesystem函数中就是根据name和名字长度遍历链表进行查找的,从而将新的文件系统加到这个链表中。

[cpp]

35    mnt->mnt_mountpoint = mnt->mnt_root;

2、find_filesystem(fs->name, strlen(fs->name));

  1. /** 
  2.  *  register_filesystem – register a new filesystem 
  3.  *  @fs: the file system structure 
  4.  * 
  5.  *  Adds the file system passed to the list of file systems the kernel 
  6.  *  is aware of for mount and other syscalls. Returns 0 on success, 
  7.  *  or a negative errno code on an error. 
  8.  * 
  9.  *  The &struct file_system_type that is passed is linked into the kernel  
  10.  *  structures and must not be freed until the file system has been 
  11.  *  unregistered. 
  12.  */  
  13.  /*注册一个新的文件系统*/  
  14. int register_filesystem(struct file_system_type * fs)  
  15. {  
  16.     int res = 0;  
  17.     struct file_system_type ** p;  
  18.   
  19.     BUG_ON(strchr(fs->name, ‘.’));  
  20.     if (fs->next)  
  21.         return -EBUSY;  
  22.     INIT_LIST_HEAD(&fs->fs_supers);  
  23.     write_lock(&file_systems_lock);  
  24.     /*从system_type链表中查找指定名称的file_system_type*/  
  25.     p = find_filesystem(fs->name, strlen(fs->name));  
  26.     if (*p)  
  27.         res = -EBUSY;  
  28.     else  
  29.         *p = fs;  
  30.     write_unlock(&file_systems_lock);  
  31.     return res;  
  32. }  

第19行字符设备初始化

显示cramfs的file_system_type结构提如下:

  1. /*初始化根文件系统*/  
  2. int __init init_rootfs(void)  
  3. {  
  4.     int err;  
  5.     /*初始化ramfs_backing_dev_info*/  
  6.     err = bdi_init(&ramfs_backing_dev_info);  
  7.     if (err)  
  8.         return err;  
  9.     /*注册rootfs_fs_type文件类型*/  
  10.     err = register_filesystem(&rootfs_fs_type);  
  11.     if (err)/*如果出错,销毁上面初始化的*/  
  12.         bdi_destroy(&ramfs_backing_dev_info);  
  13.   
  14.     return err;  
  15. }  

26    error = type->get_sb(type, flags, name, data, mnt);

 

[cpp]

initramfs

1,内核安装特殊rootfs文件系统,该文件系统仅提供一个作为初始安装点的空目录

09    mempages -= reserve;

[cpp]

37    up_write(&mnt->mnt_sb->s_umount);

[cpp]

07

07

第3行定义一个挂载点

03    int err;

01static void __init init_mount_tree(void)

09           panic(“Can’t create rootfs”);

03    struct file_system_type *type = get_fs_type(fstype);

47out:

08    reserve = min((mempages – nr_free_pages()) * 3/2, mempages –
1);

第4行定义挂载点

26    if (!fs_kobj)

当内核启动的时候,会先注册和挂载一个虚拟的根文件系统,也就是rootfs,然后会把做好的initramfs(这个可以自己制作)中的文件解压到rootfs中。然后系统会挂载真的根文件系统,rootfs隐藏之后。

15    init_waitqueue_head(&ns->poll);

07    mnt = do_kern_mount(“rootfs”, 0, “rootfs”, NULL);

第8行返回挂载点

05    struct path root;

第18行命名空间的根结点指向挂载点

06

10    if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&

第12行分配一个新的已安装文件系统的描述符,存放在局部变量mnt中

09                  0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);

02{

11    error = -ENOMEM;

5};

第5行初始化

27    if (error < 0)

23                  goto out_free_secdata;

13    atomic_set(&ns->count, 1);

15}

然后由于后来的设备类型越来越来多,比如可能在scsi,sata,flash这些设备,还有的存在于网络设备上,不可能把这些设备的驱动编译进内核,这样内核就会越来越来大。为了解决这些问题,出现了基于ram的文件系统,initramfs,这个文件系统可以包含多个目录和程序init,然后通过这个程序,内核再用这个程序去挂载真正的要文件系统。如果没有这个程序,内核可以来寻找和挂载一个根分区,接着执行一些/sbin/init的变种。

03    unsigned u;

06           150% of current kernel size */

39    return mnt;

01struct vfsmount * do_kern_mount(const char *fstype, int flags,
const char *name, void *data)

发表评论

电子邮件地址不会被公开。 必填项已用*标注