Genode操作系统框架20.08

2020-08-29 07:05:10

Genode版本20.08有两个主要主题:增加本地工作负载的重量,以及增强系统对驱动程序故障的恢复能力。

对于本机工作负载,我们所说的是直接在Genode上执行的软件,而不依赖于虚拟机。与静态系统或虚拟机管理程序场景相比,当涉及到POSIX运行时的质量、Genode协议栈的性能和软件移植的经济性时,这样的工作负载是不可原谅的。通过激活Chromium web引擎,我们将Genode暴露在现有最重的商用软件堆栈中。我们很高兴在改进的Qt5集成和工作流程一节中报告,基于Chromium的Falkon Web浏览器现在可以托管在Sculpt OS上。这项工作的附带影响至少与特定的应用程序一样有价值:改进了移植大型软件项目的工作流,并覆盖了许多以前隐藏的C和C++运行时的角落案例。

在Sculpt OS中,Genode在出现图形或输入驱动程序故障时的恢复能力受到关注。“GUI堆栈,重新堆叠”一节描述了一项复杂的操作,它将底层GUI堆栈颠倒过来,为无需重新启动即可交换或动态更新驱动程序等激动人心的功能铺平了道路。

除了这些主要主题外,当前版本还延续了两个长期项目,即CBE块加密器和对64位ARM设备的深入支持。章节一致的块加密器介绍了CBE的新的*可插拔加密*和信任锚设施。设备驱动程序一节详细介绍了64位ARM*上稳步兴起的*驱动程序环境。

当前版本突破了Genode低级GUI堆栈的几个限制。在此架构更改之前,经过了为期两年的规划和探索阶段,在我们的脑海中旋转了以下目标:

实现对故障设备驱动程序的恢复能力。Genode上的图形应用程序应能够承受驱动程序故障。我们努力将过于复杂的设备驱动程序排除在安全关键型应用程序的可信计算基础之外。

在不损害用户安全和隐私的情况下启用对屏幕捕获和远程桌面方案的主要支持。

为将来复杂的多头场景铺平道路,例如使用一个以上的图形卡。

优化像素数据路径的吞吐量和输入等待时间,目的是在没有撕裂伪影的情况下获得高响应的交互体验。

这一改变的关键思想和计划已经在重新堆叠GUI堆栈的专题文章中进行了阐述。我们执行了本文中概述的步骤,正好赶上当前版本。

Genode的GUI堆栈的起点是2006年推出的NitPicker GUI服务器,该服务器基于Genode开发人员先前的研究(PDF)。当时,实现的名称不知何故在整个Genode中传播开来,从API级别、配置级别一直到相关组件的命名。多年来,术语不假思索地悄悄进入了这个项目。这必须停止,现在是正确的时机。重命名会影响以下区域。

INCLUDE/NITPICER_SESSION标头已移动到INCLUDE/GUI_SESSION。这些标头并不绑定到特定的挑剔实现,但要通用得多。例如,窗口管理器提供同一接口的另一种实现。

挑剔服务现在称为Gui&34;服务。上/下大小写遵循在Genode核心组件之外提供的服务的约定。

Genode的帧缓冲区会话接口传统上基于RGB565像素格式。我们现在已将格式更改为32位XRGB,其中忽略了X部分。我们相应地更新了所有受影响的库、图形应用程序和设备驱动程序。

此更改也会影响Drivers_Interactive软件包的用户。现在,我们通常为驱动器子系统分配64 MiB RAM和1500个CAP,这足以覆盖每像素32位的高分辨率,并可容纳多组件USB HID输入堆栈。

传统上,Genode用于帧缓冲区和输入驱动程序的驱动程序模型遵循分层架构。每个帧缓冲区驱动程序自然提供帧缓冲区服务,每个用户输入驱动程序提供输入服务。在下一层,NitPicker GUI服务器将使用这些服务作为客户端,进而提供更高级别的NitPicker服务(现已重命名为GUI)。在此体系结构中,较高级别自然依赖于所有较低级别,特别是底部的帧缓冲区和输入驱动程序。各组件之间的相互作用如下所示。箭头表示客户端-服务器关系,其中箭头从客户端指向服务器。驱动程序和NickPicker GUI服务器显示为红色,因为它们是服务器。右侧的GUI应用程序是挑剔的客户端。反过来,NitPicker是帧缓冲区和输入驱动程序的客户端。

