当前位置: 首页 > 工具软件 > mbed OS > 使用案例 >

Mbed OS 文档翻译 之 参考(贡献(指南(软件设计)))

端木狐若
2023-12-01

软件设计

Arm Mbed 软件原理:

  • 是一致的。
  • 直观。
  • 简单。
  • 可靠。

风格

请参阅 Mbed 风格指南

组织

Arm Mbed OS 代码库被组织成概念子模块,以限制个人贡献的范围和复杂性。这些模块作为单个 Git 仓库包含在 Mbed OS 代码库中。我们建议外部库使用此模型。

  • 模块应在 OS 树中进行逻辑分组。避免使用通用词;有意命名。

  • 使用模块名称后跟下划线来为每个源文件添加前缀。这可以防止与不同模块中的其他类似命名文件冲突,例如 nanostack/thread.c 和 drivers/Thread.cpp;并非所有工具链都能够支持具有相同名称的目标文件。

    mbed-os/rtos/rtos_thread.cpp
    mbed-os/rtos/rtos_semaphore.cpp
    mbed-os/drivers/drivers_analog_in.cpp
    
  • 始终使用路径中的模块目录包含头文件。例如:#include “lwip/lwip-interface.h”,include “drivers/Ticker.h”。将 include 路径限制为模块目录。这允许将来移动模块。

  • 作为模块的入口点(来自用户空间),我们建议使用单个头文件。例如:mbed.h,rtos.h。

  • 头文件应限制外部包含,以避免间接暴露不相关的 API。头文件不应扩展名称空间。

  • 在 C++ 模块中,API 应包含在与模块名称匹配的命名空间中。例如:mbed :: Ticker,rtos :: Thread,netsocket :: Socket。

  • 在C模块中,每个非静态函数和类型都应该以模块的名称作为前缀,后跟下划线。例如:mbed_critical_section_enter(),lwip_gethostbyname(host)。

  • Mbed OS 代码库中包含的模块可以在单独的仓库中进行镜像。源回购应该清楚地标识并链接到模块的自述文件。

  • 特殊目录应遵循一致的命名约定。

贡献

  1. 请参阅 Mbed 贡献指南

  2. 每个拉取请求应该用于单一目的。

  3. 代码必须编译每次提交。

  4. 提交消息应该以子模块名称和冒号为前缀:

    lwip: Fixed buffer overrun in rx loop
    The rx loop did not properly wait for rx semaphore to release
    causing the buffer to overrun
    
  5. 修补程序必须在返回到一个或多个发布分支之前登陆主服务器。

  6. 功能开发可以在单独的分支中进行,并在完成时将其带到 master。

  7. 绝不能重写主分支和发布分支。

  8. 所有贡献者必须签署 CLA

  9. 对于传入来源,唯一可接受的许可证是:

    • MIT。
    • Apache。
    • Permissive Binary License。

API 设计

通用模块可以分为两个 API,即前端(或用户 API)和后端(或移植层)。用户 API 描述了库实现的程序员接口。对于 Mbed OS,面向用户的 API 应采用基于 C++ 类的接口,而移植层应采用 C 兼容接口。

API 设计 - 用户 API

  • 每个模块都应提供面向对象的 C++ 用户 API。
  • 目前的标准严格来说是 C++ 03(便携性)。
  • 状态应包含在相关的 C++ 类中。
  • 在采用代码库中不存在的语言功能之前,请三思而后行:
    • 用 C++ 的优点来思考 C。
    • 不要让用户学习新东西。
    • 例外和 RTTI 被禁用。
    • 由于对系统资源的未知影响而避免使用 STL。
  • 首选 C 语言功能而不是 C++ 语言功能:
    • 在模糊的重载上使用不同的函数名称。
    • 使用 uint8_t read_8(void), uint16_t read_16(void), uint32_t read_32(void)
    • 使用 void write_8(uint8_t), void write_16(uint16_t), void write_32(uint32_t)
    • 将模板限制为类型和数组大小。
  • 根据以下规则使用指针和引用。所有权可能含糊不清的使用适当的文档:
    • 使用复制或不可变引用来传递具有值语义的类型。
    • 使用指针借用动态多态类的所有权。
    • 使用指针转移动态多态类的所有权。明确记录转让的所有权。
    • 在引用上使用指针:
      • C 用户比较熟悉。
    • 所有权在语法上是清楚的。
  • 将类组织为两种类型:
    • 具有值语义的类型,例如 SocketAddress 和 Callback。
    • 内置类型的用户友好替代品。
      • 无法扩展(遭受对象切片)。
    • 按值传递,不需要内存管理(在栈上处理)。
    • 如果可能,传递 const-reference 以减少复制。
    • 必须便宜复制。
    • 应该很小(<= 16 字节)。这不包括间接引用的内存。
    • 动态多态类,如 Ticker 和 EthernetInterface:
      • 参与班级层次结构。
      • 可以通过接口抽象。
      • 可扩展 - 应该有虚拟表。如果可重载,必须包含虚拟析构函数。
      • 通过指针传递;内存管理应留给用户。
      • 应将复制构造函数和复制赋值运算符声明为私有。如果确实需要复制,我们建议使用虚拟克隆成员函数,以避免切片问题,并指出克隆是一个非常重要的操作。
      • 如果一个类包含一个非常大的内存区域(> 64 字节),则更喜欢动态分配该区域;如果用户在栈上实例化类,它可以防止栈溢出。
  • 一个类应该有一个责任。例如:UDPSocket vs TCPSocket,Ticker vs Timer。
  • 将继承用于 “是一种” 关系。例如:UDPSocket 和 Socket。
    • 首选基于模板的多态性的抽象基类,以避免代码大小增加。
    • 更喜欢在基于预处理器的条件编译和其他形式的间接调度的 C++ 代码中编写抽象接口。
  • 不要在全局范围内声明对象(应用程序应分配全局对象)。在全局范围内声明的对象在链接时很少被编译器垃圾收集。这可能会导致应用程序的大小膨胀。
  • 将 get/set 函数与私有成员变量一起使用可以隐藏用户的内部状态。
  • 如果无法发出错误信号,请避免可能失败的操作。类构造函数不应该失败。
  • 不可恢复的错误(例如中断中的 OOM 和互斥锁)不应返回给用户。
  • 应将可恢复的错误(例如 UDP 数据包丢失)通过错误代码传播给用户。
  • 小心处理弃用。在下一次主要操作系统修订之前,我们不会删除已弃用的 API。弃用的原因包括:
    • 设计模式陷阱导致开发人员编写错误的代码。
    • 代码功能不正确。
    • 代码不安全(同步)或导致未定义的行为。

