这 POSIX 吗?探究不同操作系统和库的网络实现¶
标题:Wait is it POSIX? Investigating Different OS and Library Implementations for Networking
日期:2026/03/06
作者:Katherine Rocha
链接:https://www.youtube.com/watch?v=wDyssd8V_6w
注意:此为 AI 翻译生成 的中文转录稿,详细说明请参阅仓库中的 README 文件。
大家好,我是 Katherine Rocha,我们将探讨 POSIX 网络以及不同操作系统及其实现方式。
先简单介绍一下我自己。我是 Catalyst Space 的一名软件工程师,我们有一套名为 Ether 的定制飞行软件解决方案,主要使用 C++ 编写,但也包含一些 Python。另外,大家可以了解一下,我的一些爱好包括系谱学(genealogy),实际上这也是我在大多数演讲中都会提到的内容,因为我认为回顾过去以在未来做出更好的决定,以及理解我们对未来的影响,是我们能做的最重要的事情之一。
简单介绍一下我就职的公司,这样大家就能理解我为什么关注这些内容。Catalyst 致力于制造能够进行动态太空操作的卫星。这意味着你的航天器可以不断升级,我们可以将其移动到不同的轨道,从而延长目前已经在太空中的航天器的寿命。我们主要致力于三个领域,而我倾向于更多地关注升级平台和寿命延长方面。这是我们正在研制的最大的一款航天器,它相当大,但我们也致力于一款较小的航天器,更多是用于概念验证以及做一些其他的事情。这就是那个非常简单的模型,我们在上面运行 Ether 软件。随之而来的是很多能力。相比典型的太空应用,我们拥有更丰富的内存资源,这使我们能够在太空中运行 Debian 系统。这非常棒,也很了不起,但也意味着我们不会遇到太空操作中通常会遇到的所有问题。
接着就遇到了这种情况。我的一位同事开始着手这个项目,硬件已经确定了,操作系统也已经确定了。我们在讨论时发现,他实际上被迫使用一种不同的操作系统,这种操作系统不允许我们当前的飞行软件在其上运行。所以,我们需要弄清楚什么是可能的,什么是不可能的,以及我们可以迁移什么?因为对于飞行软件来说,真正重要的是拥有已经在太空中经过测试的软件,我们验证过它能工作,验证过其行为符合预期。
所以我们将探讨以下几个方面。这是我的调查过程。有很多事情并不顺利,也有一些事情做对了,我们正在边做边摸索。引用 Ben Deane 的话,我们在“通过玩耍来学习”(learning through play)。所以在我们需要弄清楚哪些网络功能有效、哪些无效之前,我们要稍微“玩”一下使用不同的操作系统。我们能为未来做些什么不同的设计,来设计我们的库和用户代码?这样当情况发生变化,突然我们需要使用一个没预料到的实时操作系统(RTOS),而我们现有的东西都不适用时,我们就不会遇到这些问题了。
在过程中如果有任何问题请随时提问,以确保我们的理解一致。
为什么我们需要库支持?¶
我们有这些长期的太空项目,最终我们的软件需要工作很长一段时间。因此,能够使用不同的库和现代代码是非常有帮助的。我们目前实际上运行的是 C++23,所以能够在这些我们需要长期保持寿命的系统中使用现代技术真的很有帮助。
我们还有非常早期的工具链冻结(tooling freezes),所以在进入测试之前,我们要尽早冻结工具。我们希望在整个公司的工具套件中保持兼容性,而且业界也喜欢在不同小组之间尝试共享一些东西。所以能够拥有这种共享是非常重要的。而且我们希望我们的任务关键型边缘代码能够拥有与目前部分桌面代码相同的支持。
开放系统与 POSIX¶
我们将看看“开放系统”。开放系统有一系列不同的目标。
互操作性(Interoperability): 即我们跨网络移动数据和信息的能力。
应用可移植性(Application Portability): 即将应用程序从一个系统移动到另一个系统。
用户可移植性(User Portability): 如果你是一个用户,要去访问不同的系统,我们能否真正与该系统进行交互?这方面的一个好例子是,如果你主要使用 Linux,然后决定跳到 Windows 上用 PowerShell 做点什么并开始操作,你会很快碰壁,因为当你不再习惯它时,它并不好用。
现在我们来看看 POSIX。POSIX 的关键词是可移植(Portable)。我们的理念是能够在各种不同的系统、各种不同的事物上使用 POSIX。这些目的是以此作为 POSIX 网站上的说明之一。我们的想法是,我们希望 POSIX 能够成为我们可以使用并作为事实来源的东西。因此,当我们编写不同的代码时,我们可以使用它,并知道这将能与其他人互通并协同工作。这都要回到开放系统的概念,即这将会起作用,因为我们在其他东西中也使用过它。
典型的 POSIX 示例¶
对于一个典型的 POSIX 例子,我喜欢回想操作系统课程。在操作系统课上,我倾向于使用 Pthreads。当我看 Pthreads 时,我就想,这个 P 代表什么?它是 POSIX。然后我想,好吧,当我使用 Sockets(套接字)时,它们前面并没有 P。所以它们肯定不是 POSIX。
这里的 POSIX 代表 POSIX 线程 API。有趣的是,当 POSIX 被创建时,他们试图弄清楚哪些标准已经存在,哪些不存在。因此,POSIX 线程是在大家开始采用并统一步调的标准出现之前就已经实现的。所以他们能够制定标准并加以利用。相比之下,当我们看 Sockets 时,它们最早出现在 BSD 中,也被称为 Berkeley Sockets。所以他们采用了那个已经存在的接口,并能够将其应用于 POSIX。这样你就不用为了符合标准而重写所有代码。它也遵循 Unix 的“一切皆文件”模型。
C 风格网络示例与 C++ 化¶
我们将看一个简单的 C 风格网络示例。因为当你处理像 Sockets 这样的东西时,不幸的是你倾向于从 C 开始。但在这种情况下,我们并不是真的在看它是如何工作的,我们更多的是在看 API 是如何工作的。因为我们希望能够跨操作系统使用该 API,并将我们的代码从一个东西移植到另一个东西,并希望底层的东西做同样的事情。
这是一个非常简单的例子。基本上,它所做的就是拉取 NIST 时间服务器来获取当前时间。这就是这里试图做的全部事情。
当我们看着这个并打算使用 C++ 时,我们要将其“C++ 化”。所以我想到的大事情是:
我们要去掉前面的
struct关键字。我们要去掉 C 风格的转换(C style cast)。感谢 Ben 和 Jason 谈论了对此的替代类型,也就是使用
bit_cast或memcpy。我们还要将所有的数组迁移到
std::array。
我认为需要寻找的一些非常重要的调用是 socket、connect、read 和 close。
socket根据域类型和协议创建未连接的套接字。一定要注意这些,因为这在讲座后面涉及到其他操作系统做的事情时很重要。connect将套接字连接到特定的 IP 和端口。read将从缓冲区读取并获取数据。close将关闭套接字。
正如我所说,我们并不真正关注具体的函数,因为网上有大量的文档和示例。如果你想弄清楚如何做 Sockets,上网查,你可能会得到比我给出的更好的例子。但我们只是想看看 API 是什么样的。此外,我们还有 #include <sys/socket.h>,这非常重要,因为我们希望 API 和那个文件保持一致。
首先,我们来看看 POSIX 标准,你可以在网上访问它。当我们退后一步看它时,我们会想,好的,太棒了。这让我们想起了什么,对吧?是 Man pages(手册页)。它们看起来几乎一模一样。这其实真的很酷,因为这意味着我们确实是在尝试使用被访问的手册页中的标准。
那么我们应该如何利用这些信息呢?这就是使用多种资源的想法。
我们应该针对能实现最大可移植性的页面进行设计。
但要使用系统手册页(System Man Page)作为事实来源。
使用 POSIX 手册页来设计我们的代码。但如果有任何奇怪或反常的地方,使用我们特定操作系统的手册页来确保我们得到相同的行为。这个问题不仅存在于实时操作系统中,在切换 Unix 发行版或例如从 Ubuntu 移动到 FreeBSD 时也会出现,你可能会遇到一些奇怪的事情,即行为并不完全符合你的预期。
可移植性与实时操作系统 (RTOS)¶
现在谈谈可移植性。我们真的希望我们的代码能在尽可能多的地方运行。传统上,这意味着在多个编译器和多个操作系统上运行。但嵌入式领域最大的问题之一是,除了你在工业界工作且这是必须要用的需求之外,这些不同的操作系统(如实时操作系统)并不常在许多测试中运行。因此,我们希望确保当我们做事时,我们在流水线中进行测试,这样我们就不会失去支持。比如,即使我们今天不使用 RTEMS,也要添加带有我们网络的 RTEMS 运行测试,只是为了确保我们将来不会失去这种支持。
我们来谈谈为什么要使用实时操作系统。这里有谁使用或曾经使用过实时操作系统?好的,这里的大多数人都是。我们会简短一点。
这主要关乎控制。我们要有我们的时序和截止期限(deadlines)。我们希望确保事情是可复现的,并且我们可以把它放在我们需要放置的芯片上。它在“裸机+中断服务程序(ISR)”和“完整操作系统”之间取得了一种平衡。
一些例子包括:VxWorks, FreeRTOS, RTEMS, Zephyr,还有 Linux + Real-time 补丁。在实时操作系统上做事有机会减少 Linux 带来的一些开销。
RTOS 与其他操作系统不一致的地方往往在于初始化方面。它往往也是高度可定制的,而且倾向于“仅使用你需要的”。网络功能往往默认是不开启的。这非常好,因为它允许你减少代码大小,增加确定性(determinism),并降低功耗。但你也失去了像传统 Linux 那样好用的所有工具。
FreeRTOS 案例研究¶
我们先看看 FreeRTOS,这是我研究的第一个实时操作系统。它已经开源 20 年了。这里有谁用过 FreeRTOS?很酷,大部分人都用过。实际上我之前没意识到,它现在是 AWS 组织的一部分,他们将其用于 IoT 设备。而且它的应用也非常广泛,用在很多不同的地方。
它只包含实时内核。所以它没有像网络功能那样的特性。它最初只有调度功能、任务间通信、计时和同步原语。但你可以在事后添加诸如命令控制台接口和网络栈之类的东西。
对于网络功能,网上说他们拥有 Berkeley Sockets API。我们想,太棒了。这就行了。我们的代码可以迁移了。我们只要复制粘贴,这就太容易了,没人需要处理这个麻烦。它还是完全可重入和线程安全的 API。所以我们想,太好了,我们要把所有好的选项都勾选上。
然后我让大家看看这个。这是 API。有什么奇怪或有问题的地方吗?
(观众回答:全部)
全部?是的。
他们决定在所有东西前面加上 FreeRTOS_。所以现在我们不能只是做包含、复制粘贴就能工作了。这太不幸了。我们的 include 文件不同。我们开始遇到很多小问题,东西将无法移植。
这里还有一个奇怪的事情。就是我之前告诉大家要注意的域类型协议(domain type protocol)。最后一个,协议(Protocol)。在 POSIX 标准中,你可以将其设置为零,它会找到默认值。而在 FreeRTOS 的指令中,标准的要求是必须是 FREERTOS_IPROTO_TCP 才能创建 TCP 套接字,或者是 UDP 才能创建 UDP 套接字。没有其他值是有效的。
所以突然之间,我们从那个我们以为是标准的东西上偏离得更远了。我们不能只是做个“查找替换”,在前面加上 FreeRTOS,然后就能工作了。
关于可移植性:它很容易使用,很容易识别,并且与我们之前使用的类似。所以如果以前用过套接字的人,可能会在其前面加上前缀,就能创建套接字代码。它会工作。你可以把你现有的东西并排放在一起然后把所有东西都移过去。但我们的代码不能只是复制粘贴,我们突然失去了我们已有的所有飞行遗产(flight heritage),因为仍然有非零的几率会有 bug 潜入。所以我们失去了以前拥有的那种信任。而且我们不能只有一个包含库代码的库并在任何我们想用的地方使用它。
抱歉。它还有很好的示例,对于快速入门非常有帮助。
那么,如果我们真的想用它呢?我们被锁定在这个 FreeRTOS 基础设施上,我们要继续使用它。那么让我们像 Boost.ASIO 那样。我们将添加一个 API 选项,做一个 #define,使用预处理器,如果是这种方式就做点什么启用它,如果是另一种方式就使用我们的 Linux 风格,然后可能抛出一些错误检查以确保这些奇怪的偏差在后端被捕获。这样我们的用户就不必处理它并弄清楚如何使用它。这往往是你最终使用的一个非常大的方法,即试图隔离这些差异并将其提取到你的库代码中,这样用户就不必处理它了。
RTEMS 案例研究¶
接下来是 RTEMS。这里有谁用过 RTEMS?好的,有一位。
实际上我是今年在飞行软件研讨会(Flight Software Workshop)上了解到这个的,他们谈到了 RTEMS,他们说,这太棒了。你应该看看并尝试使用它。这构成了这次演讲的另一个驱动因素,即有机会玩转不同的实时操作系统,并了解其优缺点,以及我们在哪里可以进行改进并了解如何做事。
这也是开源的。而在他们的整个介绍中,我最喜欢的一句话可能是“它鼓励支持和使用标准 API”。我想,太棒了。我们的代码能工作了。事情会很棒。它支持 18 种处理器架构,200 个板级支持包(BSP)。所以我想,好吧,我们的东西大概可以在这里工作。我没用什么奇怪的东西。所以可能会没事的。
它拥有来自 FreeBSD 的全功能 IPv4、IPv6、TCP/IP 栈。这给人很好的感觉。我想,Berkeley Sockets,我们有 BSD。事情会起作用的。
再加上它使用非常现代的编译器。我想,我们稳了。他们在 GitLab 上有一个 GCC 的镜像。里面有一个非常现代的编译器镜像。所以我想,我们很棒,我可以使用我的 C++23 特性,生活会很美好。它还有一个非常活跃和令人惊叹的社区。
对于网络支持,他们实际上将 libBSD 内核的一部分移植到了 RTEMS。这是他们网站上的一些图片。有点小,但你可以看到他们有所有不同的函数,POSIX 函数。然后他们还有一些非常好的命令。
RTEMS 上的 POSIX 示例¶
那么,让我们从最初的 POSIX 示例开始。我们要调用我们的初始化函数(post initialization)。他们有一个 init 函数,你可以用来派生任务。在他们的典型示例中,你只是在 init 中调用 Hello World。你验证你实际上可以使用 RTEMS 并获得输出。我想,好吧,太好了。我们只要把我们的网络代码放进去。我们不需要客户端,不需要服务器。我们要拉取一个文件。我们就把它放进去,它会工作的。
然而,我立即得到了“网络不可达”(network is unreachable)的错误。我想,好吧,这很艰难。不知道这里发生了什么。有两种潜在的错误:
错误的 TAP 网络配置。
错误的 RTEMS 网络设置。
我在模拟硬件上运行 RTEMS。所以我可能没有对连接到网桥以试图通过它获取互联网的 TAP 接口做些什么。这是我猜测问题可能所在的地方。但因为我之前真的没用过 RTEMS,我想,也许是 init 的问题。
所以我在他们的在线社区从一位叫 Chris 的人那里得到了很多帮助。他非常有帮助。我们到了这一步。实际上,有趣的事情之一是我必须生成我自己的 /etc/rc.conf。我能够使用 DHCP,但我必须生成它,然后将其引入我们的初始化中。
看看我们的配置设置,这在我们要真正能够使用它之前需要做大量的初始化工作。这与我们的 Linux 操作系统非常不同,就像我在工作中通常使用的那样。在工作中,我能够直接做事,因为东西已经存在了。而在这种情况下,我必须确保所有正确的东西都已初始化,我有正确的设置以便能够做我想做的事情。
调试与 QEMU¶
我的部分问题很可能出在 QEMU 上。这里有谁用过 QEMU 吗?好的,超过一半,大概三分之二。基本上,它是一个机器模拟器和可视化器。我使用的是 Xilinx Zynq A9。我有一个连接到网桥的 TAP。你实际上可以在这里看到 TAP,它是 qtap。它正在拉取我的 hello.exe 并运行它。我知道它在运行,但在运行时我得到了那个错误。
当我实际上在我的宿主机器上执行 ifconfig 之类的操作时,我能够看到我通过网桥获得了数据包,但我什么也没收到。所以我想,好吧。显然,我配置错了一些东西。
所以,一件非常酷的事情是,好了,我们现在试图调试。当我们试图在 Linux 中测试网络问题时,我们倾向于使用 shell。我们想,我们不会创建一个 C++ 程序来测试我们操作系统中的网络问题。这很好。但是,我想,好吧,这是一个不同的操作系统。我该怎么做?我做什么?如果你回到网络支持的那张幻灯片,你会注意到有大量的命令。那些是我认识的命令。我想,哦,我们有 ping。我们还有什么?我们有 route。我们有 ifconfig。我想,我们稳了。我们可以看到里面发生了什么。
所以我们运行了这些调用。你会注意到我在试图出去时得到了同样的“主机不可达”(Host Unreachable)。我也能看到,好吧,我确实配置了一些东西。我有看起来表面上合理的配置。但是为了完成这次演讲,我想,好吧,我花了大量时间试图诊断硬件问题和诊断我的网桥不工作。所以,让我们转向做点不同的事情。
感谢 Robert 找到了这个 XKCD 的梗图(大概指关于配置/调试的复杂性)。但我最终看了看,并与 RTEMS 的人发了消息。他们说,嗯,我们使用 FreeBSD。所以,就像,你为什么不直接在 FreeBSD 中运行它呢?你的网桥的所有东西都是通过文件配置的。你可以那样测试它。我想,哦,这太棒了。好吧,行。我开始那样做,直到我意识到必须为你安装包管理器。我想,这是一个会花费很多时间的麻烦事(can of worms)。
是的,我稍微深入了一下这个麻烦事。但这就有点像我们从中得不到多少收获。为了构建所有东西,你实际上必须构建所有的构建工具。拉取这些可能需要很长时间。所以我让东西整夜运行了 13 个多小时。我想,这花太长时间了,没有进展。
转向 Loopback 测试¶
如果我们看看我们从 ifconfig 得到的结果,我们确实有 Loopback(回环)。而且我正确地设置了它。我们可以成功 ping 通 localhost。所以我想,好吧,我们要转向,我们要看看在同一个上面做一个客户端/服务器,只使用 Loopback。这样我们可以验证我们确实有数据通过,并且我们能够使用我们的 Linux 示例并将其拉过来。这就只是一个稍微不同的例子。
我们将派生两个任务。RTEMS 既有任务(Tasks)也有 POSIX 线程(Pthreads)。或者对不起,是任务和 POSIX 线程。我使用了他们的默认任务,以避免更多的错误空间和配置问题。因为他们有一些关于如何做到这一点的很棒的例子。我只是不想再处理潜在的配置缺失问题了。不得不为此挣扎。
我们将让客户端套接字连接到服务器并接收数据。服务器套接字连接到客户端并发送数据。
这是我们的客户端代码。这看起来好多了。看起来非常相似。有一个我在 Linux 中通常看不到的调用。大家看出来是什么了吗?是 exit 调用。通常当我编写 C++ 程序时,我会调用 return。在这里,我在见过的例子中传统上看到他们调用 exit。所以这是有点不同的地方。
现在我们有我们的 Loopback 服务器代码。非常相似,基本上就是你在网上找到的试图做 Loopback 的代码。
现在我们来看看我们的输出。我们想,耶,我们得到了输出。事情正在工作。我们能够连接。而且如果我们回去看,那段代码看起来几乎正如我们在使用典型 C 或 C++ 的 Linux 操作系统上所期望的那样。所以这太棒了。事情运作良好。这使我们能够拥有我们要寻找的代码可移植性。把我们的 return 改成 exit 并不是世界末日,而且很容易做到。
经验教训与 Boost.ASIO¶
这大概就是我学到的一些东西。在 Linux 和 Windows 中编程有时可能会很难。比如,在网络方面我可能会掉进很多陷阱。但是当你开始进入 RTOS 并做一些带有额外抽象层和可视化的事情时,你最终可能会遇到你没预料到的额外问题。
一切都需要配置和初始化,不像我们的典型操作系统。我们不需要调用所有这些东西来验证事情将被初始化并能够使用我们的线程。但这实际上也非常强大,因为我们可以控制我们使用的东西。所以,如果你不打算做像我们的用例那样实际上在太空中运行 Web 服务器的事情,你就不需要我们正在使用的所有功能,你可以节省空间。
Boost.ASIO 调查¶
现在我们来看看 Boost.ASIO,因为我不只是想运行原始套接字。我想使用一个库来运行套接字。我们在工作中已经封装了 Boost.ASIO。所以我想,这太棒了。一切都会很好。
我看了支持的平台,我很乐观。我想,好的。我们有 GCC,你可以看出来。我们在 RTEMS 上运行的 GCC 版本是 13.4。我想这比我在运行的 Linux 系统上的当前版本低一个小版本。所以,这太棒了。这太棒了。然后我们在运行某种 FreeBSD-Linux 风格的东西。有希望。也许这都能一起工作。
对于 ASIO,我想,好吧。我们得从源代码拉取,因为我们不知道到底怎么回事。我们需要预编译头文件。而且它是 header-only(仅头文件)的。所以我们应该没事。我缺少一些需要的 #define。所以我能够重新定义它们。然后我能够开始运行。我实际上能够成功运行 io_context。但在 io_context 之后的任何事情,我最终都遇到了“主机未找到”(host not found)。
所以后台肯定有一些有趣的事情在发生。我还没开始追踪那个。但这是我想在未来继续做的事情,以了解这里有什么奇怪的地方?发生了什么?我们需要做什么?但我们能够引入库并进行 #include。比如 #include <boost/asio.hpp> 并将其引入。所以在 RTEMS 中引入 Boost 并使用它是有很大希望的。
库如何识别操作系统¶
现在我们要跳一点,看看不同的库是如何弄清楚我们是什么操作系统的。这似乎是我们试图引入这些库时的问题所在。东西被包装在类似“如果未定义 Windows,那么我们做这个。或者如果定义了 Linux,我们做这个。”
首先,我们可以看看系统机器系统名称以及机器是什么。然后我们开始查看所有这些随机的 #define,你可以在随机的库中找到它们,并试图弄清楚哪些在哪里使用,哪些是有效的。所以我只是包装了它们并发送了一个 cout。我想,我们会弄清楚这是哪一个。
好的。这里有人猜出我们在这个上面得到了哪些输出吗?它有点像包装 FreeBSD,但也有些不是。有人认为任何 __BSD 下划线的宏有输出吗?(观众无反应)好的。没人摇头。
好吧。我们得到了 RTEMS。我们得到了它在 ARM 上运行,这两者都是真的。这就是我们要模拟的。并且定义了 POSIX_VERSION。
但是没有一个 BSD 预处理器输出被定义。这真的很酷,因为如果我们使用这个 POSIX 版本,我们就可以在所有这些不同的库中拥有兼容性,因为大多数东西都包装了某些 POSIX 的东西。但是如果你开始看它,很多东西只是直接去看“是不是 Linux”。所以我们正在失去这些不同库和不同用例能够互通并拥有我们需要的那种可移植性的能力。所以,这可能就是 Boost.ASIO 内部正在发生的事情,即某些东西正在使用“是不是 Linux”来进行判断。
ZeroMQ 调查¶
现在我们要看一点 ZeroMQ。这里有谁用过 ZeroMQ?好的。大概一半吧。
它是一个开源的通用消息库。我喜欢认为它做的是“花式套接字”。所以他们做像 pub/sub(发布/订阅)、push/pull(推/拉)、client/server(客户端/服务器)。但它是有点定制的。当我倾向于使用它时,我倾向于想在两边都使用它。我是说它也支持各种不同的传输。
当我们看我们的 ZeroMQ 库时,这里有一堆库。所以我拉取了所有的 C++ 库,我想还有 C 的。我想,好吧,我们要选哪一个?因为这里有很多。我就像,我不知道选哪个。
首先,我们要开始剔除其中一些。那些包装了其他东西的,我想,如果“其他东西”不能编译和工作,这个也不会工作。这就留下了 libzmq 和 azmq。azmq 正在包装 Boost.ASIO。既然我们需要弄清楚 Boost.ASIO 出了什么问题,那么一旦 Boost.ASIO 能工作,这可能就能工作。然后是 libzmq,它实际上是 C++98 带有一些 C++11 片段。我想实际上可能是 C98。但是是的。所以那是我们要看的那个。
现在我开始深入到底层。我觉得了解库发生了什么的最佳方式是不要害怕反向深入源代码。我想,也许我们有希望让事情工作。我看了 ip.cpp,因为我想,我们有这个 open_socket 调用。事情会很顺利。我可以看到套接字。我想,视线中没有 Linux 的 #define。也许有希望。而且它只在寻找“如果不是 Windows”。所以我们不是 Windows。也许事情会工作。也许事情会好起来。这看起来真的很棒。
但是因为我们在不同的硬件上运行,我们必须手动从源代码构建。RTEMS 的示例使用 Waf。你可以潜在地使用 CMake。但我只是利用示例来进行。Waf 不是 libzmq 的预配置构建选项之一。因为不是预配置的构建选项,它在构建过程中没有设置特定的 #define。所以我试图从不同的构建过程中提取它们。它检测到我在尝试使用它时并不是我所说的那个人。所以,是的。它对我试图绕过这个问题并不太高兴。
但仍有可能引入该包。既然它是一个自定义的 GCC 版本,我也许仍然可以使用包管理器将其引入。或者切换到在 CMake 中做一些事情,然后以这种方式引入。而且如果所有的东西都是通过“非 Windows”来访问的,那就有可能工作。看起来事情会工作。所以我认为我们很有可能不需要更改大量代码就能让事情工作。
未来的 RTEMS 调查¶
未来要做的一些 RTEMS 调查。
我在设置硬件和尝试运行东西方面挣扎了很多,我认为这是做嵌入式工作的一个非常常见的问题:一旦你认为事情会很快工作,什么都不会像你想象的那么快。事情往往不工作。然后你陷入非常长的编译时间。所以你只是烧掉了大量的时间却没做多少事情。
我认为调查其他一些 POSIX 实现(如 Pthreads)可能真的很有价值。这样我们的线程库就可以跨平台工作。以及能够做像使用 std::thread 做事这样的事情。
我真的很想尝试在 RTEMS 中运行我们的飞行软件。但我们需要弄清楚那些大的障碍,比如 Boost.ASIO。我们还有一堆其他的小库。所以要弄清楚我们可以搞定其中哪一个。
然后在 RTEMS 中使用各种 Boost 库。RTEMS 确实有各种不同的包,例如 libbsd。所以我认为能够添加额外的包(如 Boost)将使这个社区能够更多地使用 C++,并使用我们在做应用程序代码、做 Linux 代码时喜欢使用的一些现代工具。这将使不在嵌入式领域的人更容易进入嵌入式领域并尝试做不同的事情。
此外,为不同的 POSIX 实现创建一个最小的 RTEMS 示例。RTEMS 的困难之一是它没有大量的示例。我用来寻求帮助的很多东西都是测试代码。所以就像是,看测试。这有实现。当你不知道到底发生了什么,直到你对你应该包含什么和不应该包含什么感到困惑之前,这真的很好用。我认为一些最小的示例肯定会让它更容易访问和使用。
我真的想对 RTEMS 社区说声谢谢。他们非常有帮助。我在很多深夜试图从他们那里获得配置和初始化网络的帮助。以及帮助我弄清楚这整个理念。令人惊叹的 C++ 社区也在我遇到的随机问题上给予了帮助。这也真的很有帮助。
总结¶
让我们让我们的库为每个人工作。我认为我们需要确保做的一件事是让我们的操作系统 API 匹配其中一个标准。我们需要东西能够使用我们认为它们会使用的东西,并且能够在不同的东西之间具有那种可移植性。
然后对于库,我认为我们需要在设计中开始拥抱非 Windows 和 Linux 的操作系统。你知道,让其中一些 CI 流水线也运行不是常规操作系统的不同操作系统。这样它们实际上会在这些设计事项中被考虑进去。当我们看到这些未来的事情,比如一些网络功能被添加到标准库中时,这就好像我们需要在这个问题上考虑我们的整个用户群,否则事情将无法按我们希望的方式工作。我们只会留下一堆实际上无法操作的功能。
从用户方面来说,我认为要学的一件事是你永远不知道什么时候你需要切换操作系统。这是我们在工作中经常看到的事情,比如我们在 Linux 中做了一些东西,在 Windows 中做了一些东西,我们的 Windows 机器已经被擦除过几次,所以我们必须不断重新安装 WSL。所以弄清楚你需要什么操作系统以及如何快速在其上设置,以及习惯使用各种操作系统是非常有帮助的。
然后使用你的库,但也不要害怕围绕你使用的库构建包装器(Wrapper)。如果你包装了你的库,你就不必直接与任何可能改变或有问题的 API 交互。
还有不要害怕向操作系统专家寻求帮助。他们喜欢提供帮助,而且非常乐于助人,他们可能是在我能够让事情工作方面影响最大的人。
然后检查你是否在使用标准。如果你在使用标准,那是一件大事,这将允许你拥有跨各种不同东西的可移植性。所以我们想确保我们在前进中拥有这种能力。
所以,是的。这就是我在试图调查这个过程中学到的一些东西。谢谢。
问答环节¶
问: 有什么问题吗?
观众 A: 是的。谢谢你的精彩演讲。我真的很喜欢。我们在 POSIX 方面也遇到了很多类似的事情。我们在电视上发布软件。所以虽然不是卫星,但也属于类似领域。基本上我有一个问题。你们是直接向卫星发送更新的二进制文件吗?比如原生可执行文件?这是第一个。第二个,一般来说,迫使你们使用那些操作系统的限制是什么?比如基本上你们做出的选择,那里的限制是什么?
回答: 是的。对于第二个问题,对于我们要初始发射的卫星,我们的很多限制最终是易用性。所以我们实际上现在正在运行 Debian。Debian 包装在 Docker 中,所以它能够重启。所以我们正在使用一些非常现代、非常有趣的架构决策来实现这一点。但是当涉及到这个较小的架构时,有很多预设计是在我们许多人到那里并能够做出这些设计决策之前发生的。所以他们只是按照那个文档去做,那些街道(此处可能指路线图/规范),你会想,什么是有意义的?什么是对太空有意义的?太空是一个非常奇怪的世界,因为你喜欢以前工作过的东西。所以当我们试图这样做时,就像是他们有点顺从了那些决定。然后因为太空有很多长周期(long lead)的东西,东西刚刚开始进来,所以没有能力去转向和改变。
观众 A: 很合理。
回答: 对。然后对于第一个问题,我们倾向于尝试将我们的代码发送上去。我想我们,Julian,我们怎么把代码弄上去?
同事(Julian): 直接。是的。我们就像直接通过类似文件共享的方式发送上去。
回答: 好的。很酷。是的。谢谢。
观众 B: 嘿,你有试过屏幕上列出的其他 RTOS 吗,比如 Zephyr 之类的?那些支持怎么样?因为我认为你提到的那两个是相当老的。或者至少开始得较早。
回答: 是的。我只用了 RTEMS 和 FreeRTOS。我听说过 Zephyr 并且稍微接触过一点,但我这次没有选择处理它。我认为这在未来是非常值得探索的事情。
观众 C: 对。我知道你的演讲是关于 POSIX 的,但我忍不住注意到在那张关于 RTEMS 基于 BSD 构建的能力的幻灯片上,它支持很多是 BSD 但不是 POSIX 的东西。例如,它声称支持 Kqueue。你有什么想法,因为当你展示所有的宏时,RTEMS 并没有被识别为 FreeBSD。你有什么想法,关于比如当你继续致力于与 ASIO 集成时,试图让它弄清楚操作系统就像 BSD 并使用那些非标准的东西,有点反转你的演讲剧本?
回答: 是的。我认为那可能真的很聪明。我认为对于我们要看的很多用例,是我们已经在使用 Linux。所以我有点喜欢那种归结为我们可以在这些不同东西上使用的最通用的东西的想法。这样我们就可以在我们的软件中拥有那种可移植性。但在一般情况下,我认为对于像 ASIO 这样的东西,既然库已经具有这些能力,利用已经可用的工具和已经可用的标志是非常有意义的。所以我认为那可能是最好的选择。
观众 D: 鉴于你在这些库中发现了一些兼容性、可移植性问题,你会做什么样的上游(upstreaming)工作,或者你被允许做什么?
回答: 是的。我很幸运。我认为这是我在我们一直在做的一些事情中看到很多挣扎的地方,就是我们迭代得太快了,以至于试图将东西推向上游变得非常困难。所以我们的很多东西,我们最终只是 fork,或者人们有本地副本并只是在那里不断修改。因为这更多是我最终在搞的个人事情,而且我们目前没有在工作中使用它,我实际上已经能够(做一些事情)。其中一件事是,缺少一个 extern "C"。所以我包含了一个头文件,得到了一堆像函数调用丢失之类的错误。所以我实际上能够将其作为一个 issue 提交。而且我认为鉴于 Boost 对 C++ 有多重要,以及这个社区对 C++ 有多重要,我真的很想花些时间试图弄清楚如何将 Boost 引入那里并弄清楚上游方面的事情。很酷。谢谢。
观众 E: 当使用实时操作系统时,我的担忧之一是引入一个可能在该操作系统上编译得很好的库,但它可能会使用一些违反实时约束的系统调用。你有过这样的经历吗,或者是关于实时操作系统和那些没有实时支持的第三方库的经历,你会遇到什么样的问题?
回答: 所以我遇到很多问题,我们主要使用 Linux 做事,我们做了一点,我做了一点像 Linux 中的实时扩展,所以我不太常遇到那些问题。但我认为一般来说,试图弄清楚团队中是否有人做嵌入式系统与实时操作系统并稍微知道发生了什么。这样,这不仅仅取决于开发人员,也取决于使用库的人,而且库作者也关心它并在社区中有投入,这是使其成功并思考这些事情的关键。因为我们所有人都带着不同的心态和关于什么是重要的、什么不是重要的不同想法而来。所以如果我们带着这些不同的想法进入,我们会创造出最好的结果,因为我们没有一群想法相同的人。
观众 E: 谢谢。
回答: 太棒了。非常感谢大家。
主持人/观众: 谢谢。