这种架构是有缺陷的,因为它使受信任的低复杂度GUI服务器(NitPicker)依赖于高复杂度且可能脆弱的设备驱动程序。如果驱动程序失败,整个GUI堆栈-直到应用程序-都会受到影响。正确地看待风险:NitPicker GUI服务器的二进制文件包含大约11,000行代码,其中包括Genode API。实际挑剔的代码大约是3000行纯C++代码。相比之下,英特尔帧缓冲区驱动程序包含超过122,000行代码(Sloccount)。代码复杂性等于风险。NickPicker对英特尔帧缓冲区驱动程序的依赖是站不住脚的。对于像USB HID这样的输入设备,情况看起来非常相似。

为了解决这个难题,我们必须颠倒GUI服务器和驱动程序之间的客户机-服务器关系。此方法由两个新的服务接口启用。新的";事件";会话取代了以前的输入会话。新的捕获会话取代了以前的帧缓冲区会话。流经这些界面的信息保持不变-输入事件和像素-但方向相反。事件客户端将输入事件传播到服务器。捕获客户端从服务器获取像素数据。

在新的体系结构中,NitPicker仍然是唯一的服务器,提供事件、捕获和图形用户界面。它不再依赖于任何司机的福祉。将GUI服务器集成到初始化配置中时,这会由nitPicker的<;start>;节点反映出来:

要保留对GUI服务器的级联使用方案的支持,仍可以通过指定<;config>;属性request_framebuffer=";yes";和request_input=";yes";指示nitPicker请求帧缓冲区会话或输入会话。如果指定,则nitPicker会在启动时请求输入&34;和帧缓冲区&34;服务的会话,并将其用作输入/输出后端。这些服务通常由gui_fb组件提供。请注意,此工具最终可能会被REQUEST_GUI属性(请求GUI会话)所取代,从而完全消除帧缓冲区和输入服务的概念。

在引入新的捕获会话接口之后,我们在所有帧缓冲区驱动程序中用新的捕获会话接口替换了对帧缓冲区会话接口的使用。所有驱动程序的新版本都必须在各自的硬件上进行测试。一般来说,驱动程序变得更简单了。

与图形驱动程序类似,所有输入驱动程序都必须重新编写,才能作为事件客户端而不是输入服务器运行。这归根结底是在所有支持的平台上调整和测试各种PS/2驱动程序和USB HID驱动程序。作为次要说明,server/ACPI_INPUT伪驱动程序已经变成了APP/ACPI_EVENT应用程序。与帧缓冲区驱动程序类似,从输入服务器到事件客户端的转换通常会使驱动程序变得更简单。

各种主板的Drivers_Interactive软件包已经更改了它们的公共接口。驱动程序子系统不再提供";FrameBuffer";和";Input";服务,但需要有效路由才能到达由nitPicker提供的";Capture&34;和";Event&34;服务。不用说,这一更改影响了所有使用NickPicker的运行脚本。

从输入会话到事件会话的转换不仅限于驱动程序,还会影响Genode的输入过滤机制。INPUT_FILTER的功能现在由EVENT_FILTER提供。事件过滤器只请求一个事件会话作为过滤结果的目标,通常会将其路由到NitPicker GUI服务器。它提供可连接任意数量的事件源的事件服务。

过滤链的配置几乎保持不变。不再需要声明<;input>;节点。相反,配置必须指定<;策略&>节点,这些节点定义了";事件&34;客户端(事件源)到筛选器链中使用的输入的映射。

到目前为止,已经使用Genode构建系统构建了Genode的Qt库和应用程序。Libports/lib/mk目录中的Qt库构建文件部分是使用来自GNU make文件的shell脚本生成的,该文件是qmake工具在为Linux构建Qt时生成的。此外,只需将qmake项目文件解释为GNU make文件,并将相关的qmake变量转换为相应的Genode构建系统变量,就可以从qmake项目文件构建Qt应用程序。

