diff options
Diffstat (limited to 'Documentation/translations/zh_CN/filesystems')
5 files changed, 827 insertions, 0 deletions
diff --git a/Documentation/translations/zh_CN/filesystems/debugfs.rst b/Documentation/translations/zh_CN/filesystems/debugfs.rst new file mode 100644 index 0000000000..4981a82dd6 --- /dev/null +++ b/Documentation/translations/zh_CN/filesystems/debugfs.rst @@ -0,0 +1,221 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/filesystems/debugfs.rst + +======= +Debugfs +======= + +译者 +:: + + 中文版维护者: 罗楚成 Chucheng Luo <luochucheng@vivo.com> + 中文版翻译者: 罗楚成 Chucheng Luo <luochucheng@vivo.com> + 中文版校译者: 罗楚成 Chucheng Luo <luochucheng@vivo.com> + + + +版权所有2020 罗楚成 <luochucheng@vivo.com> + + +Debugfs是内核开发人员在用户空间获取信息的简单方法。与/proc不同,proc只提供进程 +信息。也不像sysfs,具有严格的“每个文件一个值“的规则。debugfs根本没有规则,开发 +人员可以在这里放置他们想要的任何信息。debugfs文件系统也不能用作稳定的ABI接口。 +从理论上讲,debugfs导出文件的时候没有任何约束。但是[1]实际情况并不总是那么 +简单。即使是debugfs接口,也最好根据需要进行设计,并尽量保持接口不变。 + + +Debugfs通常使用以下命令安装:: + + mount -t debugfs none /sys/kernel/debug + +(或等效的/etc/fstab行)。 +debugfs根目录默认仅可由root用户访问。要更改对文件树的访问,请使用“ uid”,“ gid” +和“ mode”挂载选项。请注意,debugfs API仅按照GPL协议导出到模块。 + +使用debugfs的代码应包含<linux/debugfs.h>。然后,首先是创建至少一个目录来保存 +一组debugfs文件:: + + struct dentry *debugfs_create_dir(const char *name, struct dentry *parent); + +如果成功,此调用将在指定的父目录下创建一个名为name的目录。如果parent参数为空, +则会在debugfs根目录中创建。创建目录成功时,返回值是一个指向dentry结构体的指针。 +该dentry结构体的指针可用于在目录中创建文件(以及最后将其清理干净)。ERR_PTR +(-ERROR)返回值表明出错。如果返回ERR_PTR(-ENODEV),则表明内核是在没有debugfs +支持的情况下构建的,并且下述函数都不会起作用。 + +在debugfs目录中创建文件的最通用方法是:: + + struct dentry *debugfs_create_file(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops); + +在这里,name是要创建的文件的名称,mode描述了访问文件应具有的权限,parent指向 +应该保存文件的目录,data将存储在产生的inode结构体的i_private字段中,而fops是 +一组文件操作函数,这些函数中实现文件操作的具体行为。至少,read()和/或 +write()操作应提供;其他可以根据需要包括在内。同样的,返回值将是指向创建文件 +的dentry指针,错误时返回ERR_PTR(-ERROR),系统不支持debugfs时返回值为ERR_PTR +(-ENODEV)。创建一个初始大小的文件,可以使用以下函数代替:: + + struct dentry *debugfs_create_file_size(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops, + loff_t file_size); + +file_size是初始文件大小。其他参数跟函数debugfs_create_file的相同。 + +在许多情况下,没必要自己去创建一组文件操作;对于一些简单的情况,debugfs代码提供 +了许多帮助函数。包含单个整数值的文件可以使用以下任何一项创建:: + + void debugfs_create_u8(const char *name, umode_t mode, + struct dentry *parent, u8 *value); + void debugfs_create_u16(const char *name, umode_t mode, + struct dentry *parent, u16 *value); + struct dentry *debugfs_create_u32(const char *name, umode_t mode, + struct dentry *parent, u32 *value); + void debugfs_create_u64(const char *name, umode_t mode, + struct dentry *parent, u64 *value); + +这些文件支持读取和写入给定值。如果某个文件不支持写入,只需根据需要设置mode +参数位。这些文件中的值以十进制表示;如果需要使用十六进制,可以使用以下函数 +替代:: + + void debugfs_create_x8(const char *name, umode_t mode, + struct dentry *parent, u8 *value); + void debugfs_create_x16(const char *name, umode_t mode, + struct dentry *parent, u16 *value); + void debugfs_create_x32(const char *name, umode_t mode, + struct dentry *parent, u32 *value); + void debugfs_create_x64(const char *name, umode_t mode, + struct dentry *parent, u64 *value); + +这些功能只有在开发人员知道导出值的大小的时候才有用。某些数据类型在不同的架构上 +有不同的宽度,这样会使情况变得有些复杂。在这种特殊情况下可以使用以下函数:: + + void debugfs_create_size_t(const char *name, umode_t mode, + struct dentry *parent, size_t *value); + +不出所料,此函数将创建一个debugfs文件来表示类型为size_t的变量。 + +同样地,也有导出无符号长整型变量的函数,分别以十进制和十六进制表示如下:: + + struct dentry *debugfs_create_ulong(const char *name, umode_t mode, + struct dentry *parent, + unsigned long *value); + void debugfs_create_xul(const char *name, umode_t mode, + struct dentry *parent, unsigned long *value); + +布尔值可以通过以下方式放置在debugfs中:: + + struct dentry *debugfs_create_bool(const char *name, umode_t mode, + struct dentry *parent, bool *value); + + +读取结果文件将产生Y(对于非零值)或N,后跟换行符写入的时候,它只接受大写或小写 +值或1或0。任何其他输入将被忽略。 + +同样,atomic_t类型的值也可以放置在debugfs中:: + + void debugfs_create_atomic_t(const char *name, umode_t mode, + struct dentry *parent, atomic_t *value) + +读取此文件将获得atomic_t值,写入此文件将设置atomic_t值。 + +另一个选择是通过以下结构体和函数导出一个任意二进制数据块:: + + struct debugfs_blob_wrapper { + void *data; + unsigned long size; + }; + + struct dentry *debugfs_create_blob(const char *name, umode_t mode, + struct dentry *parent, + struct debugfs_blob_wrapper *blob); + +读取此文件将返回由指针指向debugfs_blob_wrapper结构体的数据。一些驱动使用“blobs” +作为一种返回几行(静态)格式化文本的简单方法。这个函数可用于导出二进制信息,但 +似乎在主线中没有任何代码这样做。请注意,使用debugfs_create_blob()命令创建的 +所有文件是只读的。 + +如果您要转储一个寄存器块(在开发过程中经常会这么做,但是这样的调试代码很少上传 +到主线中。Debugfs提供两个函数:一个用于创建仅寄存器文件,另一个把一个寄存器块 +插入一个顺序文件中:: + + struct debugfs_reg32 { + char *name; + unsigned long offset; + }; + + struct debugfs_regset32 { + struct debugfs_reg32 *regs; + int nregs; + void __iomem *base; + }; + + struct dentry *debugfs_create_regset32(const char *name, umode_t mode, + struct dentry *parent, + struct debugfs_regset32 *regset); + + void debugfs_print_regs32(struct seq_file *s, struct debugfs_reg32 *regs, + int nregs, void __iomem *base, char *prefix); + +“base”参数可能为0,但您可能需要使用__stringify构建reg32数组,实际上有许多寄存器 +名称(宏)是寄存器块在基址上的字节偏移量。 + +如果要在debugfs中转储u32数组,可以使用以下函数创建文件:: + + void debugfs_create_u32_array(const char *name, umode_t mode, + struct dentry *parent, + u32 *array, u32 elements); + +“array”参数提供数据,而“elements”参数为数组中元素的数量。注意:数组创建后,数组 +大小无法更改。 + +有一个函数来创建与设备相关的seq_file:: + + struct dentry *debugfs_create_devm_seqfile(struct device *dev, + const char *name, + struct dentry *parent, + int (*read_fn)(struct seq_file *s, + void *data)); + +“dev”参数是与此debugfs文件相关的设备,并且“read_fn”是一个函数指针,这个函数在 +打印seq_file内容的时候被回调。 + +还有一些其他的面向目录的函数:: + + struct dentry *debugfs_rename(struct dentry *old_dir, + struct dentry *old_dentry, + struct dentry *new_dir, + const char *new_name); + + struct dentry *debugfs_create_symlink(const char *name, + struct dentry *parent, + const char *target); + +调用debugfs_rename()将为现有的debugfs文件重命名,可能同时切换目录。 new_name +函数调用之前不能存在;返回值为old_dentry,其中包含更新的信息。可以使用 +debugfs_create_symlink()创建符号链接。 + +所有debugfs用户必须考虑的一件事是: + +debugfs不会自动清除在其中创建的任何目录。如果一个模块在不显式删除debugfs目录的 +情况下卸载模块,结果将会遗留很多野指针,从而导致系统不稳定。因此,所有debugfs +用户-至少是那些可以作为模块构建的用户-必须做模块卸载的时候准备删除在此创建的 +所有文件和目录。一份文件可以通过以下方式删除:: + + void debugfs_remove(struct dentry *dentry); + +dentry值可以为NULL或错误值,在这种情况下,不会有任何文件被删除。 + +很久以前,内核开发者使用debugfs时需要记录他们创建的每个dentry指针,以便最后所有 +文件都可以被清理掉。但是,现在debugfs用户能调用以下函数递归清除之前创建的文件:: + + void debugfs_remove_recursive(struct dentry *dentry); + +如果将对应顶层目录的dentry传递给以上函数,则该目录下的整个层次结构将会被删除。 + +注释: +[1] http://lwn.net/Articles/309298/ diff --git a/Documentation/translations/zh_CN/filesystems/index.rst b/Documentation/translations/zh_CN/filesystems/index.rst new file mode 100644 index 0000000000..9f2a8b0037 --- /dev/null +++ b/Documentation/translations/zh_CN/filesystems/index.rst @@ -0,0 +1,29 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/filesystems/index.rst <filesystems_index>` +:Translator: Wang Wenhu <wenhu.wang@vivo.com> + +.. _cn_filesystems_index: + +======================== +Linux Kernel中的文件系统 +======================== + +这份正在开发的手册或许在未来某个辉煌的日子里以易懂的形式将Linux虚拟\ +文件系统(VFS)层以及基于其上的各种文件系统如何工作呈现给大家。当前\ +可以看到下面的内容。 + +文件系统 +======== + +文件系统实现文档。 + +.. toctree:: + :maxdepth: 2 + + virtiofs + debugfs + tmpfs + diff --git a/Documentation/translations/zh_CN/filesystems/sysfs.txt b/Documentation/translations/zh_CN/filesystems/sysfs.txt new file mode 100644 index 0000000000..547062759e --- /dev/null +++ b/Documentation/translations/zh_CN/filesystems/sysfs.txt @@ -0,0 +1,373 @@ +Chinese translated version of Documentation/filesystems/sysfs.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Maintainer: Patrick Mochel <mochel@osdl.org> + Mike Murphy <mamurph@cs.clemson.edu> +Chinese maintainer: Fu Wei <tekkamanninja@gmail.com> +--------------------------------------------------------------------- +Documentation/filesystems/sysfs.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 +英文版维护者: Patrick Mochel <mochel@osdl.org> + Mike Murphy <mamurph@cs.clemson.edu> +中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> + + +以下为正文 +--------------------------------------------------------------------- +sysfs - 用于导出内核对象(kobject)的文件系统 + +Patrick Mochel <mochel@osdl.org> +Mike Murphy <mamurph@cs.clemson.edu> + +修订: 16 August 2011 +原始版本: 10 January 2003 + + +sysfs 简介: +~~~~~~~~~~ + +sysfs 是一个最初基于 ramfs 且位于内存的文件系统。它提供导出内核 +数据结构及其属性,以及它们之间的关联到用户空间的方法。 + +sysfs 始终与 kobject 的底层结构紧密相关。请阅读 +Documentation/core-api/kobject.rst 文档以获得更多关于 kobject 接口的 +信息。 + + +使用 sysfs +~~~~~~~~~~~ + +只要内核配置中定义了 CONFIG_SYSFS ,sysfs 总是被编译进内核。你可 +通过以下命令挂载它: + + mount -t sysfs sysfs /sys + + +创建目录 +~~~~~~~~ + +任何 kobject 在系统中注册,就会有一个目录在 sysfs 中被创建。这个 +目录是作为该 kobject 的父对象所在目录的子目录创建的,以准确地传递 +内核的对象层次到用户空间。sysfs 中的顶层目录代表着内核对象层次的 +共同祖先;例如:某些对象属于某个子系统。 + +Sysfs 在与其目录关联的 kernfs_node 对象中内部保存一个指向实现 +目录的 kobject 的指针。以前,这个 kobject 指针被 sysfs 直接用于 +kobject 文件打开和关闭的引用计数。而现在的 sysfs 实现中,kobject +引用计数只能通过 sysfs_schedule_callback() 函数直接修改。 + + +属性 +~~~~ + +kobject 的属性可在文件系统中以普通文件的形式导出。Sysfs 为属性定义 +了面向文件 I/O 操作的方法,以提供对内核属性的读写。 + + +属性应为 ASCII 码文本文件。以一个文件只存储一个属性值为宜。但一个 +文件只包含一个属性值可能影响效率,所以一个包含相同数据类型的属性值 +数组也被广泛地接受。 + +混合类型、表达多行数据以及一些怪异的数据格式会遭到强烈反对。这样做是 +很丢脸的,而且其代码会在未通知作者的情况下被重写。 + + +一个简单的属性结构定义如下: + +struct attribute { + char * name; + struct module *owner; + umode_t mode; +}; + + +int sysfs_create_file(struct kobject * kobj, const struct attribute * attr); +void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr); + + +一个单独的属性结构并不包含读写其属性值的方法。子系统最好为增删特定 +对象类型的属性定义自己的属性结构体和封装函数。 + +例如:驱动程序模型定义的 device_attribute 结构体如下: + +struct device_attribute { + struct attribute attr; + ssize_t (*show)(struct device *dev, struct device_attribute *attr, + char *buf); + ssize_t (*store)(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); +}; + +int device_create_file(struct device *, const struct device_attribute *); +void device_remove_file(struct device *, const struct device_attribute *); + +为了定义设备属性,同时定义了一下辅助宏: + +#define DEVICE_ATTR(_name, _mode, _show, _store) \ +struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store) + +例如:声明 + +static DEVICE_ATTR(foo, S_IWUSR | S_IRUGO, show_foo, store_foo); + +等同于如下代码: + +static struct device_attribute dev_attr_foo = { + .attr = { + .name = "foo", + .mode = S_IWUSR | S_IRUGO, + .show = show_foo, + .store = store_foo, + }, +}; + + +子系统特有的回调函数 +~~~~~~~~~~~~~~~~~~~ + +当一个子系统定义一个新的属性类型时,必须实现一系列的 sysfs 操作, +以帮助读写调用实现属性所有者的显示和储存方法。 + +struct sysfs_ops { + ssize_t (*show)(struct kobject *, struct attribute *, char *); + ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t); +}; + +[子系统应已经定义了一个 struct kobj_type 结构体作为这个类型的 +描述符,并在此保存 sysfs_ops 的指针。更多的信息参见 kobject 的 +文档] + +sysfs 会为这个类型调用适当的方法。当一个文件被读写时,这个方法会 +将一般的kobject 和 attribute 结构体指针转换为适当的指针类型后 +调用相关联的函数。 + + +示例: + +#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr) + +static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct device_attribute *dev_attr = to_dev_attr(attr); + struct device *dev = kobj_to_dev(kobj); + ssize_t ret = -EIO; + + if (dev_attr->show) + ret = dev_attr->show(dev, dev_attr, buf); + if (ret >= (ssize_t)PAGE_SIZE) { + printk("dev_attr_show: %pS returned bad count\n", + dev_attr->show); + } + return ret; +} + + + +读写属性数据 +~~~~~~~~~~~~ + +在声明属性时,必须指定 show() 或 store() 方法,以实现属性的 +读或写。这些方法的类型应该和以下的设备属性定义一样简单。 + +ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf); +ssize_t (*store)(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); + +也就是说,他们应只以一个处理对象、一个属性和一个缓冲指针作为参数。 + +sysfs 会分配一个大小为 (PAGE_SIZE) 的缓冲区并传递给这个方法。 +Sysfs 将会为每次读写操作调用一次这个方法。这使得这些方法在执行时 +会出现以下的行为: + +- 在读方面(read(2)),show() 方法应该填充整个缓冲区。回想属性 + 应只导出了一个属性值或是一个同类型属性值的数组,所以这个代价将 + 不会不太高。 + + 这使得用户空间可以局部地读和任意的向前搜索整个文件。如果用户空间 + 向后搜索到零或使用‘0’偏移执行一个pread(2)操作,show()方法将 + 再次被调用,以重新填充缓存。 + +- 在写方面(write(2)),sysfs 希望在第一次写操作时得到整个缓冲区。 + 之后 Sysfs 传递整个缓冲区给 store() 方法。 + + 当要写 sysfs 文件时,用户空间进程应首先读取整个文件,修该想要 + 改变的值,然后回写整个缓冲区。 + + 在读写属性值时,属性方法的执行应操作相同的缓冲区。 + +注记: + +- 写操作导致的 show() 方法重载,会忽略当前文件位置。 + +- 缓冲区应总是 PAGE_SIZE 大小。对于i386,这个值为4096。 + +- show() 方法应该返回写入缓冲区的字节数,也就是 scnprintf()的 + 返回值。 + +- show() 方法在将格式化返回值返回用户空间的时候,禁止使用snprintf()。 + 如果可以保证不会发生缓冲区溢出,可以使用sprintf(),否则必须使用 + scnprintf()。 + +- store() 应返回缓冲区的已用字节数。如果整个缓存都已填满,只需返回 + count 参数。 + +- show() 或 store() 可以返回错误值。当得到一个非法值,必须返回一个 + 错误值。 + +- 一个传递给方法的对象将会通过 sysfs 调用对象内嵌的引用计数固定在 + 内存中。尽管如此,对象代表的物理实体(如设备)可能已不存在。如有必要, + 应该实现一个检测机制。 + +一个简单的(未经实验证实的)设备属性实现如下: + +static ssize_t show_name(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", dev->name); +} + +static ssize_t store_name(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + snprintf(dev->name, sizeof(dev->name), "%.*s", + (int)min(count, sizeof(dev->name) - 1), buf); + return count; +} + +static DEVICE_ATTR(name, S_IRUGO, show_name, store_name); + + +(注意:真正的实现不允许用户空间设置设备名。) + +顶层目录布局 +~~~~~~~~~~~~ + +sysfs 目录的安排显示了内核数据结构之间的关系。 + +顶层 sysfs 目录如下: + +block/ +bus/ +class/ +dev/ +devices/ +firmware/ +net/ +fs/ + +devices/ 包含了一个设备树的文件系统表示。他直接映射了内部的内核 +设备树,反映了设备的层次结构。 + +bus/ 包含了内核中各种总线类型的平面目录布局。每个总线目录包含两个 +子目录: + + devices/ + drivers/ + +devices/ 包含了系统中出现的每个设备的符号链接,他们指向 root/ 下的 +设备目录。 + +drivers/ 包含了每个已为特定总线上的设备而挂载的驱动程序的目录(这里 +假定驱动没有跨越多个总线类型)。 + +fs/ 包含了一个为文件系统设立的目录。现在每个想要导出属性的文件系统必须 +在 fs/ 下创建自己的层次结构(参见Documentation/filesystems/fuse.rst)。 + +dev/ 包含两个子目录: char/ 和 block/。在这两个子目录中,有以 +<major>:<minor> 格式命名的符号链接。这些符号链接指向 sysfs 目录 +中相应的设备。/sys/dev 提供一个通过一个 stat(2) 操作结果,查找 +设备 sysfs 接口快捷的方法。 + +更多有关 driver-model 的特性信息可以在 Documentation/driver-api/driver-model/ +中找到。 + + +TODO: 完成这一节。 + + +当前接口 +~~~~~~~~ + +以下的接口层普遍存在于当前的sysfs中: + +- 设备 (include/linux/device.h) +---------------------------------- +结构体: + +struct device_attribute { + struct attribute attr; + ssize_t (*show)(struct device *dev, struct device_attribute *attr, + char *buf); + ssize_t (*store)(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); +}; + +声明: + +DEVICE_ATTR(_name, _mode, _show, _store); + +增/删属性: + +int device_create_file(struct device *dev, const struct device_attribute * attr); +void device_remove_file(struct device *dev, const struct device_attribute * attr); + + +- 总线驱动程序 (include/linux/device.h) +-------------------------------------- +结构体: + +struct bus_attribute { + struct attribute attr; + ssize_t (*show)(const struct bus_type *, char * buf); + ssize_t (*store)(const struct bus_type *, const char * buf, size_t count); +}; + +声明: + +BUS_ATTR(_name, _mode, _show, _store) + +增/删属性: + +int bus_create_file(struct bus_type *, struct bus_attribute *); +void bus_remove_file(struct bus_type *, struct bus_attribute *); + + +- 设备驱动程序 (include/linux/device.h) +----------------------------------------- + +结构体: + +struct driver_attribute { + struct attribute attr; + ssize_t (*show)(struct device_driver *, char * buf); + ssize_t (*store)(struct device_driver *, const char * buf, + size_t count); +}; + +声明: + +DRIVER_ATTR(_name, _mode, _show, _store) + +增/删属性: + +int driver_create_file(struct device_driver *, const struct driver_attribute *); +void driver_remove_file(struct device_driver *, const struct driver_attribute *); + + +文档 +~~~~ + +sysfs 目录结构以及其中包含的属性定义了一个内核与用户空间之间的 ABI。 +对于任何 ABI,其自身的稳定和适当的文档是非常重要的。所有新的 sysfs +属性必须在 Documentation/ABI 中有文档。详见 Documentation/ABI/README。 diff --git a/Documentation/translations/zh_CN/filesystems/tmpfs.rst b/Documentation/translations/zh_CN/filesystems/tmpfs.rst new file mode 100644 index 0000000000..6fd9d83b2d --- /dev/null +++ b/Documentation/translations/zh_CN/filesystems/tmpfs.rst @@ -0,0 +1,146 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/filesystems/tmpfs.rst + +translated by Wang Qing<wangqing@vivo.com> + +===== +Tmpfs +===== + +Tmpfs是一个将所有文件都保存在虚拟内存中的文件系统。 + +tmpfs中的所有内容都是临时的,也就是说没有任何文件会在硬盘上创建。 +如果卸载tmpfs实例,所有保存在其中的文件都会丢失。 + +tmpfs将所有文件保存在内核缓存中,随着文件内容增长或缩小可以将不需要的 +页面swap出去。它具有最大限制,可以通过“mount -o remount ...”调整。 + +和ramfs(创建tmpfs的模板)相比,tmpfs包含交换和限制检查。和tmpfs相似的另 +一个东西是RAM磁盘(/dev/ram*),可以在物理RAM中模拟固定大小的硬盘,并在 +此之上创建一个普通的文件系统。Ramdisks无法swap,因此无法调整它们的大小。 + +由于tmpfs完全保存于页面缓存和swap中,因此所有tmpfs页面将在/proc/meminfo +中显示为“Shmem”,而在free(1)中显示为“Shared”。请注意,这些计数还包括 +共享内存(shmem,请参阅ipcs(1))。获得计数的最可靠方法是使用df(1)和du(1)。 + +tmpfs具有以下用途: + +1) 内核总有一个无法看到的内部挂载,用于共享匿名映射和SYSV共享内存。 + + 挂载不依赖于CONFIG_TMPFS。如果CONFIG_TMPFS未设置,tmpfs对用户不可见。 + 但是内部机制始终存在。 + +2) glibc 2.2及更高版本期望将tmpfs挂载在/dev/shm上以用于POSIX共享内存 + (shm_open,shm_unlink)。添加内容到/etc/fstab应注意如下: + + tmpfs /dev/shm tmpfs defaults 0 0 + + 使用时需要记住创建挂载tmpfs的目录。 + + SYSV共享内存无需挂载,内部已默认支持。(在2.3内核版本中,必须挂载 + tmpfs的前身(shm fs)才能使用SYSV共享内存) + +3) 很多人(包括我)都觉的在/tmp和/var/tmp上挂载非常方便,并具有较大的 + swap分区。目前循环挂载tmpfs可以正常工作,所以大多数发布都应当可以 + 使用mkinitrd通过/tmp访问/tmp。 + +4) 也许还有更多我不知道的地方:-) + + +tmpfs有三个用于调整大小的挂载选项: + +========= =========================================================== +size tmpfs实例分配的字节数限制。默认值是不swap时物理RAM的一半。 + 如果tmpfs实例过大,机器将死锁,因为OOM处理将无法释放该内存。 +nr_blocks 与size相同,但以PAGE_SIZE为单位。 +nr_inodes tmpfs实例的最大inode个数。默认值是物理内存页数的一半,或者 + (有高端内存的机器)低端内存RAM的页数,二者以较低者为准。 +========= =========================================================== + +这些参数接受后缀k,m或g表示千,兆和千兆字节,可以在remount时更改。 +size参数也接受后缀%用来限制tmpfs实例占用物理RAM的百分比: +未指定size或nr_blocks时,默认值为size=50% + +如果nr_blocks=0(或size=0),block个数将不受限制;如果nr_inodes=0, +inode个数将不受限制。这样挂载通常是不明智的,因为它允许任何具有写权限的 +用户通过访问tmpfs耗尽机器上的所有内存;但同时这样做也会增强在多个CPU的 +场景下的访问。 + +tmpfs具有为所有文件设置NUMA内存分配策略挂载选项(如果启用了CONFIG_NUMA), +可以通过“mount -o remount ...”调整 + +======================== ========================= +mpol=default 采用进程分配策略 + (请参阅 set_mempolicy(2)) +mpol=prefer:Node 倾向从给定的节点分配 +mpol=bind:NodeList 只允许从指定的链表分配 +mpol=interleave 倾向于依次从每个节点分配 +mpol=interleave:NodeList 依次从每个节点分配 +mpol=local 优先本地节点分配内存 +======================== ========================= + +NodeList格式是以逗号分隔的十进制数字表示大小和范围,最大和最小范围是用- +分隔符的十进制数来表示。例如,mpol=bind0-3,5,7,9-15 + +带有有效NodeList的内存策略将按指定格式保存,在创建文件时使用。当任务在该 +文件系统上创建文件时,会使用到挂载时的内存策略NodeList选项,如果设置的话, +由调用任务的cpuset[请参见Documentation/admin-guide/cgroup-v1/cpusets.rst] +以及下面列出的可选标志约束。如果NodeLists为设置为空集,则文件的内存策略将 +恢复为“默认”策略。 + +NUMA内存分配策略有可选标志,可以用于模式结合。在挂载tmpfs时指定这些可选 +标志可以在NodeList之前生效。 +Documentation/admin-guide/mm/numa_memory_policy.rst列出所有可用的内存 +分配策略模式标志及其对内存策略。 + +:: + + =static 相当于 MPOL_F_STATIC_NODES + =relative 相当于 MPOL_F_RELATIVE_NODES + +例如,mpol=bind=staticNodeList相当于MPOL_BIND|MPOL_F_STATIC_NODES的分配策略 + +请注意,如果内核不支持NUMA,那么使用mpol选项挂载tmpfs将会失败;nodelist指定不 +在线的节点也会失败。如果您的系统依赖于此,但内核会运行不带NUMA功能(也许是安全 +revocery内核),或者具有较少的节点在线,建议从自动模式中省略mpol选项挂载选项。 +可以在以后通过“mount -o remount,mpol=Policy:NodeList MountPoint”添加到挂载点。 + +要指定初始根目录,可以使用如下挂载选项: + +==== ==================== +模式 权限用八进制数字表示 +uid 用户ID +gid 组ID +==== ==================== + +这些选项对remount没有任何影响。您可以通过chmod(1),chown(1)和chgrp(1)的更改 +已经挂载的参数。 + +tmpfs具有选择32位还是64位inode的挂载选项: + +======= ============= +inode64 使用64位inode +inode32 使用32位inode +======= ============= + +在32位内核上,默认是inode32,挂载时指定inode64会被拒绝。 +在64位内核上,默认配置是CONFIG_TMPFS_INODE64。inode64避免了单个设备上可能有多个 +具有相同inode编号的文件;比如32位应用程序使用glibc如果长期访问tmpfs,一旦达到33 +位inode编号,就有EOVERFLOW失败的危险,无法打开大于2GiB的文件,并返回EINVAL。 + +所以'mount -t tmpfs -o size=10G,nr_inodes=10k,mode=700 tmpfs /mytmpfs'将在 +/mytmpfs上挂载tmpfs实例,分配只能由root用户访问的10GB RAM/SWAP,可以有10240个 +inode的实例。 + + +:作者: + Christoph Rohland <cr@sap.com>, 1.12.01 +:更新: + Hugh Dickins, 4 June 2007 +:更新: + KOSAKI Motohiro, 16 Mar 2010 +:更新: + Chris Down, 13 July 2020 diff --git a/Documentation/translations/zh_CN/filesystems/virtiofs.rst b/Documentation/translations/zh_CN/filesystems/virtiofs.rst new file mode 100644 index 0000000000..09bc9e012e --- /dev/null +++ b/Documentation/translations/zh_CN/filesystems/virtiofs.rst @@ -0,0 +1,58 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/filesystems/virtiofs.rst <virtiofs_index>` + +译者 +:: + + 中文版维护者: 王文虎 Wang Wenhu <wenhu.wang@vivo.com> + 中文版翻译者: 王文虎 Wang Wenhu <wenhu.wang@vivo.com> + 中文版校译者: 王文虎 Wang Wenhu <wenhu.wang@vivo.com> + +=========================================== +virtiofs: virtio-fs 主机<->客机共享文件系统 +=========================================== + +- Copyright (C) 2020 Vivo Communication Technology Co. Ltd. + +介绍 +==== +Linux的virtiofs文件系统实现了一个半虚拟化VIRTIO类型“virtio-fs”设备的驱动,通过该\ +类型设备实现客机<->主机文件系统共享。它允许客机挂载一个已经导出到主机的目录。 + +客机通常需要访问主机或者远程系统上的文件。使用场景包括:在新客机安装时让文件对其\ +可见;从主机上的根文件系统启动;对无状态或临时客机提供持久存储和在客机之间共享目录。 + +尽管在某些任务可能通过使用已有的网络文件系统完成,但是却需要非常难以自动化的配置\ +步骤,且将存储网络暴露给客机。而virtio-fs设备通过提供不经过网络的文件系统访问文件\ +的设计方式解决了这些问题。 + +另外,virto-fs设备发挥了主客机共存的优点提高了性能,并且提供了网络文件系统所不具备 +的一些语义功能。 + +用法 +==== +以``myfs``标签将文件系统挂载到``/mnt``: + +.. code-block:: sh + + guest# mount -t virtiofs myfs /mnt + +请查阅 https://virtio-fs.gitlab.io/ 了解配置QEMU和virtiofsd守护程序的详细信息。 + +内幕 +==== +由于virtio-fs设备将FUSE协议用于文件系统请求,因此Linux的virtiofs文件系统与FUSE文\ +件系统客户端紧密集成在一起。客机充当FUSE客户端而主机充当FUSE服务器,内核与用户空\ +间之间的/dev/fuse接口由virtio-fs设备接口代替。 + +FUSE请求被置于虚拟队列中由主机处理。主机填充缓冲区中的响应部分,而客机处理请求的完成部分。 + +将/dev/fuse映射到虚拟队列需要解决/dev/fuse和虚拟队列之间语义上的差异。每次读取\ +/dev/fuse设备时,FUSE客户端都可以选择要传输的请求,从而可以使某些请求优先于其他\ +请求。虚拟队列有其队列语义,无法更改已入队请求的顺序。在虚拟队列已满的情况下尤 +其关键,因为此时不可能加入高优先级的请求。为了解决此差异,virtio-fs设备采用“hiprio”\ +(高优先级)虚拟队列,专门用于有别于普通请求的高优先级请求。 + |