我在大学时使用过一点 Linux,并且熟悉这些术语。我经常使用 .NET 语言进行开发,所以我不是计算机盲。
也就是说,我真的不能说我理解 *nix 圈子中存在的“自己编译”[CIY] 的心态。我知道它会消失,但仍然时不时地听到它。作为一名开发人员,我知道设置编译器和必要的依赖项是一件很痛苦的事情,所以我觉得 CIY 工作流程有助于使 *nix 变得更难访问。
哪些社会或技术因素导致了 CIY 心态的兴起?
答案1
很简单,在 *nix 的大部分历史中,没有其他选择。程序以源代码 tarball 的形式分发,使用它们的唯一方法是从源代码编译。因此,与其说这是一种心态,不如说这是一种必要之恶。
也就是说,有充分的理由自己编译东西,因为它们将专门针对您的硬件进行编译,您可以选择启用或不启用哪些选项,因此您最终可以得到一个经过微调的可执行文件,就像您喜欢的那样。然而,这显然只对专家用户有意义,而不是对那些只想要一台工作机器来阅读电子邮件的人来说。
现在,在 Linux 世界中,主要发行版在很多年前就已经放弃了这一点。现在你很少需要自己编译任何东西,除非你使用的是专门为喜欢这样做的人设计的发行版,比如 Gentoo。然而,对于绝大多数发行版,普通用户永远不需要编译任何内容,因为他们所需的几乎所有内容都存在并在其发行版的存储库中编译。
所以你所说的这种 CIY 心态基本上已经消失了。它很可能在 UNIX 世界中仍然存在,我在那里没有经验,但是在 Linux 中,如果您使用具有像样的存储库的流行发行版,您几乎不需要自己编译任何东西。
答案2
造成这种心态的原因有很多,包括最终用户、发行版维护者和代码供应商/开发人员/项目组,每一个原因都是完全有效的。
开源方面
有些人喜欢知道他们正在使用自由软件,并通过选择从源代码编译来验证这一点。这就是 Linux From Scratch 项目/howto/guide/book 等内容的用武之地。
优化和选项方面
想要针对您的特定 CPU 架构编译具有特定优化的内容吗?也许有一个编译时选项(或创建一个补丁)来启用或禁用您需要的特定功能。这方面的例子可以是修补 postfix 以使其能够管理配额,或者使用像 Gentoo 这样的发行版,您可以选择不使用 systemd,或者由于许可问题您专门选择支持 ogg/theora/vorbis/whatever 而不是 mp3管他呢。
CPU架构方面
您的工作场所是否使用高端非 x86/amd64 机器?您需要/想要的软件包可能无法针对您的 CPU 架构进行预编译,更不用说您正在运行的任何发行版了。当然,大多数运行此类硬件的地方也得到 IBM 等的支持,并且不会随意安装/编译东西。但是,如果您从剩余销售中购买一台,挖出一台带 PPC 处理器的旧 iMac 等,该怎么办?
分布方面
发行版“家族”——即带有 Ubuntu、Mint 等的 Debian 和带有 CentOS、Whitebox、Fedora 等的 RedHat——都使用不同的软件包格式。每个版本都附带不同的库版本等。即使对于简单的单文件 shell 脚本,设置正确的 Debian .deb 文件也需要时间和精力。如果您编写了一些软件来解决一些问题,并且希望将其免费并将其发布到 gitlab、您自己的网络服务器等上,那么您是否愿意只发布一个带有构建说明的通用 .tar.gz 源文件,或者您愿意将 2 个版本的 Debian(稳定版和测试版,也许是旧稳定版)、多个版本的 Redhat 和 Fedora 打包为 RPM、Slackware 的 TGZ、Gentoo 的 ebuild 配置文件等。
答案3
正如@terdon 所说,现在编译东西的需求非常小,特别是对于家庭用户而言。
过去,在 Unix 世界中,我高度依赖编译源,例如,当我管理 Solaris、AIX、Ultrix、Digital Ultrix 和 HP/UX 系统时,这些系统有时不再由供应商维护,或者实现通用服务的数量远远落后于其他 Unix 系统(包括 Linux)常用的服务。
目前仍然存在真正需要编译的东西,要么是为了获得存储库中没有的一些更晦涩或过时的软件,要么是使用您没有兼容的二进制文件的更新版本的软件包,或者当如果您能够为其编写补丁或模块,那么您想添加额外的功能或很少添加功能。
在重新设计系统以移植到 Debian 和/或新版本的 Debian(其框架不再受操作系统支持)时,我还必须手动编译软件。
例如,过去我必须手动编译 DHCP 守护程序才能支持(当时最近的)Windows 对协议的更改,或者支持电信领域配置的特定补丁。
我仍然在本地存储库中保留自己从 dev git 存储库编译的 FreeRadius 版本的 debs,因为 Debian 中存在一系列稳定版本存在(严重)错误,并且通常 Debian/Ubuntu 的相应 .debs 尚未发布足以满足我们的需要。
不言而喻,有时我们还必须运行/或编译我们自己编写的东西。
现在安装依赖项并不像过去那么困难,有些软件甚至为一些常见的 Linux 发行版定制了规则文件,命名要编译的依赖项,并完成创建内置依赖项列表的包文件的繁重工作。从本地存储库安装这样的包与从官方存储库安装相同的包没有太大区别。
答案4
有哪些社会或技术因素导致了上涨CIY心态?
根本原因很明显是技术原因: 二进制可移植性比源代码可移植性更难。在发行版软件包之外,大多数免费软件仍然仅以源代码形式提供,因为这对于作者/维护者来说要方便得多。
在 Linux 发行版开始打包普通人想要使用的大多数东西之前,您唯一的选择是获取源代码并为您自己的系统编译它。商业 Unix 供应商通常不包括几乎每个人都想要的东西(例如像 GNUbash
或类似的漂亮 shell),只是他们自己的sh
和/或 的实现csh
,所以如果你(作为系统管理员)想要,你需要自己构建东西为用户提供一个良好的 Unix 环境以供交互使用。
现在的情况是,大多数人是坐在桌面上的机器的唯一管理员和唯一用户,这与传统的 Unix 模型有很大不同。 系统管理员负责维护中央系统和每个人桌面上的软件。 (通常是让人们的工作站仅通过 NFS 安装/opt
并/usr/local/
来自中央服务器,并在那里安装东西。)
在 .NET 和 Java 出现之前,跨不同 CPU 架构的真正二进制可移植性是不可能的。出于这个原因,Unix 文化的发展将源代码可移植性作为默认设置,甚至很少尝试启用二进制可移植性,直到最近 Linux 做出了像 LSB 这样的努力。例如,POSIX(这主要 Unix 标准)仅尝试标准化源代码可移植性,即使在最近的版本中也是如此。
相关文化因素:早期商业 AT&T Unix 带有源代码(在磁带上)。你没有有从源代码构建系统,它只是为了以防万一您想看看某些东西如何真的当文档不够时起作用。
“Unix 的广泛在线文档和(多年来)随时访问所有系统源代码的政策提高了程序员的期望,并为 1983 年自由软件运动的发起做出了贡献。”
我不确定这个决定的动机是什么,因为如今让客户访问商业软件的源代码是闻所未闻的。在这个方向上显然存在一些早期的文化偏见,但也许这源于 Unix 作为一个主要用 C(而不是汇编语言)编写的可移植操作系统的根源,可以为不同的硬件进行编译。我认为许多早期的操作系统更多地用 asm 编写针对特定 CPU 的代码,因此源代码级可移植性是早期 Unix 的优势之一。 (我对此可能是错的;我不是早期 Unix 的专家,但 Unix 和 C 是相关的。)
迄今为止,以源代码形式分发软件是让人们将其适应他们希望其运行的任何系统的最简单方法。 (最终用户或将其打包为 Linux 发行版的人)。如果软件已经由发行版打包/用于发行版,则最终用户可以直接使用它。
但期望大多数软件包的作者自己为每个可能的系统制作二进制文件就太过分了。一些主要项目为一些常见情况提供二进制文件(特别是 x86/windows,其中操作系统不附带构建环境,并且操作系统供应商非常重视仅二进制安装程序的分发)。
让一款软件在与作者使用的系统不同的系统上运行甚至可能需要一些小的更改,这对于源代码来说很容易。有人为了满足自己的痒痒而编写的一次性小程序可能从未在大多数不起眼的系统上进行过测试。有了源代码就可以做出这样的改变。原作者可能忽略了一些东西,或者故意编写了可移植性较差的代码,因为这样可以节省大量时间。甚至像这样的主要软件包信息ZIP并没有立即在每个平台上都有测试人员,并且需要人们在发现问题时发送可移植性补丁。
(还有其他类型的源代码级可移植性问题,这些问题仅由于构建环境的差异而发生,与此处的问题并不真正相关。使用 Java 风格的二进制可移植性,自动工具(autoconf
/ auto-make
) 和类似的东西cmake
是不需要的。我们不会有类似的事情有些系统需要包含<netinet/in.h>
而不是<arpa/inet.h>
为了ntohl(3)
。 (也许我们ntohl()
一开始就没有任何其他字节顺序的东西!)
我经常使用 .NET 语言进行开发,所以我不是计算机盲。
一次编译、随处运行是 .NET 和 Java 的主要目标之一,所以可以公平地说整个语言的发明就是为了解决这个问题,而您的开发经验是其中之一。使用 .NET,您的二进制文件可以运行在便携式运行时环境 (CLR)。 Java 将其运行时环境称为Java虚拟机。您只需要分发一个可以在任何系统(至少是已经实现了 JVM 或 CLR 的任何系统)上运行的二进制文件。当然,您仍然可能会遇到可移植性问题,例如路径分隔/
符\
、如何打印或 GUI 布局细节。
许多软件都是用以下语言编写的完全编译为本机代码。没有.net
Java 字节码,只有运行它的 CPU 的本机机器代码,以不可移植的可执行文件格式存储。 C 和 C++ 是主要的例子,尤其是在 Unix 世界中。显然这意味着必须针对特定的 CPU 架构编译二进制文件。
库版本是另一个问题。库可以而且通常确实在更改二进制级 ABI 的同时保持源代码级 API 的稳定。 (看API 和 ABI 之间的区别。)例如,向不透明结构添加另一个成员struct
仍然会更改其大小,并且需要重新编译新库版本的标头,以便为此类结构分配空间的任何代码,无论是动态(malloc)、静态(全局)、或自动(堆栈上的本地)。
操作系统也很重要。对于相同的 CPU 架构,不同风格的 Unix 可能有不同的二进制文件格式、用于进行系统调用的不同 ABI 以及常量的不同数值,例如fopen(3)
的O_RDONLY
, O_APPEND
,O_TRUNC
。
请注意,即使是动态链接的二进制文件仍然具有一些在main()
.在 Windows 上,这是crt0
。 Unix 和 Linux 有相同的东西,其中一些 C 运行时启动代码静态链接到每个二进制文件中。我想理论上你可以设计一个系统,其中的代码也是动态链接的,并且是 libc 或动态链接器本身的一部分,但这并不是我所知道的任何操作系统上的实际工作方式。这只能解决系统调用 ABI 问题,而不能解决标准库函数常量的数值问题。 (通常系统调用是通过 libc 包装函数进行的:使用的普通 x86-64 Linux 二进制文件mmap()
不会包含该syscall
指令,仅call
包含对同名 libc 包装函数的指令。
这就是为什么不能在 i386-Linux 上运行 i386-FreeBSD 二进制文件的部分原因。 (有一段时间,Linux 内核有一个系统调用兼容层。我认为至少有一个 BSD 可以运行 Linux 二进制文件,具有类似的兼容层,但你当然需要 Linux 库。)
如果您想分发二进制文件,则需要为 CPU/OS-flavor+version/installed-library-versions 的每一种组合制作一个单独的二进制文件。
早在 80 年代/90 年代,Unix 系统常用的 CPU 类型有很多种(MIPS、SPARC、POWER、PA-RISC、m68k 等),还有许多不同风格的 Unix(IRIX、SunOS、 Solaris、AIX、HP-UX、BSD 等)。
那只是Unix系统。许多源代码包也可以编译并运行其他系统,如 VAX/VMS、MacOS(m68k 和 PPC)、Amiga、PC/MS-DOS、Atari ST 等。
尽管现在大多数桌面都是 x86,运行三种主要操作系统之一,但仍然有许多 CPU 架构和操作系统。
因此,即使在您开始考虑对可能在不同系统上具有不同版本的第三方库的依赖关系之前,CPU/操作系统组合的数量已经多得让您难以想象。 (任何未由操作系统供应商打包的内容都必须手动安装。)
编译到二进制文件中的任何路径也是特定于系统的。 (与启动时从配置文件读取它们相比,这可以节省 RAM 和时间)。老式 Unix 系统通常有一个很多手工定制的东西,所以你无法对什么在哪里做出任何有效的假设。
对于老式 Unix 来说,分发二进制文件是完全不可行的,除了能够在所有主要组合上构建和测试的大型商业项目之外。
即使只为i386-linux-gnu
和制作二进制文件也amd64-linux-gnu
很困难。在诸如此类的事情上花费了大量的时间和精力Linux 标准库使可移植的二进制文件成为可能。即使静态链接二进制文件也不能解决所有问题。 (例如,在 RedHat 系统和 Debian 系统上,文字处理程序应该如何打印?安装时应该如何为守护进程添加用户或组,并安排其启动脚本在每次重新启动后运行?)这些都不是很好示例,因为从源代码重新编译并不能解决这些问题。
除此之外,当年的记忆比现在更加珍贵。 在编译时省略可选功能可以创建更小的二进制文件(更小的代码大小),其数据结构也使用更少的内存。如果某个功能需要在某个特定实例的每个实例中都有一个额外的成员class
或struct
跟踪某些内容,则禁用该功能会将对象缩小 4 个字节(例如),如果该对象是程序分配 100k 的对象,则这是很好的。
如今,可选的编译时功能最常用于使额外的库成为可选的。例如,您可以ffmpeg
使用或不使用libx264
、libx265
、libvorbis
以及用于特定视频/音频编码器、字幕处理等的许多其他库进行编译。更常见的是,可以使用或不使用很多东西进行编译libreadline
:如果运行时可用./configure
,则生成的二进制文件将取决于库,并在从终端读取时提供精美的行编辑。如果不是,那么程序将使用一些后备支持来仅从标准输入中读取行fgets()
或其他内容。)
出于性能原因,某些项目仍然使用可选功能来省略不需要的代码。例如,Linux 内核本身可以在没有 SMP 支持的情况下构建(例如,对于嵌入式系统或古老的桌面),在这种情况下,很多锁定会更简单。或者与许多其他影响某些核心代码的可选功能,而不仅仅是忽略驱动程序或其他硬件功能。 (尽管特定于架构和特定于硬件的配置选项占很多总源代码。看为什么Linux内核有15+百万行代码?)