docs: update CN translation for vfs and nvs_flash

This commit is contained in:
daiziyan
2021-11-15 18:58:44 +08:00
parent 6bf5d2b13e
commit c82881eaee
4 changed files with 116 additions and 82 deletions

View File

@@ -68,29 +68,24 @@ Case 2: API functions are declared with an extra context pointer (the FS driver
Synchronous input/output multiplexing
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Synchronous input/output multiplexing by :cpp:func:`select` is supported in the VFS component. The implementation
works in the following way.
Synchronous input/output multiplexing by :cpp:func:`select` is supported in the VFS component. The implementation works in the following way.
1. :cpp:func:`select` is called with file descriptors which could belong to various VFS drivers.
2. The file descriptors are divided into groups each belonging to one VFS driver.
3. The file descriptors belonging to non-socket VFS drivers are handed over to the given VFS drivers by :cpp:func:`start_select`
described later on this page. This function represents the driver-specific implementation of :cpp:func:`select` for
the given driver. This should be a non-blocking call which means the function should immediately return after setting up
the environment for checking events related to the given file descriptors.
4. The file descriptors belonging to the socket VFS driver are handed over to the socket driver by
:cpp:func:`socket_select` described later on this page. This is a blocking call which means that it will return only
if there is an event related to socket file descriptors or a non-socket driver signals :cpp:func:`socket_select`
to exit.
5. Results are collected from each VFS driver and all drivers are stopped by deinitiazation
of the environment for checking events.
3. The file descriptors belonging to non-socket VFS drivers are handed over to the given VFS drivers by :cpp:func:`start_select`, described later on this page. This function represents the driver-specific implementation of :cpp:func:`select` for the given driver. This should be a non-blocking call which means the function should immediately return after setting up the environment for checking events related to the given file descriptors.
4. The file descriptors belonging to the socket VFS driver are handed over to the socket driver by :cpp:func:`socket_select` described later on this page. This is a blocking call which means that it will return only if there is an event related to socket file descriptors or a non-socket driver signals :cpp:func:`socket_select` to exit.
5. Results are collected from each VFS driver and all drivers are stopped by de-initialization of the environment for checking events.
6. The :cpp:func:`select` call ends and returns the appropriate results.
Non-socket VFS drivers
""""""""""""""""""""""
If you want to use :cpp:func:`select` with a file descriptor belonging to a non-socket VFS driver
then you need to register the driver with functions :cpp:func:`start_select` and
:cpp:func:`end_select` similarly to the following example:
If you want to use :cpp:func:`select` with a file descriptor belonging to a non-socket VFS driver, then you need to register the driver with functions :cpp:func:`start_select` and :cpp:func:`end_select` similarly to the following example:
.. highlight:: c
@@ -101,32 +96,25 @@ then you need to register the driver with functions :cpp:func:`start_select` and
.end_select = &uart_end_select,
// ... other members initialized
:cpp:func:`start_select` is called for setting up the environment for
detection of read/write/error conditions on file descriptors belonging to the
given VFS driver.
:cpp:func:`start_select` is called for setting up the environment for detection of read/write/error conditions on file descriptors belonging to the given VFS driver.
:cpp:func:`end_select` is called to stop/deinitialize/free the
environment which was setup by :cpp:func:`start_select`.
:cpp:func:`end_select` is called to stop/deinitialize/free the environment which was setup by :cpp:func:`start_select`.
.. note::
:cpp:func:`end_select` might be called without a previous :cpp:func:`start_select` call in some rare
circumstances. :cpp:func:`end_select` should fail gracefully if this is the case.
:cpp:func:`end_select` might be called without a previous :cpp:func:`start_select` call in some rare circumstances. :cpp:func:`end_select` should fail gracefully if this is the case (i.e., should not crash but return an error instead).
Please refer to the
reference implementation for the UART peripheral in
:component_file:`vfs/vfs_uart.c` and most particularly to the functions
:cpp:func:`esp_vfs_dev_uart_register`, :cpp:func:`uart_start_select`, and
:cpp:func:`uart_end_select` for more information.
Please refer to the reference implementation for the UART peripheral in :component_file:`vfs/vfs_uart.c` and most particularly to the functions :cpp:func:`esp_vfs_dev_uart_register`, :cpp:func:`uart_start_select`, and :cpp:func:`uart_end_select` for more information.
Please check the following examples that demonstrate the use of :cpp:func:`select` with VFS file descriptors:
- :example:`peripherals/uart/uart_select`
- :example:`system/select`
Socket VFS drivers
""""""""""""""""""
A socket VFS driver is using its own internal implementation of :cpp:func:`select` and non-socket VFS drivers notify
it upon read/write/error conditions.
A socket VFS driver is using its own internal implementation of :cpp:func:`select` and non-socket VFS drivers notify it upon read/write/error conditions.
A socket VFS driver needs to be registered with the following functions defined:
@@ -141,27 +129,19 @@ A socket VFS driver needs to be registered with the following functions defined:
.stop_socket_select_isr = &lwip_stop_socket_select_isr,
// ... other members initialized
:cpp:func:`socket_select` is the internal implementation of :cpp:func:`select` for the socket driver. It works only
with file descriptors belonging to the socket VFS.
:cpp:func:`socket_select` is the internal implementation of :cpp:func:`select` for the socket driver. It works only with file descriptors belonging to the socket VFS.
:cpp:func:`get_socket_select_semaphore` returns the signalization object (semaphore) which will be used in non-socket
drivers to stop the waiting in :cpp:func:`socket_select`.
:cpp:func:`get_socket_select_semaphore` returns the signalization object (semaphore) which will be used in non-socket drivers to stop the waiting in :cpp:func:`socket_select`.
:cpp:func:`stop_socket_select` call is used to stop the waiting in :cpp:func:`socket_select` by passing the object
returned by :cpp:func:`get_socket_select_semaphore`.
:cpp:func:`stop_socket_select` call is used to stop the waiting in :cpp:func:`socket_select` by passing the object returned by :cpp:func:`get_socket_select_semaphore`.
:cpp:func:`stop_socket_select_isr` has the same functionality as :cpp:func:`stop_socket_select` but it can be used
from ISR.
:cpp:func:`stop_socket_select_isr` has the same functionality as :cpp:func:`stop_socket_select` but it can be used from ISR.
Please see :component_file:`lwip/port/esp32/vfs_lwip.c` for a reference socket driver implementation using LWIP.
.. note::
If you use :cpp:func:`select` for socket file descriptors only then you can enable the
:envvar:`CONFIG_LWIP_USE_ONLY_LWIP_SELECT` option to reduce the code size and improve performance.
.. note::
Don't change the socket driver during an active :cpp:func:`select` call or you might experience some undefined
behavior.
If :cpp:func:`select` is only used on socket file descriptors, you can enable the :envvar:`CONFIG_LWIP_USE_ONLY_LWIP_SELECT` option to reduce the code size and improve performance.
You should not change the socket driver during an active :cpp:func:`select` call or you might experience some undefined behavior.
Paths
-----
@@ -213,7 +193,6 @@ Applications which use the UART driver can instruct VFS to use the driver's inte
VFS also provides an optional newline conversion feature for input and output. Internally, most applications send and receive lines terminated by the LF (''\n'') character. Different terminal programs may require different line termination, such as CR or CRLF. Applications can configure this separately for input and output either via menuconfig, or by calls to the functions ``esp_vfs_dev_uart_port_set_rx_line_endings`` and ``esp_vfs_dev_uart_port_set_tx_line_endings``.
Standard streams and FreeRTOS tasks
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -239,11 +218,11 @@ Such a design has the following consequences:
Event fds
-------------------------------------------
``eventfd()`` call is a powerful tool to notify a ``select()`` based loop of custom events. The ``eventfd()`` implementation in ESP-IDF is generally the same as described in ``man(2) eventfd`` except for:
``eventfd()`` call is a powerful tool to notify a ``select()`` based loop of custom events. The ``eventfd()`` implementation in ESP-IDF is generally the same as described in `man(2) eventfd <https://man7.org/linux/man-pages/man2/eventfd.2.html>`_ except for:
- ``esp_vfs_eventfd_register()`` has to be called before calling ``eventfd()``
- Options ``EFD_CLOEXEC``, ``EFD_NONBLOCK`` and ``EFD_SEMAPHORE`` are not supported in flags.
- Option ``EFD_SUPPORT_ISR`` has been added in flags. This flag is required to read and the write the eventfd in an interrupt handler.
- Option ``EFD_SUPPORT_ISR`` has been added in flags. This flag is required to read and write the eventfd in an interrupt handler.
Note that creating an eventfd with ``EFD_SUPPORT_ISR`` will cause interrupts to be temporarily disabled when reading, writing the file and during the beginning and the ending of the ``select()`` when this file is set.

View File

@@ -11,7 +11,7 @@
底层存储
^^^^^^^^^^^^^^^^^^
NVS 库通过调用 :ref:`esp_partition <flash-partition-apis>` API 使用主 flash 的部分空间,包括 ``data`` 类型 ``nvs`` 子类型的所有分区。应用程序可调用 :cpp:func:`nvs_open` API 选择使用带有 ``nvs`` 标签的分区,也可以通过调用 :cpp:func:`nvs_open_from_partition` API 选择使用指定名称的任意分区。
NVS 库通过调用 :ref:`esp_partition <flash-partition-apis>` API 使用主 flash 的部分空间,即类型为 ``data`` 且子类型 ``nvs`` 的所有分区。应用程序可调用 :cpp:func:`nvs_open` API 选择使用带有 ``nvs`` 标签的分区,也可以通过调用 :cpp:func:`nvs_open_from_partition` API 选择使用指定名称的任意分区。
NVS 库后续版本可能会增加其他存储器后端,来将数据保存至其他 flash 芯片SPI 或 I2C 接口、RTC 或 FRAM 中。
@@ -77,9 +77,9 @@ NVS 与 {IDF_TARGET_NAME} flash 加密系统不直接兼容。但如果 NVS 加
NVS 加密
--------------
NVS 分区内存储的数据可使用 AES-XTS 进行加密,类似于 IEEE P1619 磁盘加密标准中提到的加密方式。为了实现加密,每个条目被均视为一个扇区,并将条目相对地址(相对于分区开头)传递给加密算法,用作扇区号。可通过 :ref:`CONFIG_NVS_ENCRYPTION` 启用 NVS 加密。NVS 加密所需的密钥存储于其他分区,并进行了 :doc:`Flash 加密 <../../security/flash-encryption>`。因此,在使用 NVS 加密前应先启用 :doc:`Flash 加密 <../../security/flash-encryption>`
NVS 分区内存储的数据可使用 AES-XTS 进行加密,类似于 IEEE P1619 磁盘加密标准中提到的加密方式。为了实现加密,每个条目被均视为一个扇区,并将条目相对地址(相对于分区开头)传递给加密算法,用作扇区号。可通过 :ref:`CONFIG_NVS_ENCRYPTION` 启用 NVS 加密。NVS 加密所需的密钥存储于其他分区,并且被 :doc:`Flash 加密 <../../security/flash-encryption>` 保护。因此,在使用 NVS 加密前应先启用 :doc:`Flash 加密 <../../security/flash-encryption>`
启用 :doc:`Flash 加密 <../../security/flash-encryption>` 时,默认启用 NVS 加密。这是因为 Wi-Fi 驱动在默认的 NVS 分区中存储了凭证(如 SSID 和口令)。启用平台级加密后,仍需将它们作为默认选项进行加密。
启用 :doc:`Flash 加密 <../../security/flash-encryption>` 时,默认启用 NVS 加密。这是因为 Wi-Fi 驱动在默认的 NVS 分区中存储了凭证(如 SSID 和密码)。启用平台级加密后,仍需将它们作为默认选项进行加密。
使用 NVS 加密,分区表必须包含 :ref:`nvs_key_partition`。在分区表选项 (menuconfig->Partition Table) 下,为 NVS 加密提供了两个包含 :ref:`nvs_key_partition` 的分区表,您可以通过工程配置菜单 (``idf.py menuconfig``) 进行选择。请参考 :example:`security/flash_encryption` 中的例子,了解如何配置和使用 NVS 加密功能。
@@ -117,7 +117,7 @@ NVS 密钥分区
i) 建立并烧录分区表
::
idf.py partition_table partition_table-flash
idf.py partition-table partition-table-flash
ii) 调用 :component_file:`parttool.py<partition_table/parttool.py>`,将密钥存储在 flash 上的 :ref:`nvs_key_partition` 中。详见 :doc:` 分区表 </api-guides/partition-tables>` 的分区工具部分。

View File

@@ -6,16 +6,17 @@
概述
--------
虚拟文件系统 (VFS) 组件可为一些驱动提供一个统一接口。有了该接口,用户可像操作普通文件一样操作虚拟文件。这类驱动程序可以是 FAT、SPIFFS 等真实文件系统,也可以是文件类接口的设备驱动程序。
虚拟文件系统 (VFS) 组件为驱动程序提供一个统一接口,可以操作类文件对象。这类驱动程序可以是 FAT、SPIFFS 等真实文件系统,也可以是提供文件类接口的设备驱动程序。
VFS 组件支持 C 库函数(如 fopen 和 fprintf 等)与文件系统 (FS) 驱动程序协同工作。在高层级,每个 FS 驱动程序均与某些路径前缀相关联。当一个 C 库函数需要打开文件时VFS 组件将搜索与该文件所在文件路径相关联的 FS 驱动程序,并将调用传递给该驱动程序。针对该文件的读取、写入等其他操作的调用也将传递给这个驱动程序。
例如,您可以使用 ``/fat`` 前缀注册 FAT 文件系统驱动,之后即可调用 ``fopen("/fat/file.txt", "w")``。之后VFS 将调用 FAT 驱动的 ``open`` 函数,并将参数 ``/file.txt`` 和合适的打开模式传递给 ``open`` 函数;后续对返回的 ``FILE*`` 数据流调用 C 库函数也同样会传递给 FAT 驱动。
注册 FS 驱动程序
---------------------
如需注册 FS 驱动程序,首先要定义一个 :cpp:type:`esp_vfs_t` 结构体实例,并用指向 FS API 的函数指针填充它。
如需注册 FS 驱动程序,应用程序首先要定义一个 :cpp:type:`esp_vfs_t` 结构体实例,并用指向 FS API 的函数指针填充它。
.. highlight:: c
@@ -67,7 +68,24 @@ VFS 组件支持 C 库函数(如 fopen 和 fprintf 等)与文件系统 (FS)
同步输入/输出多路复用
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
如需通过 :cpp:func:`select` 使用同步输入/输出多路复用,首先需要把 :cpp:func:`start_select`:cpp:func:`end_select` 注册到 VFS如下所示
VFS 组件支持通过 :cpp:func:`select` 进行同步输入/输出多路复用,其实现方式如下
1. 调用 :cpp:func:`select`,使用时提供的文件描述符可以属于不同的 VFS 驱动。
2. 文件描述符被分为几组,每组属于一个 VFS 驱动。
3. 非套接字 VFS 驱动的文件描述符由 :cpp:func:`start_select` 移交给指定的 VFS 驱动,后文会对此进行详述。该函数代表指定驱动 :cpp:func:`select` 的实现。这是一个非阻塞的调用,意味着在设置好检查与指定文件描述符相关事件的环境后,该函数应该立即返回。
4. 套接字 VFS 驱动的文件描述符由 :cpp:func:`socket_select` 移交给套接字 VFS 驱动,后文会对此进行详述。这是一个阻塞调用,意味着只有当有一个与套接字文件描述符相关的事件或非套接字驱动发出信号让 :cpp:func:`socket_select` 退出时,它才会返回。
5. 从各个 VFS 驱动程序收集结果,并通过对事件检查环境取消初始化来终止所有驱动程序。
6. :cpp:func:`select` 调用结束并返回适当的结果。
非套接字 VFS 驱动
""""""""""""""""""""""
如果要使用非套接字 VFS 驱动的文件描述符调用 :cpp:func:`select`,那么需要用函数 :cpp:func:`start_select`:cpp:func:`end_select` 注册该驱动,具体如下:
.. highlight:: c
@@ -78,14 +96,52 @@ VFS 组件支持 C 库函数(如 fopen 和 fprintf 等)与文件系统 (FS)
.end_select = &uart_end_select,
// ... other members initialized
调用 :cpp:func:`start_select` 设置环境,用以检测某一 VFS 文件描述符读取/写入/错误条件。调用 :cpp:func:`end_select` 终止、析构或释放 :cpp:func:`start_select` 设置的资源。请在 :component_file:`vfs/vfs_uart.c` 中查看 UART 外设参考实现、:cpp:func:`esp_vfs_dev_uart_register`:cpp:func:`uart_start_select`:cpp:func:`uart_end_select` 函数。
调用 :cpp:func:`start_select` 函数可以设置环境,检测指定 VFS 驱动的文件描述符读取/写入/错误条件。
调用 :cpp:func:`end_select` 函数可以终止/取消初始化/释放由 :cpp:func:`start_select` 设置的环境。
.. note::
在少数情况下,在调用 :cpp:func:`end_select` 之前可能并没有调用过 :cpp:func:`start_select`。因此 :cpp:func:`end_select` 的实现必须在该情况下返回错误而不能崩溃。
如需获取更多信息,请参考 :component_file:`vfs/vfs_uart.c` 中 UART 外设的 VFS 驱动,尤其是函数 :cpp:func:`esp_vfs_dev_uart_register`:cpp:func:`uart_start_select`:cpp:func:`uart_end_select`
请参考以下示例,查看如何使用 VFS 文件描述符调用 :cpp:func:`select`
- :example:`peripherals/uart/uart_select`
- :example:`system/select`
如果 :cpp:func:`select` 用于套接字文件描述符,您可以启用 :envvar:`CONFIG_LWIP_USE_ONLY_LWIP_SELECT` 选项来减少代码量,提高性能。
套接字 VFS 驱动
""""""""""""""""""""""
套接字 VFS 驱动会使用自实现的 :cpp:func:`socket_select` 函数,在读取/写入/错误条件时,非套接字 VFS 驱动会通知该函数。
可通过定义以下函数注册套接字 VFS 驱动:
.. highlight:: c
::
// In definition of esp_vfs_t:
.socket_select = &lwip_select,
.get_socket_select_semaphore = &lwip_get_socket_select_semaphore,
.stop_socket_select = &lwip_stop_socket_select,
.stop_socket_select_isr = &lwip_stop_socket_select_isr,
// ... other members initialized
函数 :cpp:func:`socket_select` 是套接字驱动对 :cpp:func:`select` 的内部实现。该函数只对套接字 VFS 驱动的文件描述符起作用。
:cpp:func:`get_socket_select_semaphore` 返回信号对象 (semaphore),用于非套接字驱动程序中,以终止 :cpp:func:`socket_select` 的等待。
:cpp:func:`stop_socket_select` 通过传递 :cpp:func:`get_socket_select_semaphore` 函数返回的对象来终止 :cpp:func:`socket_select` 函数的等待。
:cpp:func:`stop_socket_select_isr`:cpp:func:`stop_socket_select` 的作用相似,但是前者可在 ISR 中使用。
请参考 :component_file:`lwip/port/esp32/vfs_lwip.c` 以了解使用 LWIP 的套接字驱动参考实现。
.. note::
如果 :cpp:func:`select` 只用于套接字文件描述符,您可以启用 :envvar:`CONFIG_LWIP_USE_ONLY_LWIP_SELECT` 选项来减少代码量,提高性能。
不要在 :cpp:func:`select` 调用过程中更改套接字驱动,否则会出现一些未定义行为。
路径
-----
@@ -142,7 +198,7 @@ VFS 还为输入和输出提供换行符转换功能(可选)。多数应用
``stdin````stdout````stderr````FILE`` 对象在所有 FreeRTOS 任务之间共享,指向这些对象的指针分别存储在每个任务的 ``struct _reent`` 中。
预处理器把如下代码:
预处理器把如下代码解释为 ``fprintf(__getreent()->_stderr, "42\n");``
.. highlight:: c
@@ -150,29 +206,26 @@ VFS 还为输入和输出提供换行符转换功能(可选)。多数应用
fprintf(stderr, "42\n");
解释为:
.. highlight:: c
::
fprintf(__getreent()->_stderr, "42\n");
其中 ``__getreent()`` 函数将为每个任务返回一个指向 ``struct _reent`` 的指针。每个任务的 TCB 均拥有一个 ``struct _reent`` 结构体,任务初始化后,``struct _reent`` 结构体中的 ``_stdin````_stdout````_stderr`` 将会被赋予 ``_GLOBAL_REENT````_stdin````_stdout````_stderr`` 的值,``_GLOBAL_REENT`` 即为 FreeRTOS 启动之前所用结构体。
其中 ``__getreent()`` 函数将为每个任务返回一个指向 newlib libc 中 ``struct _reent`` 的指针。每个任务的 TCB 均拥有一个 ``struct _reent`` 结构体,任务初始化后,``struct _reent`` 结构体中的 ``_stdin````_stdout````_stderr`` 将会被赋予 ``_GLOBAL_REENT````_stdin````_stdout````_stderr`` 的值,``_GLOBAL_REENT`` 即为 FreeRTOS 启动之前所用结构体。
这样设计带来的结果是:
- 允许重定向给定任务的 ``stdin````stdout````stderr``,而不影响其他任务,例如通过 ``stdin = fopen("/dev/uart/1", "r")``
- 允许设置给定任务的 ``stdin````stdout````stderr``,而不影响其他任务,例如通过 ``stdin = fopen("/dev/uart/1", "r")``
- 但使用 ``fclose`` 关闭默认 ``stdin````stdout````stderr`` 将同时关闭相应的 ``FILE`` 流对象,因此会影响其他任务;
- 如需更改新任务的默认 ``stdin````stdout````stderr`` 流,请在创建新任务之前修改 ``_GLOBAL_REENT->_stdin`` (``_stdout````_stderr``)。
Event fds
-------------------------------------------
应用示例
-------------------
``eventfd()`` 是一个很强大的工具,可以循环通知基于 ``select()`` 的自定义事件。在 ESP-IDF 中, ``eventfd()`` 的实现大体上与 `man(2) eventfd <https://man7.org/linux/man-pages/man2/eventfd.2.html>`_ 中的描述相同,主要区别如下:
`指南`_ (未完成)
- 在调用 ``eventfd()`` 之前必须先调用 ``esp_vfs_eventfd_register()``
- 标志中没有 ``EFD_CLOEXEC````EFD_NONBLOCK````EFD_SEMAPHORE`` 选项;
- ``EFD_SUPPORT_ISR`` 选项已经被添加到标志中。在中断处理程序中读取和写入 eventfd 需要这个标志。
注意,用 ``EFD_SUPPORT_ISR`` 创建 eventfd 将导致在读取、写入文件时,以及在设置这个文件的 ``select()`` 开始和结束时,暂时禁用中断。
.. _指南: ../../template.html
API 参考
-------------
@@ -180,3 +233,5 @@ API 参考
.. include-build-file:: inc/esp_vfs.inc
.. include-build-file:: inc/esp_vfs_dev.inc
.. include-build-file:: inc/esp_vfs_eventfd.inc