赞
踩
编程语境中的 runtime 至少有三个含义,分别可以这样概括:
一个程序从写好代码字符串(起点)到跑完退出(终点),有一整套标准化的生命周期(流程),可以被拆分为多个阶段。这其中编译阶段是 compile time,链接阶段是 link time,那运行起来的阶段自然就是 run time 了。如果在前面的阶段预先做了通常在后面才方便做的事,我们就管这个叫 ahead of time。
运行时库是一种被编译器用来实现编程语言内置函数,以提供该语言程序运行时支持的一种特殊的计算机程序库。运行时库由编译器决定,以面向编程语言,一般由编译器生产商提供。
通常运行时库是以LIB或DLL形式提供的,运行时库除了提供必要的库函数调用(如memcpy、printf、malloc等)之外,它提供的另一个最重要的功能是为应用程序添加启动函数。C运行时库启动函数的主要功能为进行程序的初始化,对全局变量进行赋初值,加载用户程序的入口函数。
换句话说,虽然 C 的 if、for 和函数等语言特性都可以很朴素且优雅地映射(lowering)到汇编,但必然会有些没法直接映射到系统调用和汇编指令的常用功能,比如上面介绍的那几项。对于这些脏活累活,它们就需要由运行时库(例如 Linux 上的 glibc 和 Windows 上的 CRT)来实现。
我们可以把「应用程序、运行时库和 OS」三者间的关系大致按这样来理解:
注意运行时库并不只是标准库,你就算不显式 include 任何标准库,也有一些额外的代码会被编译器插入到最后的可执行文件里。比如上面提到的 main 函数,它在真正执行前就需要大量来自运行时库的辅助:
除了加载和退出这些程序必备的地方以外,运行时库还可以在程序执行过程中被隐式而按需调用。例如 gcc 的 libgcc,这些库都是特定于编译器的。
由于系统级语言被设计成既可以用来写操作系统上的原生应用,也可以用来写 bare metal 的裸机程序,因此这类语言需要的运行时(runtime)被设计成了可以按需使用的库(library)。
上面介绍的运行时库,主要针对的是 C、C++ 和 Rust 这些系统级语言。只要将这个概念继续推广到其他高级语言,这时候的运行时指的就是 runtime system 了——如果讨论某门高级语言的运行时,我们通常是在讨论一个更重、更大而全的运行时库。
比如 Java 的运行时是 JRE,C# 的运行时是 CLR。这两者都相当于一个需要在 OS 上单独安装的软件,借助它们来解释执行相应语言的程序(编译出的字节码)。而对 JavaScript 来说,一般JS 引擎是个不带 IO 支持的虚拟机,需要浏览器和 Node 这样的 JS 运行时才能让它控制文件、网络、图形等硬件资源而真正实用。这些都是很经典的模型了。
典型的高级语言运行时系统里大概需要这些基础组件:
可以看到相比上面 C 语言的运行时,这已经是个复杂的基础软件系统了。
稍微再展开一点,注意上面的运行时里是不包含应用程序业务逻辑的。那么拿 JavaScript 举例来说,如果我们把业务逻辑先编译成字节码,再把它和运行时一起编译成一个可执行文件,那不就相当于直接把 JavaScript 编译成机器码了吗?QuickJS 就可以这么做,但其实这时候业务逻辑解释执行的天性不会变——难道真有黑科技能把弱类型的脚本直接靠静态分析编译达到系统级语言的水平?这更多地只是概念定义上的话术而已。
因此,理论上任意的弱类型动态语言都可以基于这种形式来 AOT 编译成原生机器码,你看 Dart、Swift 和 Java 都可以直接编译成可执行文件,区别只是这个运行时的轻重量级不同——当然实际情况肯定没有这么理想化,譬如哪怕编译成了 ARM 机器码,Flutter 里的 Dart 运行时也必然需要比 C 做更多的类型检查和 stop the world 的 GC,这都是有成本的。但对于应用层开发来说,能做到这样已经够好了。所以我们甚至可以激进地认为对于 OS 上的应用程序,各种编程语言都是或多或少地需要运行时的,大家只有运行时轻重的区别。
以上内容来源于网络知识总结,如有侵权请私信联系立即删除:)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。