程序原本(四十九):程序设计的核心思想——执行体与它在执行过程中的环境(所谓操作系统,不过是参考环境更复杂的执行体而已)

阅读数:36 2019 年 10 月 1 日 16:06

程序原本(四十九):程序设计的核心思想——执行体与它在执行过程中的环境(所谓操作系统,不过是参考环境更复杂的执行体而已)

我们已经讨论四点:

  • 关联数组的实质是“名 / 值”映射;
  • 代码中的数据与逻辑可以统一为数据,进而统一为标识;
  • 关联数组可以维护一个计算过程所需的参考,亦即上下文环境;
  • 对于静态语言来说,上下文环境的实质是一个不需要名字 / 标识的(或称标识可选的)关联数组——这种情况下,它更像一个索引数组。

综上所述,对于静态的、编译型语言来说,下面的代码:

复制代码
// Pascal Syntax
var
x1 : Integer = 100;
x2 : String = 'Hello.';
procedure P(y1: String);
begin
...
end;

意味着P()这个计算过程所需的环境有两个,其一是x1x2 … xn,它们在P()进行计算之前就可以预知;其二是y1...yn,它们在P()计算的当时才会得知。对于编译型语言来说,它认为:

  • X 是 P() 计算的前设;且,
  • X 与 P 本身是后续其他计算的前设;继续推论之,则,
  • 所有在最外层出现的标识是整个系统运算的前设。

由此,编译型语言通过编译器程序

  • 将所有这些标识与其内容——例如数据、函数(逻辑、行为),
  • 根据所有计算过程约定的计算环境——例如操作系统,
  • 按照确认的规则——例如可执行文件格式,

编排并放在一个实体文件中。最后,操作系统(桌面用户或服务进程)决定何时将该实体文件装载并运行(Launch)起来,以完成所需的全部计算。

我们看到上述过程的限制条件包括:操作系统环境、可执行文件格式和编排方法。这些是程序得以执行的要件,但并非我们需要讨论的内容主体。因此,图 14 以 32 位 Windows 为例,仅大体说明这些要件之间的关系:

图 14 从一个入口开始:操作系统准备的执行环境

程序原本(四十九):程序设计的核心思想——执行体与它在执行过程中的环境(所谓操作系统,不过是参考环境更复杂的执行体而已)

参考图 14,对于操作系统来说,仅需要知道一个程序的“入口代码位置”。例如:

复制代码
// C Syntax
void function main(int argc,char *argv[]) {
...
}

随后,入口代码将按照规则在“代码表”中查找标识,例如:

复制代码
// C Syntax
function P() {
...
}
void function main() {
P();
...

最后,如果Pmain需要数据,则使用类似的方法通过“数据表”来查找标识。

编译型语言在程序执行前就明确这些标识,因此编译过程可以省去这些名字,而指代以存储中的索引,亦即地址;因为上述规则是操作系统对存储的约定,所以对于编译型语言来说,该可执行文件(PE 文件,Portable Execute)可以置入存储环境中,以应用上述地址;因为操作系统知道上述 PE 文件的入口地址,所以只需要:

  • 装载该 PE 文件、
  • 分配地址、
  • 交出 CPU 的执行权限,

然后就可以完成整个计算过程。

如上图所见,节表是节的索引,是节中的数据与代码5(及其标识),是整个计算过程的参考——上下文环境。推而广之,对于整个进程来说,它可以通过导入表来获得整个操作系统的上下文环境;细究之,对于一个内部函数来说,它可以通过入口参数表来得到上下文环境(例如此前讨论的y1...yn),进一步也可以得到进程的、操作系统的上下文环境。

5 这里是特指可以执行的机器指令。

总结这所有的行为,仅仅只有两种6:1、找到(包括逻辑在内的)数据;2、计算。

6 这里的推论是相当重要的。综合上述的讨论,我们对于一个环境的依赖,事实上可以看成对“定义与查找(数据)”这样的行为的依赖。再进一步,这也就揭示了我们的计算环境之于“数与算”的抽象本质,以及将数据结构(包括以此为基础的种种算法)作为核心研究领域的真实原因。

那么对于解释型语言来说,上述过程是更复杂,亦或更简单呢?

评论

发布