Nodejs 如何实现不同 Linux 发行版之间看似二进制的兼容性

Nodejs 如何实现不同 Linux 发行版之间看似二进制的兼容性

如果您访问下载网站节点js您可以选择Linux Binaries (x64)哪个是 tar 存档,其中(以及其他文件)包含一个bin/名为nodejs.

Nodejs 如何能够提供一个“通用”二进制文件,据说(或看似)能够在其上运行任何Linux 发行版?我知道他们没有在他们的网站上做出这样的声明,但这就是我的假设,因为没有特定的操作系统版本(例如 Ubuntu、Debian 等的 Nodejs)。

在阅读这个主题(Linux 发行版之间的二进制兼容性)时,我遇到了多个答案,这些答案基本上表明这实际上并不是一件事:

不,Debian 和 Ubuntu 不兼容二进制。 Debian 和 Ubuntu 可能使用具有不同 ABI、不同内核版本、不同库、不同软件包/版本等的不同编译器。由于并非所有 Ubuntu 软件包都在 Debian 中(反之亦然)deb 软件包也可能依赖于可卸载版本。

...

所以从技术上来说它们不兼容二进制。

回答问题“Ubuntu LTS 二进制文件与 Debian 兼容吗?”

我明白一些部分关于ABI像架构、调用约定、系统调用等,这对我来说是有意义的,如果使用相同的架构和调用约定,就会有一定的兼容性,因为每个 Linux 发行版的核心都是 Linux 内核。

然而我仍然很难理解为什么人们说两个 Linux 发行版不二进制兼容。

什么是吗那不兼容?

为什么 NodeJS 似乎只能为“所有”Linux 发行版提供一个二进制版本?

这更多的是一个教育问题,而不是“如何使二进制 X 与发行版 x、y 和 z 兼容”。

答案1

有多个级别的 ABI(应用程序二进制接口)。要制作一个在任何地方都能工作的二进制文件,必须针对其中一个级别。目标两个级别是内核 ABI 或 LSB ABI。

.deb 包在 Debian 和 Ubuntu 之间可能不兼容,因为它可能依赖于一种或另一种风格的其他东西,但不能同时依赖于两种风格。但是,许多 .deb 软件包将在 Ubuntu 和 Debian 的某些版本中运行。更糟糕的是,RedHat 的 rpm 不太可能在 Ubuntu 系统上运行,因为打包格式不被理解。

依赖关系导致可执行文件无法在不同的 Linux 风格中运行。几乎所有程序都依赖于外部库。当构建可执行文件时,Linux(以及许多其他操作系统,包括 Windows 和其他 UNIX)不会将这些外部库作为可执行文件的一部分,而是仅将其链接到存根,并且实际的库在许多可执行文件之间共享。这减少了磁盘和内存中可执行文件的大小,并且在某些情况下,允许修复库中的错误而无需重建可执行文件。但是,如果您的 Linux 风格中没有正确版本的共享库,则可执行文件将无法工作。

使用共享库的可执行文件被称为动态链接(这意味着,到共享库的最终链接是在运行时动态完成的)。也可以静态链接可执行文件。结果是一个更大的可执行文件,但它可以在更广泛的系统上运行。到那时,ABI 就不再是 Linux 的风格,而是内核本身了。一般来说,内核是向后兼容的,可执行文件是向前兼容的,这意味着(在某些广泛的内核版本中),旧的可执行文件将在较新的内核上运行,而较新的内核可以运行较旧的可执行文件。在 Linux 的历史上,可执行文件格式本身很少发生变化,因此这也是有限制的。

运行操作系统的 CPU 架构也必须与可执行文件相匹配,但有少数例外。 (通常,如果您有相关的匹配库,i386 二进制文件可以在 x86_64 系统上运行。)可以制作一个胖二进制文件,其中包含用于多种 cpu 架构的代码。这在一些操作系统中很常见,但在linux中很少见。

也可以有容器化的可执行文件。在这种情况下,就像乌龟一样,可执行文件被内置到文件系统(如squashfs)中,并在其二进制文件中作为单独的文件携带所有依赖库和支持文件。这就是 appimage 的作用。

file和命令ldd有时可以告诉您二进制文件是如何构建的,如上所述。例如:

$ file /bin/ls
/bin/ls: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=897f49cafa98c11d63e619e7e40352f855249c13, for GNU/Linux 3.2.0, stripped
$ ldd /bin/ls
    linux-vdso.so.1 (0x00007fff95ae1000)
    libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007fc6aebb8000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc6ae990000)
    libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007fc6ae8f9000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fc6aec50000)

该二进制文件适用于 x86_64 架构,动态链接到动态链接器的版本 2,并且需要上面列出的各种共享库的特定版本...(注意:主要版本必须匹配,次要版本甚至没有在上面列出。 )

将此与以下内容进行比较:

$ file /bin/busybox
/bin/busybox: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=36c64fc4707a00db11657009501f026401385933, for GNU/Linux 3.2.0, stripped
$ ldd /bin/busybox
    not a dynamic executable

这个静态链接的二进制文件将在任何支持 x86_64 ELF 版本 1 的 Linux 内核上运行。

$ file prusaslicer.AppImage
prusaslicer.AppImage: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.18, stripped
$ ldd prusaslicer.AppImage
    not a dynamic executable

这似乎是一个动态链接的可执行文件,但除了动态链接器本身之外没有任何共享库依赖项。这实际上是一个容器化的可执行文件,当它运行时,它会挂载内部文件系统并运行内部真正的可执行文件。唯一的线索(不运行它)是文件名(和大小)。运行它后,可以检查保险丝安装的文件系统,尽管这在幕后工作有些隐藏。

查看您询问的具体示例...我下载了 x86_64 linux 版本的 node.js 并发现了以下内容:

$ file bin/node 
bin/node: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=e8b23b15ec6280a0d4838fbba1171cb8d94667c5, with debug_info, not stripped
$ ldd bin/node
    linux-vdso.so.1 (0x00007ffd633f0000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007efec3198000)
    libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007efec2f6c000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007efec2e85000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007efec2e65000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007efec2e60000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007efec2c36000)
    /lib64/ld-linux-x86-64.so.2 (0x00007efec31e5000)

因此,作为一个具有相当数量的外部共享库依赖项的动态链接可执行文件,这将限制它可以在哪些系统上运行,但具有这些特定库(或可以安装它们)的版本范围可能相当广泛。这些都是大多数 Linux 系统应该具备的基础库,并且它们已经足够稳定,以至于各种操作系统版本都拥有这些版本的库。有时,如果您缺少其中一些,安装适合您的 Linux 风格的 lsb-base 或 lsb-core 软件包将使它们可用。 LSB(Linux Standard Base)是一种 ABI 标准,包括一组基本标准库和其他项目,这些项目成为所有希望为第三方开发人员提供共同目标的 Linux 发行版的最低公分母。

请注意,如果您有一个可执行文件不会运行,但说它应该运行,您可以像上面那样进行侦探工作,确定系统上缺少什么并安装缺少的库。

相关内容