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

大家好,我是「柒八九」

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

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

文章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显示出来。

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


后记

「分享是一种态度」

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

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


计算机底层知识之运行环境可执行文件相关推荐

  1. 生猛!这篇万字长文,一下子把计算机底层知识说明白了!

    点击上方"码农突围",马上关注 这里是码农充电第一站,回复"666",获取一份专属大礼包 真爱,请设置"星标"或点个"在看&quo ...

  2. 一网打尽:Java 程序员必须了解的计算机底层知识!

    公众号后台回复"面试",获取精品学习资料 扫描下方海报了解专栏详情 本文来自公众号读者cxuan的投稿 我们每个程序员或许都有一个梦,那就是成为大牛,我们或许都沉浸在各种框架中,以 ...

  3. 计算机底层知识之内存

    ❝ 渔夫出海前,并不知道鱼在哪里,可是他们还是选择出发, 因为他们相信,一定会满载而归.人生很多时候,是「选择了才有机会,是相信了才有可能」. -- 「稻盛和夫」 ❞ 大家好,我是「柒八九」. 今天, ...

  4. 计算机底层知识之CPU

    ❝ Only the disciplined in life are free. 唯自律者得自由 ❞ 大家好,我是「柒八九」. 想必能看到这篇文章的小朋友,大都是有一定编程能力的「程序媛.程序猿」.无 ...

  5. 计算机底层知识之二进制

    ❝ 巴西作家保罗·科埃略的一句话:「如果你想成功,你必须遵守一条规则:永远不要对自己撒谎.」 ❞ 大家好,我是「柒八九」. 今天,我们继续「计算机底层知识」的探索.我们来谈谈关于「二进制」的相关知识点 ...

  6. Java程序员需要掌握的计算机底层知识(三):进程、线程、纤程、中断

    面试高频问题 问:进程和线程有什么区别? 答:进程是一个程序运行起来的状态(运行态),线程是一个进程中不同的执行路径(线程只是其中一个). 更为专业的回答:进程是操作系统用来分配资源的基本单位,线程是 ...

  7. Java程序员需要掌握的计算机底层知识(一):CPU基本组成、指令乱序执行、合并写技术、非同一访问内存 NUMA

    一些书籍 读书的原则:不求甚解,观其大略 你如果进到庐山里头,二话不说,蹲下头来,弯下腰,就对着某棵树某棵小草猛研究而不是说先把庐山的整体脉络跟那研究清楚了,那么你的学习方法肯定效率巨低而且特别痛苦, ...

  8. Java程序员需要掌握的计算机底层知识(五):内核同步方法

    内核同步机制 关于同步理论的一些基本概念 临界区(critical area): 访问或操作共享数据的代码段 简单理解:synchronized大括号中部分(原子性) 竞争条件(race condit ...

  9. Java程序员需要掌握的计算机底层知识(四):内存管理

    内存管理 单进程DOS时代 DOS时代 - 同一时间只能有一个进程在运行(也有一些特殊算法可以支持多进程) windows9x - 多个进程装入内存存在的问题: 内存不够用 互相打扰 为了解决这两个问 ...

  10. Java程序员需要掌握的计算机底层知识(二):操作系统、内核、用户态与内核态、系统调用的执行过程

    操作系统 启动过程 通电 -> bios uefi 工作 -> 自检 -> 到硬盘固定位置加载bootloader -> 读取可配置信息 -> CMOS CMOS 用来存 ...

最新文章

  1. 关于JSON的一些问题
  2. ES6 系列之 let 和 const
  3. Windows 显示环境变量
  4. 查询方法android的CursorLoader用法小结
  5. 浅析ASP.NET回车提交事件[转]
  6. 推荐 | 8 个 SpringBoot 精选项目
  7. .NET6 如期⽽⾄
  8. mysql初体验学习笔记_【MySQL】mycli初体验
  9. word中的图片设定成统一文字环绕格式
  10. java wifi类_Java 接口——面向对象的精髓
  11. 面向对象设计——七大原则
  12. wpf button无边框_中国式新房无玄关?客厅真不缺这点面积!
  13. 技巧:如何从苹果Mac跟踪设备上所有电池的电量?
  14. PMP 第十章 项目沟通管理
  15. Julia: 如何转换PyPlot的X轴的日期格式?
  16. 必备技能:IDEA一定要懂的30条快捷键
  17. win10不能拖动文件到其它软件
  18. 20100920卡巴最新激活码下载
  19. 学习Python,你都遇到了哪些坑?
  20. java 判断今天星期几_java判断今天星期几

热门文章

  1. (转)日本語を輸入について
  2. 台式机机械硬盘 - 简单快捷的安装
  3. C++ 逻辑与或非 逻辑与逻辑或 逻辑非
  4. 【OOC学习19】TKBrep:边界表示法的实现
  5. redis streams_如何使用Redis Streams
  6. Java类和对象之对象组合之求圆柱体积
  7. python 计时器模块_python中计时器
  8. 美颜相机android版,美颜相机手机版
  9. 武汉城建学院计算机专业柴曲,采访对话丨在世界一流的柴院学音乐,是怎样一种感受?...
  10. ES2015 Module(模块)