FreeBSD不应采用Launchd(2015)

2020-11-07 18:22:50

首先,由于这些讨论的本质,我想补充一条免责声明,即本文并不属于Luddiism(正如您将看到的那样,恰恰相反),更重要的是,我不反对在FreeBSD的衍生产品(如FreeNAS或PC-BSD)中集成Mach/libSystem/Launchd。这完全是关于FreeBSD的,也是为什么我对iXSystems领导的NextBSD项目的目标持怀疑态度的原因,该项目正在推动OS X化的努力。

我关注NextBSD已经有一段时间了,当时它刚开始是Open Launchd,这是R.Tyler在2005年作为GSOC学生时发起的一项努力,意在将启动的系统和服务管理器移植到FreeBSD上,这一点并不令人意外。它被搁置了很长一段时间,直到2013年底复兴,但再次进展非常缓慢。

2014年11月左右,在MeetBSD大会上,Jordan Hubbard发表了题为“FreeBSD:未来10年”的演讲,概述了对FreeBSD更“事件驱动”和统一配置方法的普遍渴望,强烈暗示了将Launchd用作系统引导和服务守护进程,以及OS X低级用户空间的其他部分。

当时,很多人(包括我自己)认为这更多的是一种假设,而不是真正的持续努力。他们当然很低调。然而,随着我对GNU Hurd的研究兴趣与日俱增,我很快就想起了Open Launchd的努力,并向Kip Macy询问了FreeBSD的Mach IPC端口的状态。

事实证明,他不仅移植了OSF Mach的部分实现,而且还移植了Apple System Logger、xPC、libdispatch、libtify、libaudit和带有JSON解析器的Launchd。

在这一点上,我意识到事情变成了现实。不久之后,该项目在2015年8月的旧金山湾区FreeBSD用户小组会议上正式揭幕,乔丹·哈伯德和基普·梅西发表了演讲。

这就引出了这篇文章,以及为什么我认为这是被误导的。虽然实验没有什么错,但我会尝试说服读者为什么Launchd和Mach不适合FreeBSD。

它的要点是,启动架构是如此特定于OS X的特定用例,试图将其逐字移植到另一个系统会导致概念上的不匹配。

在OS X环境中,Launchd是Mach Bootstrap服务器,管理名称-端口绑定的名称空间,用于在Mach服务之间进行发现。由于Mach端口命名空间是任务内部的,因此选择了一个中央仲裁器来处理查找和服务注册。

非常重要的一点是要注意到,这绝不是必须的。例如,GNU Hurd没有相当于引导服务器的功能。相反,每个进程都被分配了一个引导端口,用于与启动它的服务器库会合。因此,引导是一项分散的活动,但NextBSD开发人员在很大程度上考虑的是Mach语义,关于OS X如何具体实现它们,而不是一般的,并继续推断和推断特定的体系结构,就好像它是法定的一样。

Launchd可能是第一个普及“套接字激活”这一时髦概念的系统,这实际上是对FD传递和inetd风格的服务启动这一古老观念的重塑。它不是像UCSPI那样的通用方案,而是一种临时集中管理的协议,如果完全利用该协议,会鼓励守护程序编写者链接到liblaunch,从而造成不受欢迎的锁定。

然而,Launchd的核心污点是它对服务分类的绝对混乱的语义。Launchd旨在启动和监控守护进程,即所谓的代理(实际上是按用户/会话守护进程)、Mach服务和xPC服务。一开始,后两者就是特定于OS X的,并绑定到IPC机制,这是一种不一致的服务分类方式。然后,代理可以是四种“会话类型”之一:预登录、按用户、图形用户界面会话和非图形用户界面会话。请注意,图形用户界面和登录前会话类型的概念是如何在考虑到Aqua界面和LoginWindow应用程序(特定于OS X的显示管理器)的情况下清晰地设计出来的。它们无关紧要,而且在其他平台上存在阻抗不匹配问题。因此,非GUI会话代理的概念与Launchd如何从其plist启动sshd紧密相连,而plist是一个专门用于整个代理类型的特别有限的领域。

更复杂的是实际的流程类型,如Launchd.plist(5)中所述。在这里,甚至像system d这样的东西都将它们视为应该执行哪些启动例程的信息,在这里,它们与应该应用什么调度策略和资源限制紧密相关!有四种类型,名称含糊的背景、标准、自适应和交互。背景很简单,这是一个由Launchd…守护的常规服务。但具有自动资源限制。标准是隐式默认设置。使用xPC在后台和交互式之间进行自适应切换。因此,它是一种与使用IPC机制的特定方式相耦合的进程类型。令人困惑的是,互动是没有任何资源限制的。你可能会对这个泥潭感到困惑。嗯,对于需要良好的优先级调度和资源统计的桌面来说,这确实是很有意义的,这样即使在系统事件排队时,应用程序也可以保持响应。但是对于服务器和嵌入式系统来说,FreeBSD的主要用途是什么呢?这绝对是无稽之谈。这种行为甚至也不能很好地观察到。

