# 前言

newlib 的 strtod、sprintf、sscanf、snprintf、vsnprintf 这些函数都是非线程安全的,这些函数的行为和 keil \ IAR 以及我们的认知并不一样,这些函数在使用 %f or %lf 来输出浮点时,会在申请一块 3.5K 左右的内存空间,该内存空间只申请一次,且不释放。作为全局内存使用。如果有多个线程同时调用 sprintf 来字符串化浮点数据,会有几率或较大几率导致系统死机(尤其是多个线程高密度的调用 sprintf 来输出浮点数据)。如果仅用来输出字符串或整形数据,则不会出该问题,即该问题仅局限于浮点数这一块。

总而言之,sprintf、sscanf、snprintf、vsnprintf、sscanf 这些函数在不处理浮点数相关的时候还是线程安全的,一旦涉及到浮点数 %f or %lf 就会申请一块公共的内存空间 (3.5KB) 导致线程的不安全;而 strtod 以及 printf 是彻头彻底的线程不安全(printf 线程不安全是正常的,printf 在哪个编译链下都是线程不安全的;而 strtod、sprintf 等函数理论上都应该是线程安全的 https://manpages.courier-mta.org/htmlman3/strtod.3.html)。

# 版本

以下说明仅对于:

1、FreeRTOS v9.0.0(https://github.com/FreeRTOS/FreeRTOS/tree/V9.0.0)

2、GNU Arm Embedded Toolchain 10-2020-q4-major(https://launchpad.net/gcc-arm-embedded/10.0/10-2020-q4-major)内置的 newlib 库

# FreeRTOS 对 newlib 的支持

当我们使用 FreeRTOS 的同时,需要值得注意的是,必须要使我们的执行任务处于线程安全之中,否者将会容易出现数据访问异常;而对 newlib 库来讲,其本身是默认不带线程安全保护的(因其内部大多数调用了 malloc() 等内存分配函数),虽是这样,但 FreeRTOS 仍为其上下文管理提供支持,因此当我们在 FreeRTOSconfig.h 中添加:
#define configUSE_NEWLIB_REENTRANT 1 // newlib sprintf、strtok 等的线程安全需要... ,那么,在使用此选项时 FreeRTOS 将执行以下操作(在 task.c 中):

  • 对于每个任务,在任务控制块 (TCB) 中分配并初始化一个 newlib 重入结构。
  • 每次任务切换,设置 _impure_ptr 指向新激活任务的可重入结构。
  • 在任务销毁时,清理重入结构(帮助 newlib 释放任何相关内存)。

当然,newlib 支持是已经包含在 FreeRTOS 的普遍需求中了,但 FreeRTOS 维护者本身并未使用。FreeRTOS 是不负责由此产生的 newlib 操作的,因此用户必须熟悉 newlib,并且必须提供必要存根的系统级实现,即这部分还需要您的供应商提供内置 newlib 中内存管理线程安全的支持,否者,就自己设计实现了一个系统级的内存管理线程安全(主要是 malloc() 等函数的可重入等问题)

# 当在 FreeRTOS 中直接使用 newlib 库的 printf/sprintf/snprintf/vsprintf/vsnprintf 时出现的问题

前面说了 newlib 库本身是默认不带线程安全保护,因此使得一些调用了 malloc() 函数的 API 函数线程不安全了,这样的结果往往就会导致出现 Hardfault 异常,因此也写了 ARM Cortex-M3/M4/M7 Hardfault 异常分析 这篇笔记。嘛,摊牌了,是来填坑的。

至于这种情况,网上出现很多这样的问题(本次碰到了,为了避免忘记,所以也记录一下吧):

FreeRTOS 社区:

HardFault from printf - freertos 9.0.0 / stm32f0 / gcc-arm-eabi 7.2.0

How to make printf/sprintf/strtod thread safe

How to catch code that caused the hard fault

I/O Hardfault

ST 社区:

HardFault Debug in STM32CubeIDE

BUG: CubeMX FreeRTOS projects corrupt memory

注意:在 CubeMX 中生成的,对 newlib 库可重入函数 _sbrk () 的实现,需要干掉它(至少在我对它进行编译的时候,出现 Hardfault 异常的可能性更大)

stackoverflow 社区:

problem with sprint/printf with freeRTOS on stm32f7

附:newlib 可重入问题及外部接口定义

Reentrancy

Definitions for OS interface

# 调用 printf/sprintf/snprintf/vsprintf/vsnprintf 的线程安全处理

在使用 printf/sprintf/snprintf/vsprintf/vsnprintf 等 newlib 库中的 I/O 输入输出 API 函数时,由于其调用了 malloc() 函数等问题,导致整个任务线程处于非安全状态,然而对于一些任意内部使用 malloc 系列或依赖于特定于线程的可重入上下文的库,您往往无能为力,因为您并没用这么大的精力去主动对 malloc() 等函数进行加锁保护。那么不如考虑一下大佬提供的如下操作:

https://nadler.com/embedded/newlibAndFreeRTOS.html

https://nadler.com/embedded/NXP_newlibAndFreeRTOS.html

https://github.com/DRNadler/FreeRTOS_helpers

# newlib 下 memcpy/memset 函数可能导致的死机问题

在 newlib 下面的 memcpy 以及 memset 等函数是不考虑字节对齐问题的,这可能会导致指针地址为非字节对齐时直接死机。

详见:https://github.com/lvgl/lvgl/issues/2790#issuecomment-980481374

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

夏沫の浅雨 微信支付

微信支付

夏沫の浅雨 支付宝

支付宝