前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >计算机底层知识之运行环境&可执行文件

计算机底层知识之运行环境&可执行文件

作者头像
前端柒八九
发布2022-12-19 21:37:34
1K0
发布2022-12-19 21:37:34
举报
文章被收录于专栏:柒八九技术收纳盒

❝学习,说到底是一个「学」「练」,以及学以致用的过程 ❞

大家好,我是「柒八九」

今天,我们继续「计算机底层知识」的探索。我们来谈谈关于「运行环境&可执行文件」的相关知识点。

如果,想了解该系列的文章,可以参考我们已经发布的文章。如下是往期文章。

文章list

  1. 计算机底层知识之CPU
  2. 计算机底层知识之二进制
  3. 计算机底层知识之处理小数
  4. 计算机底层知识之内存
  5. 计算机底层知识之内存和磁盘的关系&数据压缩

你能所学到的知识点

  1. 运行环境 = 操作系统 + 硬件 「推荐阅读指数」 ⭐️⭐️⭐️⭐️⭐️
  2. 不同操作系统的API不同 「推荐阅读指数」 ⭐️⭐️⭐️⭐️⭐️
  3. Java 虚拟机 「推荐阅读指数」 ⭐️⭐️⭐️
  4. BIOS和引导 「推荐阅读指数」 ⭐️⭐️⭐️
  5. 计算机只能运行本地代码 「推荐阅读指数」 ⭐️⭐️⭐️⭐️⭐️
  6. 本地代码的内容 「推荐阅读指数」 ⭐️⭐️⭐️⭐️⭐️
  7. 编译器负责转换源代码 「推荐阅读指数」 ⭐️⭐️⭐️⭐️⭐️
  8. DLL文件及导入库 「推荐阅读指数」 ⭐️⭐️⭐️⭐️⭐️
  9. 可执行文件运行时的必要条件 「推荐阅读指数」 ⭐️⭐️⭐️⭐️⭐️
  10. 程序加载时会生成栈和堆 「推荐阅读指数」 ⭐️⭐️⭐️⭐️⭐️

好了,天不早了,干点正事哇。


运行环境 = 操作系统 + 硬件

「应用软件」能够运行,是需要依赖指定的运行环境的。而运行环境是「操作系统」「计算机硬件」两者的综合。也就是说「操作系统」「硬件」决定了程序的运行环境。

这就是说明了,从应用市场上下载软件是,一般都需要按照你「自身本地」的操作系统而选择对应的软件包。

CPU只能解释其「自身固有」「机器语言」

不同的CPU能解释的机器语言的种类也是不同的。例如,CPU品牌分为IntelAMD两种;,它们各自的机器语言是完全不同的。

「机器语言」的程序称为本地代码Native Code。程序员用C/Java等编写的程序,在「编写阶段」仅仅是「文本文件」

文本文件(排除文字编码问题)在「任何环境」下都能显示和编辑。我们称之为「源代码」

❝通过对源代码进行「编译」,就可以得到本地代码 ❞


不同操作系统的API不同

同样机型的计算机,可安装的操作系统类型也会有多种选择。也就意味着,应用软件则必须根据不同的操作系统类型来专门开发。

CPU的类型不同,所对应的机器语言也不同,同样的道理,「操作系统的类型不同,应用程序向操作系统传递指令的途径也是不同的」

应用程序向操作系统传递指令的途径称为「API」(Application Programming Interface)。WindowsUnix系列操作系统的API,提供了任何应用程序都可以利用的「函数组合」

因为不同操作系统的API是有差异的,因此,将同样的应用程序移植到其他操作系统时,就必须重写应用中利用到API的部分。

在同类型操作系统下,不管硬件如何,API基本上没有差别。因此,针对某特定操作系统的API所编写的程序,在任何硬件上都可以运行。

当然,由于CPU种类不同,机器语言也不相同,因此本地代码也不同。这种情况下,就需要利用能够生成各CPU专用的本地代码的「编译器」,来对源代码进行重新编译。

❝程序(本地代码)的运行环境是由操作系统和硬件来决定的 ❞


Java 虚拟机

