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

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

2000元阿里云代金券免费领取,2核4G云服务器仅664元/3年,新老用户都有优惠,立即抢购>>>


阿里云采购季(云主机223元/3年)活动入口:请点击进入>>>,


阿里云学生服务器(9.5元/月)购买入口:请点击进入>>>,

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月前
|
数据库
分布式集群时钟同步问题及解决方案
分布式集群时钟同步问题及解决方案
226 1
|
4天前
|
存储 NoSQL Redis
高性能存储 SIG 月度动态:多项内核特性移植到 6.6,erofs 完成共享特性 POC
高性能存储 SIG 月度动态送达,一键了解各项目当前进展。
|
4天前
|
存储 监控 负载均衡
TiDB数据迁移工具TiCDC:高效同步的引擎
【2月更文挑战第28天】TiCDC是TiDB生态中一款强大的数据迁移工具,它专注于实现TiDB增量数据的实时同步。通过解析上游TiKV的数据变更日志,TiCDC能够将有序的行级变更数据输出到下游系统,确保数据的实时性和一致性。本文将深入探讨TiCDC的原理、架构、应用场景以及使用方式,帮助读者更好地理解和应用这一工具,实现高效的数据迁移和同步。
|
5月前
|
NoSQL Cloud Native Redis
【性能优化下】组织结构同步优化二,全量同步/增量同步,断点续传实现方式
【性能优化下】组织结构同步优化二,全量同步/增量同步,断点续传实现方式
如何将文章同步到其他平台?
如何将文章同步到其他平台?
343 0
如何将文章同步到其他平台?
|
存储 安全 Linux
嵌入式应用的超轻量级、高性能的 C/C++ 日志库
嵌入式应用的超轻量级、高性能的 C/C++ 日志库
501 0
嵌入式应用的超轻量级、高性能的 C/C++ 日志库
|
存储 SQL Cloud Native
PolarDB-X内核新版本:更精细的数据管理
非常高兴为大家带来PolarDB-X内核5.4.14版本。在最新版本中提供了冷热数据存储分离、数据Locality、数据热点诊断、并行DML优化、Flashback Query和AUTO_INCREMENT兼容性,大幅提升了数据的可管理性、SQL处理能力和兼容性,并针对数据热点问题为用户提供了更多运维手段。
|
canal SQL Oracle
新的增量数据同步工具闪亮登场,完美支持Oracle增量同步
新的增量数据同步工具闪亮登场,完美支持Oracle增量同步
新的增量数据同步工具闪亮登场,完美支持Oracle增量同步
|
存储 消息中间件 SQL
http://www.vxiaotou.com