此外,Launchd在很大程度上仅限于一个创业纪律:懒惰地按需加载服务。现在,例如,类似daemontools的东西是基于“让它崩溃”的哲学,即不断地重新启动服务,直到它们的依赖关系得到满足。由于其低开销和多功能性,它可以用作一次启动一切的标准渴望服务管理器,但也可以通过使用简单的套接字工具包接口(类似于inetd,但模块化程度更高)(如UCSPI)来使其变得懒惰。System d也可以以懒惰和急切的方式使用,通过其复杂的依赖系统实现这一点。

不过,Launchd并不能很好地应对这种渴望。它没有正式的依赖或排序系统,也不太符合“让它崩溃”的范例。事实上,引用《创建启动守护程序和代理》,它一般不能很好地处理非启动守护程序:

如果您的守护程序依赖于未启动的守护程序,则在启动守护程序时该未启动的守护程序尚未启动时,您必须格外小心,以确保守护程序正常工作。要做到这一点,最好的方法是在启动时包含一个循环,该循环检查未启动的守护进程是否正在运行,如果没有运行,则在再次检查之前休眠几秒钟。

确保在此循环之前为SIGTERM设置处理程序,以确保在您所依赖的守护进程永远不可用时能够正确关闭。

为了帮助解决这个问题,Launchd以KeepAlive密钥的形式提供了一个黑客攻击,将服务的正常运行时间与特定的资源条件绑定在一起。它几乎所有的选择都受到内在竞争条件的影响。还有NetworkState,它绑定到正在运行的网络接口,在这里被定义为“至少有一个非环回接口处于运行状态,并且为其分配了IPv4或IPv6地址”,而PathState用于只要给定路径上的inode存在,就保持活动状态,并具有文件系统事件通知所带来的所有快捷性。但是,最可笑的是,还有其他启用的工作。没错。您可以将一个作业耦合到由其标签启用的另一个作业。一个穷人的依赖体系,已经到了可以滥用的时候,完全颠覆了Launchd存在的整个假想。