Java能够提供「不依赖于特定硬件及操作系统」的程序运行环境。

针对Java有两个层面。一是作为「编程语言」,另一个是作为「程序运行环境」

同其他编程语言相同,Java也是将Java语法记述的「源代码」编译后运行。不过,编译后生成的「并不是特定CPU使用的本地代码」,而是名为「字节代码」的程序。

字节代码的运行环境被称为「Java虚拟机」Java Virtual Machine)。

Java虚拟机是一边把Java字节代码逐一准换成本地代码,一边运行的。 ❞

「编译器」将程序员编写的「源代码」(xx.java)转换成「字节代码」xx.class)。而Java虚拟机(java.exe)则会「把字节代码变成本地CPU适用的本地代码」,然后由本地CPU负责实际的处理。

从操作系统方面来看,Java虚拟机是一个应用,而从Java应用来看,Java虚拟机就是运行环境。


BIOS和引导

程序的运行环境中,存在着名为「BIOS」(Basic Input/Output System)的系统。

BIOS存储在ROM中,是预先「内置」在计算机主机内部的程序。 ❞

BIOS除了键盘、磁盘、显卡等基本控制程序外,还有启动「引用程序」的功能。

「引导程序」是存储在「启动驱动器」起始区域的小程序。 ❞

开机后,BIOS会确认「硬件是否正常运行」,没有问题的话就会启动「引导程序」。引导程序的功能是把在硬盘等记录的OS加载到内存中运行。


源代码完成后,就可以编译生成「可执行文件」了。负责实现该功能的是「编译器」

计算机只能运行本地代码

假设我们通过「高级语言」(语言类型不限),编写一个把123456的平均值289.5显示出来。

代码语言:javascript
复制
function Main(){
  let ave;
  ave = (123 + 456)/2;
  alert(ave);
}

类似上述的代码,用「某种」编程语言编写的程序被称为「源代码」,保存源代码的文件被称为「源文件」。用JS编写的源文件的扩展名通常是.js

上述的源代码是无法直接运行的。这是因为,CPU能直接解析并运行的不是源代码而是「本地代码」的程序。作为计算机大脑的CPU,也「只能解释已经准换成本地代码的程序内容」

本地这个术语有母语的意思。「对CPU来说,母语就是机器语言,而转换成机器语言的程序就是本地代码」。用任何编程语言编写的源代码,最后都要翻译成本地代码,否则CPU就不能理解。也就是说,「即使是用不同编程语言编写的代码,转换成本地代码后,也都变成用同一种语言(机器语言)来表示」


本地代码的内容

WindowsEXE文件的程序内容,使用的就是本地代码。本地代码的内容是人类无法理解的,也正是因为如此,才有了用人类容易理解的C语言等编程语言来编写源代码,然后再将源代码转换成本地代码。

我们可以把EXE文件的内容Dump一下。Dump是指把文件的内容,每个字节用2位十六进制数来表示的方式。本地代码的内容就是各种数值的罗列,而这些数值就是本地代码的真面目。每一个「数值」都表示某一个命令或数据。

本地代码Dump之后的样子


编译器负责转换源代码

能够把C/Java等高级编程语言编写的源代码准换成本地代码的程序称为「编译器」。每个编写源代码的编程语言都需要其「专用」的编译器。将C语言编写的源代码转换成本地代码的编译器称为C编译器.

❝编译器首先读入代码的内容,然后再把源代码转换成本地代码。 ❞

编译器中就好像有一个源代码同本地的对应表。但实际上,仅仅靠对应表是无法生成本地代码的。读入的源代码还要经过「语法解析」「句法解析」「语义解析」等才能生成本地代码。

根据CPU类型不同,本地代码的类型也不同。因此,编译器不仅和编程语言的种类有关,和CPU的类型也是相关的。「同样的源代码就可以翻译成适合不同CPU的本地代码」

因为「编译器本身也是程序的一种,所以也需要运行环境」。例如,有Windows用的C编译器Linux用的C编译器等。此外,还有一种「交叉编译器」,它生成的是和运行环境中的CPU不同的CPU所使用的本地代码。

仅靠编译是无法得到可执行文件

