如何在RTOS上全量支持C++11

简介: C语言自诞生以来已被广泛应用于系统和应用开发。

1、概述

1.1、 C++语言的特点

C语言自诞生以来已被广泛应用于系统和应用开发。比如Google的微内核操作系统fuchsia就是用C实现的,ARM的嵌入式操作系统ARMmbed也主要基于C实现。在应用开发方面,C被广泛用于GUI、游戏引擎、图形引擎、浏览器引擎、数据库等的开发。

C++语言的广泛使用,得益于其如下特点:
(1)支持面向对象编程,封装、继承、多态等机制使编程更加高效。
(2)兼容C,支持面向过程编程及驱动开发。
(3)标准库支持丰富的文件和数据结构操作。
(4)性能优异。

1.2、C++在嵌入式RTOS上的应用

随着MCU芯片处理能力的增强,嵌入式设备的图形显示、模式识别、脚本解析等能力不断赋能设备拓展应用边界。同时,随着应用的不断拓展,对嵌入式设备提出了更高的要求。通常,嵌入式系统采用C语言开发,但GUI、AI算法等复杂应用采用C开发,为此在RTOS上支持C语言的需求变得越来越强烈。

有些RTOS封装系统接口为上层应用提供了自定义的C类,但由于这些类不符合C标准,基于这些类开发的应用缺乏可移植性。另一方面,当使用外部开源软件时,需要进行适配,若软件比较复杂,适配工作量比较大,更为灾难性的是,自定义的类由于不够全,往往很难满足上层软件的需要。所以,最可行的方法还是要支持标准C++库。

image.png

本文主要阐述了基于GCC工具链在RTOS上支持C++的两个关键部分:

RTOS上对C++初始化的支持;
RTOS上适配C++库;
说明:本篇文章基于物联网操作系统AliOS Things上C++11实践总结而成,已在智能音箱等场景中应用。

2、C++初始化支持

对C语言而言,只能用常量或常量表达式初始化全局变量,比如不允许调用函数初始化,也不允许用另一个全局变量初始化。也即是说,全局变量的值在编译时就确定了。另外对于全局数组,其长度在编译时也确定了。

编译器将未赋初值的全局变量放在bss段,有初始值的全局变量放在data段(只读数据放到rodata段)。当RTOS启动时,bss段全部清为0,从程序镜像中读取data段的内容并写入到对应data段的内存,这样就完成了所有全局变量的初始化。

引入C++后,有了对象的概念,这个时候RTOS启动过程中的初始化就不再是清内存、拷贝内存那么简单了。对象内部的空间需要调用new分配,比如虚函数表、一些容器的内部存储空间。同时,对象初始化过程中需要调用父类的构造函数。这些都无法在编译时确定。

C处理这个问题的办法是:把所有C源文件中需要在初始化时调用的函数的地址集中放到一个表中,RTOS在初始化时遍历该函数表并调

image.png
RTOS启动时,初始化C++对象的伪代码如下:

for (f = __ctors_start__; f < __ctors_end__; f++) {

    (*f)();

}

3、在RTOS上适配C++库

在RTOS上实现对C的初始化支持后,下载一个芯片厂商提供的工具链,配置一下编译选项,C似乎可以跑起来,但其实存在诸多问题。

芯片厂商提供的要么是基于linux的工具链,要么是基于裸机的工具链(bare-metal)。在RTOS上显然只能选bare-metal工具链。所谓bare-metal,其含义是无操作系统平台,其线程模式为single,即无多线程并发。在这种模式下,不支持C的多线程,比如不支持mutex、thread、condition_variable等类,同时C库内部实现中不考虑多线程互斥。所以这种模式下的工具链用在RTOS上,一方面功能不全,另一方面存在稳定性隐患,尤其在多核平台上多线程并发问题将变得严重。因此,为了真正实现对C++11的全量支持,需针对RTOS进行适配。

3.1、C++库的依赖关系

GCC工具链中集成了一个C++库,其依赖关系如下:
image.png

上图中三个依赖部分说明如下:

C把C库中的符号导入到std命名空间提供给上层应用使用,同时C内部机制的实现也有赖于C库,比如输出流依赖于puts、putchar等接口。当然,C库也要实现多线程支持,这个有机会另外开辟一篇进行阐述,这里就不展开了。
libgcc库提供了一些较底层的接口与机制,比如C++的线程变量基于libgcc库提供的线程变量管理机制。
C适配层实现了C与RTOS的对接,这部分因底层OS的不同而不同。所以,ARM等芯片厂商或编译器厂商提供的针对嵌入式的工具链往往是裸机平台的,因为RTOS数量众多,无法一一满足。针对特定RTOS的多线程版本工具链需RTOS厂商自己定制。

3.2、适配

适配主要涉及类型与接口两部分,具体可参考./gcc/libgcc/gthr.h文件。