当然,最重要的是,使用名为LaunchOnlyOnce的键(而不是某种类型,这更有意义)的一次性作业、StartCalendarInterval的类似crond的功能(据报道不能很好地与实际的crond配合使用)、更模糊的资源平衡键(如LimitLoadTo和LowPriorityIO),以及首先通过WaitForDebugger向调试器屈服的能力(这是否意味着Launchd中断ptrace(2。我希望不会。)。

我们不要忽视,所有这些服务状态都与全局系统状态相关联,而不是被隔离到具有不同职责的不同进程中。

顺便说一下。我描述的一切?所有的数据都在PID1中。这只是一个可靠性问题,不管你怎么切分它。它可能适用于桌面系统,但一点也不通用。

另一个可靠性问题是配置解析也是在PID1中完成的。随着苹果公司的推出,其格式是可扩展标记语言(XML)。随着NextBSD的推出,它变得更理智了。相反,他们选择了JSON。但总的来说,解析可能应该在PID1的临界基之外处理,因为解析器长期以来一直是臭名昭著的错误来源,尤其擅长输入模糊。

归根结底,推出的架构是一堆分层违规、语义不一致和奇怪的模棱两可的界面,这使得它真的很难使用。这源于这样一个事实,即它试图成为整个该死系统上几乎每一个CPU时间单位的管理者。显然,它是一个针对OS X的限制而高度定制的工具。它主要是为面向开发人员和塑造桌面体验而设计的,而不是系统管理员会大量使用的东西。这在OS X上没问题。它很笨拙,对FreeBSD或其他很多东西都不好。

乔丹·哈伯德(Jordan Hubbard)声称,在Mach和Unix概念之间并没有那么大的阻抗不匹配,但我要反驳这一点。

我们希望更进步,愿意尝试新事物。如果有必要,我们不怕改变启动系统,也不怕把Unix拖入21世纪。

他们将把unix的喧哗和尖叫拖入21世纪的…。通过将80年代中期的第一代研究微内核集成到其臭名昭著的IPC中。

现在,请记住,我不是在抨击马赫本身。GNU Hurd在GNU Mach上做得很好,多年来甚至对其进行了一些有趣的重构和更新。我相信OS X在XNU中也能很好地使用Mach(它的IPC被广泛使用)。

然而,…在2015年有意识地采用了它的界面。这是一个令人费解的时代错误,特别是对于一个如此自豪于其未来主义和远见的项目。当然,现在他们填补的MkLinux上的OSF Mach接口主要只是获得OS X系统的一种手段,但他们也试图以其自身的优点来证明它的合理性。

现在,有了这样的决定,最好是拥有一个Userland Mach服务器。然而,这是不切实际的,因为Mach语义实际上很难确定为单片Unix。即使作为内核模块,它们也不支持内存对象,因为这些对象是由来自外部分页程序(即用户空间页面错误处理程序)的存储支持的。铁板一块的工会从来没有好的机制来实现这一点。一种古老的黑客攻击是陷害SIGSEGV(例如,使用GNU libsigSegv),但这是非常有限的。受KVM工作的启发,有人提出了一种适用于Linux的userfaultfd(2)机制,但该机制尚未合并。

Mach强制执行非常严格的任务-线程二分法,即虚拟地址空间和在其下运行的CPU时间单位之间的二分法。它们结合在一起就形成了一个过程。在FreeBSD的例子中,他们不得不以一种丑陋的方式来简化它,假定任务=进程和线程=kthread,这违反了正确的Mach语义。顺便说一句,在Linux上也可能需要这样做。

Mach VM系统似乎移植得很好。Mach的vm_read()和vm_write()函数通过映射到离散任务地址空间和从离散任务地址空间映射来工作,因此这可能是促使它成为内核模块的原因。这仍然是一个可靠性问题。

NextBSD幻灯片称赞Mach IPC促进了不是文件系统的独立服务命名空间,好像这是一个巨大的缺点。使用Plan 9和Inferno告诉我,具有每个进程名称空间的9p比整数描述符表(这就是端口名称空间)要优雅和通用得多。

他们提到了一个“预先存在的定义良好的RPC接口”,我猜想这个接口是指MIG。MiG是一个编译器,它将RPC定义从一种名为Matchaker的语言转换成用于打包Mach消息的精心设计的C代码。它使用起来不是特别舒服,主要是Mach IPC使用起来费力的副作用(这就是苹果公司做出XPC的原因),而且据我所知,它从未被用来实现位置透明通信。

归根结底,这就是问题所在。NextBSD的努力几乎没有考虑过解决问题。他们只是拥有与苹果有关联或曾经与苹果联系在一起的人,被OS X作为一种技术迷住了,只是照例移植OS X基础设施,就好像它是一个不需要正当理由的内在正确的法令一样。我们想要动力系统,所以我们当然会把船运到港口。我们想要更好的索引日志记录,所以我们当然会移植ASL。嘿,所有这些都需要Mach和XPC,所以让我们将部分OSF Mach作为内核模块放在同一个地址空间中。

最大的问题是:我为什么不直接使用OS X呢?我想不出任何理由。NextBSD目前的努力并不打算对OS X系统做任何有趣的事情,而是直接按照上游计划使用它们,因此它们将永远追赶达尔文。如果我想直接使用达尔文,我会运行类似PureDarwin的程序。如果我想要一整套,我只需要使用OSX--它已经可以和Unix(实际上是一个授权的Unix)进行合理的互操作了。NextBSD是关于“必须有所作为”这一古老笑话的深刻例证。这是一件事,所以我们必须去做。“。

一种是使用nosh,这是由jonathan de boyne pollard开发的高度灵活和模块化的系统和服务管理工具包,它实现了许多类似system和Launchd的功能,但其架构却与它们截然相反。它在FreeBSD上运行,大多数rc.d脚本甚至已经移植。

二是效仿蜻蜓BSD。Matthew Dillon为它编写了一个小型但功能强大的服务管理器svc(8),它使用监狱进行服务跟踪和隔离,并使用proctl(2)系统调用在没有初始化的情况下成为收割机(类似于Linux>;=3.4的prctl(2)中的PR_SET_CHILD_SUBREAPER标志)。虽然在其他方面还不够完善,但我们的目标是编写一个简单但完整且多功能的服务管理器,利用本地的FreeBSD特性。

第三个是什么都不做,坚持使用rc.d。我不会进一步评论这一点,这是相当不言而喻的。

我已经发现了Launchd架构的结构性技术问题,Mach的概念如何不能很好地映射到单片Unix上,以及作为二手OS X而不是利用本地功能或在现有结构上构建而不是粗俗地逐字移植外国系统软件的普遍愚蠢之处。