编译器转换源代码后,就会生成本地代码。不过,本地文件是无法直接运行的。为了得到可以运行的EXE文件,编译之后还需要进行「链接」操作。

我们拿C语言Windows环境下举例。

编译后生成的不是EXE文件,而是扩展名为.obj「目标文件」xx.c编译后,就生成了xx.obj目标文件。虽然目标文件的内容是本地代码,但却无法直接运行。其原因就是「当前程序还处于未完成状态」

把多个目标文件结合,生成1个EXE文件的处理就是「链接」,运行链接的程序就是链接器Linkage Editor。


DLL文件及导入库

Windows「函数的形式」为应用提供了各种功能。这些形式的函数称为「API」(Application Programming Interface,应用程序接口)。

Windows中,API的目标文件,并不是存储在通常的库文件中,而是存储在名为「DLL」(Dynamic Link Library)文件的特殊库文件中。「DLL文件是程序运行时动态结合的文件」

与此相反,存储着目标文件的实体,并直接和EXE文件结合的库文件形式称为「静态链接库」


可执行文件运行时的必要条件

EXE文件是作为「单独的文件」存储在硬盘中的。通过资源管理器找到并双击EXE文件,就会把EXE文件的内容加载到内存中运行。 ❞

这里有一个疑问?本地代码在对程序中记述的变量进行读写时,是参照数据存储的内存地址来运行命令的。在调用函数时,程序的处理流程就会跳转到存储着函数处理内容的内存地址上。EXE文件作为本地代码的程序,并没有指定变量及函数的「实际内存地址」。在类似于Windows操作系统这样的可以加载多个可执行程序的运行环境中,每次运行时,程序内的变量及函数被分配到的内存地址都是不同的。

那么,在EXE文件中,变量和函数的内存地址的值,是如何来表示的呢?

那就是EXE文件中给变量和函数分配了「虚拟的内存地址」。在程序运行时,「虚拟的内存地址会转换成实际的内存地址」

❝链接器会在EXE文件的开头,追加转换内存地址所需的必要信息。这个信息称为「再配置信息」。 ❞

EXE文件的再配置信息,就成了变量和函数的「相对地址」。相对地址表示的是相对于「基点地址」的偏移量,也就是相对距离。

链接后的EXE文件的构造


程序加载时会生成栈和堆

EXE文件的内容分为「再配置信息」「变量组」「函数组」。不过,当程序加载到内存后,除此之外还会额外生成两个组,那就是「栈」「堆」

  • 「栈」是用来存储「函数内部临时使用的变量」局部变量),以及函数调用时所用的参数的内存区域。
  • 「堆」是用来存储程序运行时的任意数据及对象的内存领域

加载到内存的程序由4部分构成

堆和栈的相似之处在于,他们的内存空间都是在程序运行时得到分配。


Q&A

编译器和解释器有什么不同

「编译器」是在「运行前」对所有源代码进行解释处理的。而「解释器」则是在「运行时」对源代码的内容一行一行的进行解释处理

分割编译

将整个程序分为多个源代码来编写,然后分别进行编译,最后链接成一个EXE文件。

使用DLL文件的好处

DLL文件中的函数可以被「多个程序共用」。因此,「借助该功能可以节约内存和磁盘」。此外,在对函数的内容进行修正时,还不需要重新链接使用这个函数的程序。


后记

「分享是一种态度」

参考资料:《程序是怎样跑起来的》

「全文完,既然看到这里了,如果觉得不错,随手点个赞和“在看”吧。」

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-11-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端柒八九 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章list
  • 你能所学到的知识点
  • 运行环境 = 操作系统 + 硬件
  • 不同操作系统的API不同
  • Java 虚拟机
  • BIOS和引导
  • 计算机只能运行本地代码
  • 本地代码的内容
  • 编译器负责转换源代码
    • 仅靠编译是无法得到可执行文件
    • DLL文件及导入库
    • 可执行文件运行时的必要条件
    • 程序加载时会生成栈和堆
    • Q&A
      • 编译器和解释器有什么不同
        • 分割编译
          • 使用DLL文件的好处
          • 后记
          相关产品与服务
          对象存储
          对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档