3.2.1、适配的接口说明如下:

image.png

3.2.2、适配的接口如下
image.png

3.2.3、适配说明

用typedef把C内部类型定义为RTOS的类型,基于RTOS的接口实现上述适配接口,便完成了C库的适配。如果RTOS上已经完成了对posix接口的支持,那么适配就比较方便了。示例如下:

//__gthread_t类型定义

typedef pthread_t __gthread_t;



//__gthread_create接口实现

static inline int

__gthread_create (__gthread_t *__threadid,  void *(*__func) (void*), void *__args)

{
    return pthread_create (__threadid, NULL, __func, __args);
}

3.3、配置与重编C++库

在编译C++库时,需配置为使能多线程模式,主要配置项如下:
--enable-threads=posix

其实使能该选项只是触发了配置脚本检查是否支持多线程,若配置脚本执行过程中判断系统不支持多线程,最终编译出来的库还是单线程的。比如,配置脚本中对__GTHREADS_CXX0X宏是否定义进行了判断,若该宏未定义则使能多线程失败。

完成适配与配置后,重编工具链即可生成多线程版本的C库。编译完成后可查看cconfig.h文件,确认使能的C++特性。以_GLIBCXX_USE_SCHED_YIELD宏为例,若没有生成该宏,那么thread类yield()函数实现为空函数、

4、后记

得益于C++良好的封装机制,用C++写的代码比用C写的代码bug率低很多。但硬币的另一面是,调试难度增加了。

即便开启了编译优化,C语言的一行语句与汇编的对应关系也相对比较清楚。但C++由于其复杂的机制,一行简单的赋值语句往往会对应十几条、甚至几十条汇编。这需要RTOS的维测能力提供高效的调试支持。

相关文章
|
6月前
|
消息中间件 物联网 数据处理
TDengine 数据接入功能支持 Wonderware 了,不需一行代码完成迁移工作
TDengine 3.2.3.0 引入了 Wonderware Historian (AVEVA Historian) 连接器,简化了数据迁移至 TDengine 的过程,增强时序数据管理与分析。此连接器解决了传统实时数据库扩展性问题,提供成本效益高、国产化的替代方案。TDengine 已支持多种数据源集成,如 OPC、Kafka、MQTT等,打造统一数据平台,助力企业数字化转型。作为高性能时序数据库,TDengine 提供云原生解决方案,减少系统复杂度,加速业务创新。
73 6
|
5月前
|
分布式计算 DataWorks NoSQL
MaxCompute产品使用合集之数据总线同步到DataWorks的任务状态持续显示为HANG(挂起)且同步延迟不断增加,该如何排查
MaxCompute作为一款全面的大数据处理平台,广泛应用于各类大数据分析、数据挖掘、BI及机器学习场景。掌握其核心功能、熟练操作流程、遵循最佳实践,可以帮助用户高效、安全地管理和利用海量数据。以下是一个关于MaxCompute产品使用的合集,涵盖了其核心功能、应用场景、操作流程以及最佳实践等内容。
|
7月前
|
Unix Linux Shell
不同RTOS中POSIX接口的实现差异
本文探讨了在开发实时应用时使用POSIX API来实现跨平台和可移植性的策略。
126 1
不同RTOS中POSIX接口的实现差异
|
7月前
|
存储 NoSQL Redis
高性能存储 SIG 月度动态:多项内核特性移植到 6.6,erofs 完成共享特性 POC
高性能存储 SIG 月度动态送达,一键了解各项目当前进展。
如何将文章同步到其他平台?
如何将文章同步到其他平台?
458 0
如何将文章同步到其他平台?
|
NoSQL Cloud Native Redis
【性能优化下】组织结构同步优化二,全量同步/增量同步,断点续传实现方式
【性能优化下】组织结构同步优化二,全量同步/增量同步,断点续传实现方式
|
canal SQL Oracle
新的增量数据同步工具闪亮登场,完美支持Oracle增量同步
新的增量数据同步工具闪亮登场,完美支持Oracle增量同步
新的增量数据同步工具闪亮登场,完美支持Oracle增量同步
|
存储 运维 监控
用这个小工具解决了Linux下文件实时和增量同步两大问题
用这个小工具解决了Linux下文件实时和增量同步两大问题
用这个小工具解决了Linux下文件实时和增量同步两大问题
|
Linux 调度
内核开发基础-如何使用内核延时
从事Linux内核开发特别是驱动开发的小伙伴,肯定需要经常使用到定时器,比如,按键的去抖、LED屏幕显存buffer的刷新等。同时,在控制硬件时,可能会用到十分精确地短延时,这时,定时器的精度就不能满足这种需求了,这时就会使用到高精度定时器和忙等延时。今天就来简要说一下如何正确的使用内核提供的delay和sleep函数。
385 0