API 设计 - 移植层

  • 每个模块应提供与 C 兼容的移植层。
  • 目前的标准严格来说是 C99(便携性)。
  • 移植层不应该假设它是如何被消耗的。
  • 状态应该包含在由用户 API 指针传递的结构中。避免全局状态。
  • 移植层的设计应允许在实现中尽可能多地采用合理的方差。
  • 简洁是美好的。

线程和 IRQ 安全

  • 用户 API 应该是线程安全的。
  • 如果用户 API 旨在中断安全,则应明确记录。
  • 如果用户 API 无法保证线程安全,则应使用警告符号明确记录。在所有 API 中使用一致的表单:“警告:不是线程安全的”。
  • 模块的移植层应该设计用于非线程安全的实现。
  • 如果在中断上下文中调用回调,则应明确记录负责的 API 并发出警告。在所有 API 中使用一致的表单:“警告:从中断上下文调用”

文档

  • 模块中的每个函数和类都应提供 doxygen 注释,记录函数以及每个参数和返回值:

    /** Wait until a Mutex becomes available.
     *
     * @param   millisec  timeout value or 0 in case of no time-out. (default: osWaitForever)
     * @return  status code that indicates the execution status of the function.
     */
     osStatus lock(uint32_t millisec=osWaitForever);
    
  • 每个类的头文件的 doxygen 必须包含使用 @code 和 @endcode 的使用示例。

  • 每个 API 还应该在类头示例的基础上提供 @code 和 @endcode 部分。

  • 如果需要有关方法的更具体信息,则应使用 @note 完成此操作。

  • 如果不推荐使用某个方法,则必须使用 @deprecated 标记并包含要替换它的方法的说明。

  • 每个模块都应提供一个记录模块的自述文件:

    • 自述文件应以一个小段落开头,向用户介绍模块,而无需事先了解。
    • README 应包含一个代码示例,说明如何使用该模块。
    • 如果模块包含移植层,则 README 应包括移植指令。
    • 如果模块包含测试,则 README 应提供测试指令。
  • 扩展文档应位于模块的 docs 目录中,并带有模块自述文件中的相应链接。

测试

  • 每个模块都应包含一个测试目录,其中包含涵盖模块功能的测试。
  • 测试应根据被测试的类别进行组织;每个类大约有一个测试文件。
  • 代码库中包含的测试必须与 Mbed OS 测试框架兼容。
  • 为了避免回归,每个错误修复都应该包含一个额外的测试用例,用于识别错误并在修复错误之前确定性地失败。

完整的文档

配置

Mbed OS 为应用程序开发提供了强大的配置系统。但是,模块还应该关注在 Mbed 构建系统之外保持可配置性。模块应在简单的头文件中提供记录良好的配置选项。

  • 每个模块都应该提供带有配置选项的 module_lib.json(或类似)。
  • 每个配置选项都应包含涵盖其目的和对系统的影响的文档。
  • 为了帮助移植新目标,每个配置选项应提供合理的默认值(如果未定义配置选项)。
  • 配置选项不应更改 API 的行为。
    • 首选用户 API 中需要不同功能的多个类。
  • 目标和应用程序应该能够覆盖每个配置。
  • 在所有平台上,默认的优化选择应该是大小。

完整的文档

 类似资料: