您可以采取一些好的技巧来提供帮助。
一是性能好。VxWorks 以其非常好的上下文切换时间而著称。无论您使用哪种锁定解决方案,它都可能涉及信号量。我不会害怕为此使用信号量(复数),它们在 VxWorks 中得到了很好的优化,并且快速的上下文切换时间有助于减少评估许多信号量状态等导致的性能下降。
此外,我会忘记使用 POSIX 信号量,它们将简单地叠加在 VxWork 自己的信号量之上。VxWorks 提供本地计数、二进制和互斥信号量;使用适合的那一款会让这一切变得更快。二进制有时非常有用。多次发布,永远不会超过1的值。
其次,写比读更重要。当我在 VxWorks 中有这种需求并且一直使用信号量来控制访问时,我使用任务优先级来指示哪个任务更重要并且应该首先访问资源。这工作得很好;从字面上看,VxWorks 中的所有东西都是一个任务(嗯,线程),就像其他任何东西一样,包括所有设备驱动程序等。
VxWorks 还解决了优先级倒置(Linus Torvalds 讨厌的那种事情)。因此,如果您使用信号量实现锁定,则可以依靠 OS 调度程序在较低优先级的读取器阻塞较高优先级的写入器时分配它们。它可以导致更简单的代码,并且您也可以充分利用操作系统。
因此,一个潜在的解决方案是使用单个 VxWorks 计数信号量来保护资源,并将其初始化为等于读取器数量的值。每次读者想要阅读时,它都会使用信号量(将计数减少 1。每次读取完成时,它都会发布信号量,将计数增加 1。每次写入者想要写入时,它都会使用信号量 n(n =读者数)次,完成后发布n次。最后使写任务的优先级高于任何读者,并依靠操作系统快速的上下文切换时间和优先级倒置。
请记住,您是在硬实时操作系统上编程,而不是在 Linux 上。获取/发布本机 VxWorks 信号量并不涉及与 Linux 上的类似行为相同的运行时间,尽管如今即使 Linux 也相当不错(我现在正在使用 PREEMPT_RT)。可以依赖 VxWorks 调度程序和所有设备驱动程序来运行。如果您愿意,您甚至可以将您的编写器任务设置为整个系统中的最高优先级,甚至高于所有设备驱动程序!
为了帮助解决问题,还要考虑每个线程在做什么。VxWorks 允许您指示任务是否正在使用 FPU。如果您使用本地 VxWorks TaskSpawn 例程而不是 pthread_create,那么您有机会指定它。这意味着如果您的线程/任务没有进行任何浮点数学运算,并且您在调用 TaskSpawn 时已经说过,上下文切换时间会更快,因为调度程序不会费心保留 /恢复 FPU 状态。
这很有可能成为您正在开发的平台上的最佳解决方案。它发挥了操作系统的优势(快速信号量、快速上下文切换时间),而无需引入大量额外代码来重新创建其他平台上常见的替代(可能更优雅)解决方案。
第三,坚持使用旧的 GCC 和旧的 Boost。基本上,除了打电话给 WindRiver 和讨论购买升级的低价值建议外,我无能为力。就我个人而言,当我为 VxWorks 编程时,我使用的是 VxWork 的原生 API 而不是 POSIX。好的,所以代码不是很便携,但它最终很快;无论如何,POSIX 只是原生 API 之上的一层,这总是会减慢速度。
也就是说,POSIX 计数和互斥信号量与 VxWork 的本机计数和互斥信号量非常相似。这可能意味着 POSIX 分层不是很厚。
关于 VxWorks 编程的一般说明
调试我一直试图使用可用于 Solaris 的开发工具 (Tornado)。这是迄今为止我遇到过的最好的多线程调试环境。为什么?它允许您启动多个调试会话,一个用于系统中的每个线程/任务。您最终会为每个线程提供一个调试窗口,并且您可以单独且独立地调试每个线程。跳过阻塞操作,该调试窗口被阻塞。将鼠标焦点移动到另一个调试窗口,跳过将释放块的操作并观察第一个窗口完成它的步骤。
你最终会得到很多调试窗口,但它是迄今为止调试多线程东西的最佳方式。它使编写非常复杂的东西并发现问题变得非常容易。您可以轻松地探索应用程序中的不同动态交互,因为您可以随时简单而强大地控制每个线程正在执行的操作。
具有讽刺意味的是,Windows 版本的 Tornado 不允许您这样做。每个系统只有一个可悲的单个调试窗口,就像任何其他无聊的旧 IDE(如 Visual Studio 等)一样。我从未见过甚至现代 IDE 在多线程调试方面与 Solaris 上的 Tornado 一样好。
硬盘如果您的读者和作者正在使用磁盘上的文件,请考虑 VxWorks 5.5 已经相当老了。不会支持像 NCQ 这样的东西。在这种情况下,我提出的解决方案(如上所述)可能会更好地使用单个互斥信号量来完成,以阻止多个读取器在读取磁盘的不同部分时相互绊倒。这取决于您的读者到底在做什么,但如果他们正在从文件中读取连续数据,这将避免读/写头在磁盘表面来回颠簸(非常慢)。
在我的例子中,我使用这个技巧来调整网络接口上的流量;每个任务都在发送不同类型的数据,任务优先级反映了网络上数据的优先级。它非常优雅,没有消息被分割,但重要的消息占据了可用带宽的最大份额。