pthread.rst 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. POSIX Thread
  2. ============
  3. :link_to_translation:`en:[English]`
  4. 概述
  5. --------
  6. ESP-IDF 基于 FreeRTOS,但提供了一系列与 POSIX 兼容的 API,以便轻松移植第三方代码,例如支持 pthread 常用的 ``pthread`` API。
  7. 在 ESP-IDF 中,pthread 对 FreeRTOS 中的等效功能进行了包装。pthread API 所需的运行内存或性能开销很低,但 pthread 或 FreeRTOS 的可用功能并非都可以通过 ESP-IDF 的 pthread 支持来实现。
  8. 添加标准 ``pthread.h`` 头文件后可以在 ESP-IDF 中使用 pthread,该头文件已包含在工具链 libc 中。还有另一个专用于 ESP-IDF 的头文件 ``esp_pthread.h``,其中提供了一些额外的非 POSIX API,以便通过 pthread 使用一些 ESP-IDF 功能。
  9. C++ 标准库中的 ``std::thread``、``std::mutex``、``std::condition_variable`` 等功能也是通过 pthread(利用 GCC libstdc++)实现的。因此,本文档提到的限制条件也同样适用于 C++ 标准库中等效功能。
  10. RTOS 集成
  11. ----------------
  12. 与许多使用 pthread 的操作系统不同,ESP-IDF 是一个实时操作系统,具有实时调度程序。这意味着只有当一个更高优先级的任务准备就绪、线程在 OS 同步结构(如 mutex)上发生阻塞、或者线程调用 ``sleep``、:cpp:func:`vTaskDelay`、``usleep`` 等函数时,线程才会停止运行。
  13. .. note::
  14. 如果调用 C 标准库或 C++ sleep 函数,例如在 ``unistd.h`` 中定义的 ``usleep``,那么只有当睡眠时间超过 :ref:`一个 FreeRTOS 滴答周期 <CONFIG_FREERTOS_HZ>` 时,任务才会阻塞并让出内核。如果时间较短,线程将处于忙等待状态,不会让步给另一个 RTOS 任务。
  15. 默认情况下,所有 pthread 具有相同的 RTOS 优先级,但可以通过调用 :ref:`ESP-IDF 提供的扩展 API <esp-pthread>` 对此优先级进行更改。
  16. 标准功能
  17. -----------------
  18. ESP-IDF 中实现了以下标准 API。
  19. 请参考 `标准 pthread 文档 <https://man7.org/linux/man-pages/man7/pthreads.7.html>`__ 或 ``pthread.h``,了解每个函数标准参数和行为的详细信息。下文还列出了 pthread API 与标准 API 相比的差异或局限性。
  20. .. _posix_thread_api:
  21. 线程 API
  22. ^^^^^^^^^^^
  23. * ``pthread_create()``
  24. - ``attr`` 参数仅支持设置堆栈大小和分离状态,其他属性字段将被忽略。
  25. - 与 FreeRTOS 任务函数不同,``start_routine`` 函数允许返回。如果函数返回,分离类型的线程会被自动删除。而默认的可连接类型线程将被挂起,直到调用 ``pthread_join()``。
  26. * ``pthread_join()``
  27. * ``pthread_detach()``
  28. * ``pthread_exit()``
  29. * ``sched_yield()``
  30. * ``pthread_self()``
  31. - 如果从不是 pthread 的 FreeRTOS 任务中调用此函数,断言会失败。
  32. * ``pthread_equal()``
  33. 线程属性
  34. ^^^^^^^^^^^^^^^^^
  35. * ``pthread_attr_init()``
  36. * ``pthread_attr_destroy()``
  37. - 此函数不需要释放任何资源,而是将 ``attr`` 结构体重置为默认值。其实现与 ``pthread_attr_init()`` 相同。
  38. * ``pthread_attr_getstacksize()`` / ``pthread_attr_setstacksize()``
  39. * ``pthread_attr_getdetachstate()`` / ``pthread_attr_setdetachstate()``
  40. Once
  41. ^^^^^^^^
  42. * ``pthread_once()``
  43. 支持静态初始化常量 ``PTHREAD_ONCE_INIT``。
  44. .. note::
  45. 在使用 pthread 或 FreeRTOS API 创建的任务中都可以调用此函数。
  46. 互斥锁
  47. ^^^^^^^
  48. POSIX 互斥锁被实现为 FreeRTOS 互斥信号量(普通类型用于“快速”或“错误检查”互斥锁,递归类型用于“递归”互斥锁),因此与使用 :cpp:func:`xSemaphoreCreateMutex` 创建的互斥锁具有相同的优先级继承行为。
  49. * ``pthread_mutex_init()``
  50. * ``pthread_mutex_destroy()``
  51. * ``pthread_mutex_lock()``
  52. * ``pthread_mutex_timedlock()``
  53. * ``pthread_mutex_trylock()``
  54. * ``pthread_mutex_unlock()``
  55. * ``pthread_mutexattr_init()``
  56. * ``pthread_mutexattr_destroy()``
  57. * ``pthread_mutexattr_gettype()`` / ``pthread_mutexattr_settype()``
  58. 支持静态初始化常量 ``PTHREAD_MUTEX_INITIALIZER``,但不支持其他互斥锁类型的非标准静态初始化常量。
  59. .. note::
  60. 在使用 pthread 或 FreeRTOS API 创建的任务中都可以调用这些函数。
  61. 条件变量
  62. ^^^^^^^^^^^^^^^^^^^
  63. * ``pthread_cond_init()``
  64. - ``attr`` 参数未实现,将被忽略。
  65. * ``pthread_cond_destroy()``
  66. * ``pthread_cond_signal()``
  67. * ``pthread_cond_broadcast()``
  68. * ``pthread_cond_wait()``
  69. * ``pthread_cond_timedwait()``
  70. 支持静态初始化常量 ``PTHREAD_COND_INITIALIZER``。
  71. * ``pthread_cond_timedwait()`` 超时的分辨率为 RTOS 滴答周期(参见 :ref:`CONFIG_FREERTOS_HZ`)。在请求超时后,超时最多会延迟一个滴答周期。
  72. .. note::
  73. 在使用 pthread 或 FreeRTOS API 创建的任务中都可以调用这些函数。
  74. 信号量
  75. ^^^^^^^^^^
  76. ESP-IDF 中实现了 POSIX **未命名信号量**,下文介绍了可访问的 API。除非另有说明,否则 ESP-IDF 中未命名信号量的实现遵循 `POSIX 标准规定的信号量 <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/semaphore.h.html>`_。
  77. * `sem_init() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_init.html>`_
  78. * `sem_destroy() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_destroy.html>`_
  79. - ``pshared`` 被忽略。信号量始终可以在 FreeRTOS 任务之间共享。
  80. * `sem_post() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_post.html>`_
  81. - 如果信号量的值已经是 ``SEM_VALUE_MAX``,则返回 ``-1``,并将 ``errno`` 设置为 ``EAGAIN``。
  82. * `sem_wait() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_wait.html>`_
  83. * `sem_trywait() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_trywait.html>`_
  84. * `sem_timedwait() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_timedwait.html>`_
  85. - 通过 abstime 传递的时间值将被向上舍入到下一个 FreeRTOS 时钟滴答。
  86. - 超时实际发生在被舍入到的滴答之后,下一个滴答之前。
  87. - 在计算超时后,任务有可能被立即抢占(可能性较小),从而延迟下一个阻塞操作系统调用的超时,延迟的时间等于抢占的持续时间。
  88. * `sem_getvalue() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_getvalue.html>`_
  89. 读/写锁
  90. ^^^^^^^^^^^^^^^^
  91. ESP-IDF 中实现了 POSIX 读写锁规范的以下 API 函数:
  92. * `pthread_rwlock_init() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_init.html>`_
  93. - ``attr`` 参数未实现,将被忽略。
  94. * `pthread_rwlock_destroy() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_destroy.html>`_
  95. * `pthread_rwlock_rdlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html>`_
  96. * `pthread_rwlock_tryrdlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_tryrdlock.html>`_
  97. * `pthread_rwlock_wrlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html>`_
  98. * `pthread_rwlock_trywrlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_trywrlock.html>`_
  99. * `pthread_rwlock_unlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_unlock.html>`_
  100. 支持静态初始化器常量 ``PTHREAD_RWLOCK_INITIALIZER``。
  101. .. note::
  102. 在 pthread 或 FreeRTOS API 创建的任务中都可以调用此函数。
  103. 线程特定数据
  104. ^^^^^^^^^^^^^^^^^^^^
  105. * ``pthread_key_create()``
  106. - 支持 ``destr_function`` 参数。如果线程函数正常退出并调用 ``pthread_exit()``,此参数就会被调用,或者在使用 FreeRTOS 函数 :cpp:func:`vTaskDelete` 直接删除了底层任务时被调用。
  107. * ``pthread_key_delete()``
  108. * ``pthread_setspecific()`` / ``pthread_getspecific()``
  109. .. note::
  110. 在 pthread 或 FreeRTOS API 创建的任务中都可以调用此函数。当从 FreeRTOS API 创建的任务中调用这些函数时,必须先启用 :ref:`CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS` 配置选项,以确保在删除任务之前清理线程数据。
  111. .. note::
  112. ESP-IDF 中还有其他的线程本地存储选项,包括性能更高的选项。参见 :doc:`/api-guides/thread-local-storage`。
  113. 未实现 API
  114. ---------------
  115. ``pthread.h`` 头文件是一个标准头文件,包含了在 ESP-IDF 中未实现的额外 API 和功能,包括:
  116. * 如果调用 ``pthread_cancel()``,返回 ``ENOSYS``。
  117. * ``pthread_condattr_init()`` 如果被调用,返回 ``ENOSYS``。
  118. 其他未列出的 pthread 函数未在 ESP-IDF 中实现,如果从 ESP-IDF 应用程序中直接引用,将产生编译器错误或链接器错误。如果希望 ESP-IDF 支持某个尚未实现的 API,请 `在 GitHub 上发起功能请求 <https://github.com/espressif/esp-idf/issues>`_ 并提供详细信息。
  119. .. _esp-pthread:
  120. ESP-IDF 扩展
  121. ------------------
  122. 在 ``esp_pthreads.h`` 头文件中定义的 API :cpp:func:`esp_pthread_set_cfg` 提供了自定义扩展,能够对后续 ``pthread_create()`` 的调用行为进行控制。目前提供以下配置:
  123. .. list::
  124. - 如果调用 ``pthread_create()`` 时未指定默认堆栈大小,可设置新线程的默认堆栈大小(覆盖 :ref:`CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT`)。
  125. - 新线程的 RTOS 优先级(覆盖 :ref:`CONFIG_PTHREAD_TASK_PRIO_DEFAULT`)。
  126. :not CONFIG_FREERTOS_UNICORE: - 新线程的内核亲和性/内核固定(覆盖 :ref:`CONFIG_PTHREAD_TASK_CORE_DEFAULT`)。
  127. - 新线程的 FreeRTOS 任务名称(覆盖 :ref:`CONFIG_PTHREAD_TASK_NAME_DEFAULT`)
  128. 此配置的作用范围是调用线程或 FreeRTOS 任务,这意味着 :cpp:func:`esp_pthread_set_cfg` 可以在不同的线程或任务中独立调用。如果在当前配置中设置了 ``inherit_cfg`` 标志,那么当一个线程递归调用 ``pthread_create()`` 时,任何新创建的线程都会继承该线程的配置,否则新线程将采用默认配置。
  129. 示例
  130. --------
  131. - :example:`system/pthread` 演示了如何使用 pthread API 创建线程。
  132. - :example:`cxx/pthread` 演示了如何通过线程使用 C++ 标准库函数。
  133. API 参考
  134. -------------
  135. .. include-build-file:: inc/esp_pthread.inc