哪些社会或技术因素导致 CIY心态的兴起?
根本原因显然是技术原因: 二进制可移植性比源可移植性难。在发行版软件包之外,大多数免费软件仍仅以源代码形式提供,因为这对于作者/维护者而言要方便得多。
在Linux发行版开始打包普通人想使用的大多数东西之前,您唯一的选择是获取源代码并针对您自己的系统进行编译。商业Unix供应商通常不包含几乎每个人都想要的东西(例如,像GNU bash
或类似的不错的shell ),而只是他们自己的sh
和/或实现csh
,因此如果您(作为系统管理员)需要,则需要自己构建东西。为您的用户提供一个不错的Unix环境以进行交互使用。
现在的情况与大多数人是坐在桌面上的计算机的唯一管理员和唯一用户相比,这种情况与传统的Unix模型有很大的不同。 系统管理员将软件维护在中央系统以及每个人的桌面上。(通常是使人们的工作站仅安装在NFS上/opt
并/usr/local/
从中央服务器安装,然后在其中安装东西。)
在诸如.NET和Java之类的东西之前,跨不同CPU体系结构实现真正的二进制可移植性是不可能的。出于这个原因,Unix文化演变为默认具有源可移植性,在最近的Linux之类的LSB努力之前,几乎没有任何努力甚至尝试启用二进制可移植性。例如,POSIX(将主要的Unix标准)只尝试标准化源的便携性,即使在最近的版本。
相关的文化因素:早期的商业化AT&T Unix带有源代码(在磁带上)。你没有必须从源代码编译系统,它只是有万一你想看看东西真的当文档还不够努力。
维基百科说:
“ Unix的广泛在线文档政策以及(多年以来)随时可以访问所有系统源代码的政策提高了程序员的期望,并推动了1983年自由软件运动的启动。”
我不确定是什么原因促使了这一决定,因为这些天没有让客户访问商业软件的源代码了。在这个方向上显然有一些早期的文化偏见,但是也许是从Unix的根源发展出来的,因为它是一种主要用C(不是汇编语言)编写的便携式OS,可以针对不同的硬件进行编译。我认为许多早期的OS都在asm中为特定的CPU编写了更多代码,因此源代码级可移植性是Unix早期的优势之一。(对此我可能是错的;我不是早期Unix的专家,但是Unix和C是相关的。)
到目前为止,以源代码形式分发软件是使人们适应他们想要在其上运行的任何系统的最简单方法。(最终用户或将其打包为Linux发行版的人)。如果软件已经由分发包打包/用于分发,则最终用户可以使用它。
但是,期望大多数软件包的作者自己为每种可能的系统制作二进制文件实在是太过分了。一些主要项目为一些常见情况提供了二进制文件(尤其是x86 / windows,其中OS并未附带构建环境,并且OS供应商已将重点放在仅二进制安装程序的分发上)。
要使某款软件在与作者使用的软件不同的系统上运行,甚至可能需要进行一些小的更改,而这些更改对于source来说很容易。有人写过一个小小的一次性程序,以挠痒痒,可能从未在大多数晦涩的系统上进行过测试。有了来源,就可以进行此类更改。原始作者可能忽略了某些内容,或者有意编写了可移植性较低的代码,因为它节省了大量时间。即使像Info-ZIP这样的主要软件包也没有立即在每个平台上都配备测试人员,并且需要人们在发现问题后发送其可移植性补丁。
(还有其他类型的源代码级的便携性问题,只有发生的,因为在建ENV的差异,而不是这里的问题。随着Java风格的二进制可移植性,汽车修理工具(真正相关的autoconf
/ auto-make
),并像类似的事情cmake
止跌我们不需要<netinet/in.h>
<arpa/inet.h>
ntohl(3)
像某些系统要求包含而不是 for的东西(也许我们一开始就不需要ntohl()
或任何其他字节顺序的东西!)
我会定期使用.NET语言进行开发,因此我不会识字。
一次编译,随处运行是.NET和Java的主要目标之一,因此可以说,已经发明了整个语言来解决此问题,而您的开发经验就是其中之一。使用.NET,您的二进制文件可以在可移植运行时环境(CLR)上运行。Java将其运行时环境称为Java虚拟机。您只需要分发一个可以在任何系统上使用的二进制文件(至少在有人已经实现了JVM或CLR的任何系统上)。当然,您仍然会遇到可移植性问题,例如/
vs \
路径分隔符,如何打印或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
。
请注意,即使是动态链接的二进制文件也仍然具有某些特定于OS的启动代码,这些启动代码之前运行main()
。在Windows上,这是crt0
。Unix和Linux具有相同的功能,其中一些C运行时启动代码静态链接到每个二进制文件中。我想从理论上讲,您可以设计一个系统,在该系统中代码也可以动态链接,并且是libc或动态链接器本身的一部分,但这并不是我所知道的任何操作系统在实际中的工作方式。那只能解决系统调用的ABI问题,而不能解决标准库函数常数的数值问题。(通常,系统调用是通过libc包装函数进行的:使用的源代码的普通x86-64 Linux二进制文件mmap()
将不包含syscall
指令,仅包含call
相同名称的libc包装函数的指令。
这就是为什么您不能仅在i386-Linux上运行i386-FreeBSD二进制文件的部分原因。(有一阵子,Linux内核具有系统调用兼容性层。我认为至少一个BSD可以运行Linux二进制文件,并且具有类似的compat层,但是您当然需要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 / OS组合。(操作系统供应商未打包的所有内容都必须手动安装。)
编译为二进制文件的任何路径也是特定于系统的。(与启动时从配置文件读取它们相比,这节省了RAM和时间)。老式的Unix系统通常有很多手工定制的内容,因此您无法对何处进行任何有效的假设。
对于老式的Unix而言,分发二进制文件是完全不可行的,除了可以负担所有主要组合的构建和测试费用的大型商业项目。
即使做的只是二进制文件i386-linux-gnu
和amd64-linux-gnu
硬。为了使可移植的二进制文件成为可能,在Linux Standard Base之类的东西上花费了大量时间和精力。即使静态链接二进制文件也不能解决所有问题。(例如,在RedHat系统和Debian系统上应如何打印文字处理程序?安装程序应如何为守护程序添加用户或组,并安排其启动脚本在每次重新启动后运行?)例子,因为从源头重新编译不能解决它们。
除此之外,过去的记忆比现在更宝贵。 在编译时保留可选功能可以创建较小的二进制文件(较小的代码大小),从而也为它们的数据结构使用较少的内存。如果某个功能在某个对象的每个实例中都需要一个额外的成员class
或struct
要追踪某个东西,则禁用该功能会将对象缩小4个字节(例如),如果它是程序分配100k的对象,则很好。
如今,可选的编译时功能通常用于使其他库成为可选的。例如,您可以针对特定的视频/音频编码器,字幕处理等,使用,ffmpeg
或不使用libx264
,,和其他许多库进行编译。更常见的是,可以使用或不使用来编译很多东西:如果运行时可用,则生成的二进制文件将取决于库,并在从终端读取时提供精美的行编辑。如果不是,则该程序将使用一些后备支持以仅从标准输入中读取带有或的行。)libx265
libvorbis
libreadline
./configure
fgets()
出于性能原因,某些项目仍会使用可选功能来省略不需要的代码。例如,Linux内核本身可以在不支持SMP的情况下构建(例如,用于嵌入式系统或古老的台式机),在这种情况下,很多锁定操作都比较简单。或具有影响某些核心代码的许多其他可选功能,而不仅仅是遗漏了驱动程序或其他硬件功能。(尽管特定于arch的配置和特定于硬件的配置选项在总的源代码中占了很大的比例。请参阅为什么Linux内核有超过1500万行代码?)