但是当构建qtwebengine模块(不建议使用的qtwebkit模块的后继者)时,这种方法就不再可行了。qtwebengine模块基于Chromium web引擎源代码,并且大部分是在官方支持的平台上使用忍者构建系统构建的,而不是qmake。

我们还想让Genode用户更容易构建Qt应用程序,特别是通过使用Goa工具。最好能够使用qmake工具实际处理qmake项目文件,而不是将它们解释为GNU make文件并在项目文件包含GNU make不理解的特定于qmake的代码时中止。

出于这些原因,我们重新设计了Qt构建流程,现在它使用带有定制Genode平台配置的qmake来构建Qt库和应用程序。

如果您已经为Genode构建了一个Qt应用程序,并且希望在新版本中继续使用它,但是您还不能使用Goa工具(例如,因为其他Qt库不能用GOA构建,或者因为GOA目前不支持x86_64以外的其他架构),那么您需要更新项目的target.mk文件(以及任何运行脚本)。有关详细信息,请查看libports存储库中更新的Qt示例项目,如果在转换过程中出现任何问题,请随时询问Genode邮件列表。还需要使用tool/tool_chain_qt5脚本重新构建Qt工具链,以构建并安装qmake工具。

在转换Qt示例项目的运行脚本时,我们切换到了Drivers_Interactive包的使用,这是目前为交互场景启动驱动程序的首选方式。缺点可能是网络或存储驱动程序不是以这种方式加载的。如果Qt项目需要这些功能,建议的解决方案是使用Goa,或者在可能的情况下使用Sculpt OS运行场景。

此版本中与Qt相关的另一个特性是,qt5_component库现在可以从配置ROM中提取命令行参数和环境变量,就像POSIX库已经为非Qt应用程序所做的那样。

由于之前移植的Arora Web浏览器已经多年没有上行维护,同时它所依赖的qtwebkit Qt模块也已弃用,因此最终是时候尝试将更新的基于Chromium的qtwebengine模块移植到Genode,并找到一个新的和维护的基于qtwebengine的Web浏览器来取代Arora。乍一看,将qtwebengine移植到Genode似乎是一项相当具有挑战性的任务,因为官方只支持Linux、Windows和MacOS,而且大多数qtwebengine代码(也包含大量的第三方库)在很大程度上不像其他Qt模块那样使用qmake构建,而是使用忍者构建系统构建。

幸运的是,事实证明问题没有预期的那么严重,至少在让轻量级网络浏览器运行并显示与Arora类似的常规网站这一最初目标方面是这样。其中一个原因是已经存在一个用于FreeBSD的qtwebengine端口,它解决了大多数可能的libc兼容性问题,因此成为Genode端口的基础。不过,代码中仍然有一些操作系统细节需要处理,比如使用mmap支持共享内存,或者对JIT编译的JavaScript代码进行缓存维护。诸如多媒体支持等高级web引擎功能目前被禁用,一些与安全相关的功能,如服务器证书验证(需要libnss端口)、多处理或沙盒目前仍未使用。

构建系统问题最终可以通过为qmake添加Genode平台后端并将正确的编译器选项传递给Chromium构建系统来解决。

作为基于qtwebengine的web浏览器,我们选择了Falkon浏览器,它以前被称为QupZilla。尽管目前的性能和内存占用显然需要我们进行进一步的优化工作,但到目前为止,它看起来相当有希望:

对于9月份即将发布的a Sculpt OS,Falkon将以即用包的形式提供。

在当前版本中,我们完成了从Lock到Mutex和Block类型的代码库迁移,以改进诊断并为未来的性能优化扫清道路。

传统上,Genode的基库负责atexit处理程序的执行,因为这种机制被认为是C和C++的基础。具体地说,对于实例化为局部静态变量的对象,C++编译器自动生成对C++ABI的__cxa_atexit函数的调用,以便在程序退出时调用此类对象的析构函数。

