乐趣区

关于linux:TIL-二进制是如何被执行的

行文会比拟乱,因为 TIL 次要目标是组织本人的想法而非分享。如果凑巧能帮到他人就更好了,有感兴趣的部份感觉没讲清楚的,能够留言,我能够尝试进一步阐明。


要执行一个二进制,同时须要 CPU 和操作系统的配合。

  • CPU

    • CPU 能够解读针对某种指令集的二进制指令(比方 x86、amd64)
    • 有些 CPU 反对扩大指令集,即不蕴含在指令集标准中的额定的指令。在编译二进制时如果启用了这些扩大,就能够在反对扩大指令集的 CPU 中用上这些个性。能够在运行时查看 CPU 是否反对某种扩大,所以编译器通常会提供一条不反对的状况下用于降级的分支。
  • OS

    • 一个二进制可能会用到一些别的库,比方用了 windows api 的二进制就不能运行在 linux 上。Unix 兼容零碎中,要害的零碎 api 被标准化为 POSIX 规范。如果一个二进制只用了 POSIX api,它就能够在任何 Unix 零碎中运行,比方 Mac OS X 或 Solaris 等
  • 二进制格局

    • 除此之外,二进制必须抉择恪守某种 OS 要求的二进制格局,能力被 OS 加载、执行。如 windows 中宽泛应用的 Portable Executable format(exe 文件),Linux 中宽泛应用的 ELF format(Executable and Linkable Format)

最终,如果两个零碎:

  • 有同样的 System API、零碎库
  • 同样的指令集
  • 应用同样的二进制格局

为一个零碎编写的二进制就能够在另一个零碎中运行。

  • x86 指令集的二进制能够运行在 AMD64 指令集中

    • 二进制能够指定 CPU 用什么模式运行本人
    • 有一些二进制格局容许把多个程序放在同一个文件中,每个程序针对一种不同的指令集

      • 苹果从 PowerPC 架构转移到 x86 架构时就用上了“fat binaries”
  • 有一些程序会编译成两头模式,而非最终在 CPU 上执行的二进制

    • 比方 java 的字节码、v8 也有字节码的优化
    • 这种计划要求在不同的指令集上实现各自的虚拟机,以运行中间代码,转换成 CPU 能运行的实在指令集

杂记

本文相干的一些概念,想到的都放这

  • 当 OS 加载一个二进制的时候,它会决定二进制被如何运行。能够在编译时指定一个指标 CPU,如果不指定编译器通常应用以后的 CPU 并只应用这个 CPU 和它更低版本反对的指令集。如果你想用只在你的 CPU 上反对的新指令,则须要告知编译器,或应用汇编自行编写。但这样一来,你失去的二进制如果在不反对改指令的 CPU 上运行,就会解体。
  • 指令集的由来

    • 最后的二进制都是为特定的某一款 CPU 编写的,无奈执行在另一款中,为了解决这个问题提出了指令集,不同的 CPU 只有反对了同样的指令集,同样的二进制就能够运行在这些不同的 CPU 中
  • 穿插编译

    • 在一个零碎中为另一个不同的零碎编译二进制
    • 比方在 windows 中编译出 linux 中能够运行的二进制
  • 只有两个设施的 CPU 反对雷同的指令集,雷同的二进制就能够在两个设施上运行么

    • 不是,还要看 OS 的 System API 是否兼容
    • 比方同样的硬件能够装置 linux、windows 零碎,windows 中的利用就不能在 linux 中运行
  • 指令集是啥

    • CPU 反对的一系列指令,比方咱们有一个 CPU 反对以下指令

      • 写入寄存器 a
      • 读取寄存器 a
      • 写入寄存器 b
      • 读取寄存器 b
      • 将第一个、第二个数相加,后果写入寄存器 a
    • 咱们写了一段代码“1×3+2=?”它为指标,编译进去的二进制就是:

      • 将 1 写入寄存器 a
      • 将 1 写入寄存器 b
      • 相加
      • 将 1 写入寄存器 b
      • 相加
      • 将 2 写入寄存器 b
      • 相加
      • 读取寄存器 a
    • 下面的指令是能够在这个 CPU 中执行的,但如果你写了这样的二进制

      • 将 1 写入寄存器 a
      • 将 3 写入寄存器 b
      • 相乘 <<<<
      • 将 3 写入寄存器 b
      • 相加
      • 读取寄存器 a
    • 这个二进制就不能在该 CPU 中执行,因为用到了一个它不反对的指令“相乘”
  • System API 是啥

    • 操作系统提供的 API
    • 常见的有

      • POSIX:UNIX、LINUX、Mac OS X 等
      • Win32: Windows
      • Java 虚拟机提供给字节码的 API 也算是一种 System API
      • 并不齐全对标,比方 Win32 中蕴含 GUI 相干的 API,创立窗口之类的,但 POSIX 中没有
    • 假如有这样一个 OS A,他的窗口必须有,且只有一个题目,当二进制想要创立一个本人的窗口,能够调用这个 OS 提供的一个 API

      • create_window("Hello world")
      • 这样就能创立出一个题目为 Hello world 的窗口
    • 而后再假如另一个 OS B,它的窗口必须有题目和副标题,它提供的 API 可能就是

      • `create_window(“Hello world”, “subtitle”)
      • 如果在这个 OS 中执行后面的二进制,显然短少参数,它们是不兼容的
      • 因而只管 CPU 能够执行二进制,但 System API 不兼容的状况下,程序仍然可能解体
      • wine 能够让 windows 利用在 linux 中执行,一部分原理就是翻译这种零碎调用

        • 以后面的创立窗口为例,为了在 OS B 中运行 OS A 的利用,wine 这种利用可能在收到 create_window("Hello world") 时,将它翻译为 create_window("Hello world", "__SPOOF__"),这样一来 OS B 失去了本人想要的两个参数。但缺点是所有通过兼容层运行的 OS A 的利用,都会同样的副标题“__SUB_TITLE__”。这种 System API 差别有时能够完满翻译,但有时也会像这个例子一样有缺憾。
  • 二进制格局是啥

    • 把内容组织进一个二进制文件的规定
    • 假如一个二进制格局 A,规定如下

      • 结尾 N 个字节:我是格局 A、我的指标架构是 AMD64、我应用大端 / 小端、应该从哪个地址开始执行,吧啦吧啦
      • 接下来 N 个字节:可执行指令的范畴
      • 接下来 N 个字姐:数据的范畴
      • 可执行指令
      • 数据
    • OS 在执行这个二进制的时候会有这样的步骤

      • 解读结尾 N 个字节,失去各种信息
      • 创立内存空间,加载可执行指令到内存
      • 从指定地址开始执行指令
  • 后面说 POSIX 没有 GUI 相干的 API,wine 是怎么把 windows 的 gui 利用跑在 linux 中的

    • wine 把 GUI 相干的 API 翻译为 X11 的
    • 想独自开一篇讲 X11(QT、GTK、GNOME 等一众小弟),感觉也很有意思

参考资料

https://www.geeksforgeeks.org…

退出移动版