抢购和扁平包装-社区的悲剧

经纬仪警告:您已被警告,很多字母。


长期以来,它一直在为应用程序开发一种分发格式,这些应用程序不受系统范围的依赖。 Ubuntu非常非常积极地推广其快照,gnome-flatpack。 两者都保证了天堂和不受rpm / deb影响的自由。 让我们考虑一下他们想解决的问题,以及他们为解决这个问题而要价。


图书馆


在当今世界,没有人可以在不使用他人代码的情况下编写应用程序。 有以下几个原因:


  • 许多库非常严重,以至于从头开始编写其功能是一项艰巨的任务。 示例-支持Unicode,字体渲染,数学。
  • 其他库提供了相当适度的功能集,但是它们写得很好,以至于至少写得这么好几乎是不可能的。 编程语言的标准库,libc的各种实现等。
  • 使用其他人的代码(本节专门讨论)的成本通常低于维护您的代码的成本。 “每行代码的错误”的密度可能是可比的,您必须自己捕获自己的错误。 外国(受欢迎的)库很可能会被错误的人调试和纠正。

关键在于,即使我们可以根据原理自己编写单个库的功能,所需功能(和依赖项)的总数也会使需要解决的任务数量几乎成倍增长,从而推迟了编写程序本身代码的开始时间进入无法达到的距离。


一个实现话剧规模的示例:假设您的应用程序将两行输入作为可选参数,并在归一化后将它们一起显示。 如果要编写工业应用程序(看起来像“真实”应用程序的应用程序),则:


  • 您需要命令行解析器
  • 哪个应该接受unicode
  • 也许给用户一个暗示,他已经在参数名称中加盖了
  • 什么需要语音比较
  • 也许是正则表达式
  • 通常,您不仅必须支持Unicode,还必须支持其他语言环境,这需要一个语言环境支持库以及人们在语言环境中想到的所有内容
  • 具有规范化的字符串连接是另一个Unicode库的另一种用法;您自己不会实现此功能。
  • 屏幕上的输出(命令行上的帮助,您的结果)可能需要支持ncurses-支持不同终端的库(您可以使用文本模式,但是应用程序通常使用颜色功能)。
  • 测试涉及使用测试框架,可能是moks库。

显然,“两行”任务的这种复杂性是粗略的过度设计,但是一旦您开始做更多的事情,“自己做的一切”的想法就开始超越可观察和实现的范围。


您认为需要多少个库才能保证curl http(s):// ...将起作用? 很多 您将使用一个,但是您的依赖关系就是您的依赖关系。


复制粘贴和供应VS动态链接


虽然不可避免要使用库,但使用本身在实现上可能会有所不同。 请注意,我们有两个重要的词:“使用”和“使用的实现”。 使用是什么意思? 最粗鲁的形式-必要时调用库代码的能力。 这是此的实现:


  • 我们可以复制执行所需操作的代码。 以一段代码(复制和粘贴)的形式,作为一种编程语言的单独模块(对于编译语言的目标文件),或者作为一个单独的模块(对于解释语言)。 它旁边的某个位置是“使用应用程序将库的源文件复制到您的目录中”。 这会带来什么问题? 主要的主要问题是我们(永远)失去了与原著的联系。 即使原始库的作者纠正了错误,我们也不会知道。 而且,如果我们只是复制代码,那么下一个在程序上工作的人甚至无法发现该代码是“异类”。 实际上,我们切断了“从头开始写”问题的路径,并采用了别人的方法。 但是,我们仅削减了一部分,因为如果此代码中有错误(但它们不会在那里 ,则存在 ),那么它们的更正将需要更正一个,才能从根本上理解问题的实质。 即使试验需要阅读数十万行源代码和数百个RFC(以及有关实现与RFC不同的注释),我们也别无选择。 此处的主要错误是我们丢失了该代码是外来代码的信息。 在文件中添加注释可能会有所帮助,但是需要一个人的积极而深入的参与,因为如果我们写注释“取自libfoobar,src / lib / foo.c版本364a51577f3782dbf8e5d2482ab941980357c492”,那么有人将需要查看libfoobar的位置,它是什么版本,以及与先前版本相比有什么变化。“为了简化此过程,我们需要机器可读的元信息。
  • 如果我们将“别人的代码”与元信息一起使用,并使用程序来管理此代码(而不是复制粘贴),则这称为自动售货 ,即 控制在您的代码中包含其他人的代码。 从技术上讲,自动售货可以发生在源文本,将对象链接到可执行文件,从应用程序导入模块(在解释器中)或什至与库的“您”版本进行动态链接的阶段(稍后会对此进行更多介绍)。
  • 最后,我们可以在应用程序启动阶段执行动态链接。 对于编译语言,这些是普通的“常识”;对于解释语言,在系统范围的导入中有一个模块。 如果几个应用程序可以导入它,则这是一个共享库。 如果应用程序“带来了它的模块”,则该库是“自己的”,即使其接口暗示“共享库”也是如此。 例如,如果某个应用使用了so的“自己的”版本,则无论它是否与一般版本都不同,那么这就是自动售货。 如果导入了系统,则这是一个共享库。

