Newlib
Newlib 是面向嵌入式系统的完整 C 库实现,属于独立开源项目,Zephyr 不包含其源代码。Zephyr SDK 则为每种受支持的架构提供预编译库(libc.a 和 libm.a)。
备注
GNU Arm Embedded 等第三方工具链也附带预编译形式的 Newlib。
Zephyr 实现了由 Newlib 标准 C 库函数调用的“API 钩子”函数。这些函数位于 lib/libc/newlib/libc-hooks.c,将库内部系统调用转换为等效的 Zephyr API 调用。
Newlib 类型
Zephyr SDK 中的 Newlib 有 full(完整)和 nano(精简)两个变体。
完整 Newlib
Newlib 完整变体(libc.a 和 libm.a)是 Zephyr SDK 中功能最齐全的 Newlib 变体,支持几乎所有标准 C 库功能。它优先优化性能而非代码体积,因此占用显著大于 nano 变体。
在应用配置文件中选择 CONFIG_NEWLIB_LIBC,并取消选择 CONFIG_NEWLIB_LIBC_NANO,即可启用此变体。
精简 Newlib
Newlib nano 变体(libc_nano.a 和 libm_nano.a)针对体积优化,支持完整变体的全部功能,但不支持 C99 新增的格式说明符,例如 char 和 long long 类型的格式说明符,即 %hhX 和 %llX。
在应用配置文件中选择 CONFIG_NEWLIB_LIBC 和 CONFIG_NEWLIB_LIBC_NANO,即可启用此变体。
注意,并非所有架构都提供 Newlib nano 变体。是否可用由 CONFIG_HAS_NEWLIB_LIBC_NANO 指定。
格式化输出
Newlib 支持所有标准 C 格式化输入输出函数,包括 printf、fprintf、sprintf 和 sscanf。
Newlib 格式化输入输出实现支持 C 标准定义的所有格式说明符,但有以下例外:
浮点格式说明符(例如
%f)要求启用CONFIG_NEWLIB_LIBC_FLOAT_PRINTF和CONFIG_NEWLIB_LIBC_FLOAT_SCANF。Newlib nano 变体不支持 C99 格式说明符,即
char的%hhX、long long的%llX、intmax_t的%jX、size_t的%zX和ptrdiff_t的%tX。
动态内存管理
Newlib 实现了内部堆分配器,用于管理标准动态内存管理接口(例如 malloc() 和 free())使用的内存块。
不同 Newlib 类型的内部堆分配器可能不同。例如,Zephyr SDK 的完整 Newlib(libc.a 和 libm.a)向操作系统请求更大的内存块,其最低内存需求显著高于精简 Newlib(libc_nano.a 和 libm_nano.a)。
Newlib 动态内存管理函数与 Zephyr 侧 libc 钩子之间的唯一接口是 sbrk(),Newlib 通过它管理为内部堆分配器保留的内存池大小。
Newlib 的内存池大小变更请求由 _sbrk() 钩子处理(实现于 libc-hooks.c),该钩子在系统内存不足时返回错误,确保内部堆分配器的内存池不超出可用空间。
启用用户空间时,Newlib 内部堆分配器的内存池放在名为 z_malloc_partition 的专用内存分区中,供用户模式线程访问。
Newlib 堆的可用内存空间取决于系统配置:
启用 MMU(选择
CONFIG_MMU)时,Newlib 堆的保留空间取k_mem_free_get()返回的空闲内存大小与CONFIG_NEWLIB_LIBC_MAX_MAPPED_REGION_SIZE两者中的较小值。启用 MPU,且 MPU 要求分区大小和地址对齐均为 2 的幂(
CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE为非零值)时,Newlib 堆的保留空间由CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE指定。否则,Newlib 堆的保留空间等于 SRAM 区域中空闲、尚未分配的内存大小。
Newlib 实现的标准动态内存管理接口是线程安全的,允许多个线程同时调用。