随着过渡到Genode的现代组件API(在版本16.05中引入),这种机制对于普通的Genode组件变得无关紧要。但是对于依赖于C运行时的高级组件,特别是POSIX应用程序,该机制仍然至关重要。因此,我们在很长一段时间内都没有质疑Genode基础库中atexit机制的存在。

然而,我们最终意识到,必须将atexit功能从基库移到C运行时,才能完全遵循C运行时的执行模型。毕竟,Atexit处理程序可以执行I/O,例如关闭和同步文件。

此更改具有降低基础库复杂性的积极副作用。此外,它克服了以前静态标注的atexit处理程序数组的限制,这对于普通Genode组件是浪费的,但对于复杂的POSIX应用程序是不够的。

请注意,此更改故意使普通Genode组件(没有C运行时)的atexit机制无效。过去依赖genode_exit函数调用清理代码的Genode组件必须更改为显式调用清理代码。

此更改还通过ABORT删除了GENODE_EXIT(1)的隐式调用,该调用由未捕获的异常触发。依赖前一种行为进行组件监控的场景应该改为使用Genode的心跳监控机制。

作为另一个次要但仍然值得注意的细节,共享库析构函数不再通过atexit机制调用,而是由动态链接器显式调用。这稍微改变了析构函数的调用顺序:标记为析构函数的函数现在是在libc管理的atexit处理程序之后调用的。

现在删除了在上一版本中标记为不推荐使用的不安全的xml_node方法。

与上面提到的修改后的同步原语密切相关的是,我们删除了过时的Cancelable_lock API以及底层的取消-阻塞机制,它们现在已经过时了。在过去,这种机制被用来干净利落地关闭在销毁时被阻塞的线程。Genode的API设计从阻塞RPC转向异步工作组件,这使问题边缘化到不复存在的地步。

与取消阻塞机制类似,将RPC请求的分派延迟到显式调用rpc_entrypoint::activate的能力是每个RPC服务器都是多线程时遗留下来的API。现代的执行流程变得简单得多,消除了对此功能的需要。

不出所料,VFS是C运行时不可或缺的一部分,因为UNIX哲学中的“一切都是文件”。Libc VFS的基础是VFS库及其通用插件概念。因为LIBC(和库的其他用户)可以使用多个线程,这可以使用VFS、数据结构的完整性,例如。那些更复杂的插件,如VFS_LWIP中的网络堆栈,必须防止并发访问。从一开始,我们就计划将访问的序列化从插件移到使用VFS库的代码中,但未能通过libc执行模型完全实现这一目标。使用Genode 20.08,我们将libc的较低部分转换为执行监视器,以确保期望的VFS操作的序列化处理。这为在即将发布的版本中删除所有VFS插件中的同步措施铺平了道路。

VFS管道插件是在Genode 19.11中引入的,并已成熟为旧的libc_PipePlugin库的合适替代品。现在,我们通过从Genode中删除旧的libc插件并调整所有运行脚本(最突出的是针对Qt5示例的脚本)来完成此过程。用户可以迁移到VFS插件,但需要对其组件配置进行以下更改。

Libc必须配置为通过PIPE=";...";配置属性从VFS路径访问管道机制,如下所示。

到目前为止,pthread在创建时没有亲和力配置,并且只在组件可用的第一个CPU上隐式运行。在此版本中,我们向C运行时添加了放置策略和配置选项,以支持调优libc组件的pthread的CPU放置。默认的放置策略是All-CPU,这意味着将pthread分配给可用的CPU。循环赛。

可以使用新的<;pthread_place=";Manual";>;libc配置子节点根据pthread在CPU上的ID手动放置它们,如下所示。

<;config>;<;libc...>;<;p线程放置=";手册&34;>;。<;线程id=";0";CPU=";0";/>;<;!--pthread.0放在CPU 0-->;上。<;线程id=";1";CPU=";1";/&>;<;!--pthread.1放在CPU 1上-->;。<;线程id=";2";CPU=";2";/&>;<;!--pthread.2放在CPU 2上-->;。<;线程ID=";3";CPU。

.