这些方法有什么区别? 我将简要介绍一些论点;它们在许多文章中已经讨论了很多次。 尽管存在相邻的反论点,但每个论点仍然有效:


  • 节省内存(RAM和磁盘),以减小安装的系统的大小。 使用相同功能的应用越多,节省的内存就越大。 因此,相反,应用程序带来的“您的”库越多,它的“脂肪”就越多。
  • 关于谁监视漏洞的争论是系统(提供库更新)还是应用程序的作者(按时更新)。
  • 解决依存关系冲突(自动售货机解决了这个问题,因为共享库需要过程中所有参与者的关注和准确性,有时会造成无法克服的困难),这就是传说中的dll地狱。
  • 库的新版本-根据应用程序作者的要求出现,也可以由发行版本的作者决定。 在一种情况下,作者可以带来他需要的新功能,在另一种情况下,发行版可以通过支持库中的新功能来对现有应用程序进行改进(例如,hidpi屏幕在所有动态链接到qt / gtk库的应用程序中开始正常工作) 。

所有这些问题以前已经处理了很多次。 相反,我想着重于“所有矿山”和“所有普通”分水岭的社会方面。


社会契约与权力维护者


共享图书馆是合作,力量和责任。 确定操作系统中可用的共享库的人员向软件制造商指示他们可以使用哪些共享库。 许多软件可以使用不同的库,链接器(对于编译语言)或依赖文件处理程序(pip,bundler等)可以自行决定使用哪个确切版本。 如果分发中的所有应用程序都以相同的要求进行编译,那么就会出现宽限期:如果某个库中有错误,则该库的维护者将更新版本,并且此修复程序将自动应用于所有应用程序。 即使该应用程序每两年发布一次,有条件的openssl中的修复程序也将在一周内应用。 如果在特定的OS中决定放弃旧协议,进行某些修改(例如,用户界面),那么这些更改也将适用于所有人。 外观(或也许可以由用户一劳永逸地更改)的一般风格。 这不是恩典吗?


权力及其斗争


...此宽限期要求所有应用程序都可以使用库的选定版本。 但是,如果某些应用程序希望从库中获得非常非常新的功能,而所有其他应用程序都不想使用它,那是因为例如,这不是库的LTS版本,即 它不够稳定吗? 但是分发工具包可能会拒绝“脱离原理”切换到新版本,因为我们向用户保证仅会修复错误,并且仅在下一个版本的操作系统中(例如)将在半年内发布新版本。 这引起了应用作者的抵制。 您要告诉谁我应该使用哪个版本? 我是作者,我知道了。 我需要的是libfoobar 3.14-pre2或更早版本,而不是您古老的枯燥的libfoobar 3.10。


...此时,作者仅在应用程序需求中libfoobar>=3.14-pre2 。 维护者获取并修补请求,并删除依赖于此库的代码。 也许吧 或者只是拒绝接受具有这种依赖关系的新版本,直到该依赖关系(libfoobar 3.16)处于发行版的新版本中。


如果作者确实需要用户使用新版本(例如,由于作者不希望支持旧版本),那么他将寻找解决方法以将应用程序发送给用户。


当有多个发行版,一些较新,一些较旧时,也会发生相同的情况。 维护较旧的发行版,使用不同的库进行测试非常困难。 因此,“随您的图书馆运送”选项几乎立即出现。


社区悲剧


这为出现社区悲剧创造了先决条件:


  • 每个制造商(软件作者)都希望按需运输。 适应他人的规则(版本)是浪费时间和精力,尤其是因为世界上有许多不同的发行版
  • 用户想要新版本。

同时,带有库的应用程序越多,对系统库的使用就越少。 还记得恩典吗? “通用”越少,恩典就越少。 如果一个共享库被995个其他应用程序中的5个不同的应用程序使用,则此库的收益为0.5%。 太可惜了,是的。 而且,这伤害了所有用户,即使是那些原则上不急需新功能的用户-但是如果该应用程序仅以自动售货形式提供,那么用户将没有选择权。


事实证明,我们有一个全球性的极端:所有应用程序仅使用共享库(最大的通用宽限度,给单个应用程序的作者带来不便)或“每个应用程序本身”(大量分布的应用程序,可能未发现但广泛使用的漏洞,吃了很多内存,但是每个应用程序的作者都很方便)。


这就是我们遇到的rpm / deb VS snap / flatpack之争的地方


自由还是奴隶制?


Ubuntu非常非常坚决地倡导snap'y。 GNOME相信未来将会出现扁平包装。 它们每个都是用于深度个人主义应用程序的框架。 各种各样的电子,不仅与发动机舱罩相连,还与发动机舱操作系统相连。 拥有libc,拥有libssl,拥有regexp,拥有ncurses等。 只有核心是共同的,即 实际上,这是相同的容器化应用程序,只是用于桌面。 赋予每个核心自己的核心,即可获得虚拟机形式的设备。 添加元数据-您将获得一个Docker容器。


应用程序(应用程序作者)的个人主义是可以理解的,但是谁代表着共同的利益呢? 局部的重大改进被总体分布的轻微下降乘以纯应用所抵消。 如果每个人都为自己进行本地改进,那么损害的金额将大于改进金额的收益。


似乎在这个地方发行的创建者应该充当共同利益的保管人。 但是...


政治学


Ubuntu对Debian的依赖远超过Canonical(Ubuntu公司)想要的。 Ubuntu的价值不在于Ubuntu维护者的努力,而在于Debian所提供的庞大软件存储库中,这种形式通过所有拥有Debian发行版的独立软件包的数千名维护者的努力,使所有应用程序都能很好地协同工作。 Canonical在此基础上进一步努力完善结果-为此,它受到了一些人的喜爱。 添加少许营销和固定的生命周期,这是企业所喜欢的,我们得到了不错的产品。


...这取决于周围数千名志愿者的意愿。


这几乎不适合任何商业公司。 如何打破这种瘾? 制作自己的应用程序包是正确的。 应用程序越多,上游的上行空间将对公司造成的伤害越小。 当Debian在systemd上进行的投票掩盖了Canonical开发的新贵时,就足以使人回想起这个故事了。


但是要维护成千上万的应用程序,其中一些是它们自己的空间(erlang,go,perl,python,R,julia等),而某些是相应主题领域中的怪物(浏览器,emacs,tex,心脏起搏器等)-这些是繁重的工作。 难怪这些是成千上万的维护者。


...有一个主意。 并且,让应用程序作者自己维护应用程序。 让我们给每个人一个沙盒,让他们挖掘。 作者可以自由使用Canonical-不依赖Debian且至少有人免费维护的应用程序。 用户得到...


...繁重的应用程序,它们的更新是不定期的,并且可以轻松地使漏洞在数年内得到纠正...但是其中一些是崭新的。


那接下来呢?


想象一下一个世界,每个人都随身携带所有东西……您知道它的外观吗? 看看chefsdk。 他随身携带自己的postgresql(及其依赖项),rabbitmq(取决于erlang),再加上厨师服务器也基于erlang,因此他也有自己的erlang。 突然,我们在同一个应用程序中有两个erlang和同一个库的许多副本,版本略有不同。 这不是最终的选择,因为 在内部,组件之间仍然存在公共库。 如果进一步削减它们,我们将为一个应用程序获得几十个openssl和libc副本。 即使不是最终形式,每个应用程序看起来也只有600MB。


...当然比一般的电子应用要大得多...并且比整个mariadb服务器(整个DBMS!)或krita或gimp(巨大的图形应用程序)多12倍。


如果每个人都会那样? 我的计算机上安装了2000个软件包(不包括-dev和lib)... 2000 * 300 = 600GB(对于结果的平均大小,我从chefsdk中拿走了一半,因为并不是每个人都因依赖而如此糟糕)。 现在它们占据了大约7GB(包括资源,例如文档,纹理编辑器,CAD模板等)。


如果变成600GB,那不是纯粹的社区悲剧吗? 在每时每刻,我们都会观察到局部优化(以及对其他人带来的不便的解决方案),但是,这些局部优化的总和降低了系统的整体最优性。 我认为,这不仅仅是每个参与者的本地收获。


我理解为什么Canonical会按部就班。 我理解这一点,不同意。

Source: https://habr.com/ru/post/zh-CN433052/


All Articles