avatar

🧊foril

avatar

🧊foril

小记常见语言实现机制

2024-11-23 -

在了解 Python 协程的实现原理的时候,对于几种语言的运行机制有了更进一步的认识。本文简单记录一下集中常见语言的运行机制,可以更好的理解比较。

几种语言的运行机制

首先明确一下几种语言类型,以 C/C++,Java,Python 和 Node.js 为例,它们的运行机制可以简单概括如下:

  1. C/C++:编译型语言,源代码通过编译器直接编译为机器码,运行时不需要解释器。
  2. Java:半编译半解释型语言,源代码编译为字节码,运行时通过 JVM 解释执行。
  3. Python:解释型语言,源代码通过解释器逐行解释执行,运行时不需要编译。
  4. Node.js:基于 V8 引擎的 JavaScript 运行时环境,源代码通过 JIT 编译为机器码,运行时通过事件循环实现异步执行。

大白话

C/C++:静态编译型语言

C/C++ 是直接通过编译器(如 GCC、Clang)转换为目标机器的二进制机器码,编译直接生成针对底层硬件生成的高效的 二进制机器码可执行文件,可以直接由操作系统运行。

Java:半编译半解释型语言

Java 是先将代码编译为字节码,然后在 真正运行时通过 JVM 解释字节码执行,各个平台有自己的 JVM 实现,所以字节码可以跨平台运行。在配合 JIT 编译以后,JVM 可以将热点代码编译为机器码,提高执行效率,在许多场景下与 C/C++ 的性能差距已经非常小了。

Python:解释型语言

Python 是一种解释型语言,以 CPython 为例,其解析器、编译器和虚拟机都是用 C 语言实现的,Python 代码在运行时会先经过解释器解析为抽象语法树(AST),然后编译为字节码(.pyc 文件),最后由 Python 虚拟机(PVM)逐条执行字节码,这种解释执行的方式导致 Python 的运行效率相对较低。虽然 Python 本身运行效率较低,但 CPython 可以通过 C 扩展库(如 NumPy、TensorFlow)调用高效的底层实现,大幅提升性能,这种机制被称为 Python C API,这使得 Python 尽管速度慢,但在数据科学和机器学习领域仍然非常强大。

C 扩展模块是共享库,编译后是 .so(Linux/macOS)或 .dll(Windows)格式的共享库,CPython 可以直接加载这些共享库,并通过 C API 调用其中的函数。
NumPy 的底层核心部分是用 C 语言实现的(比如数组操作和矩阵计算)。NumPy 通过调用高性能的 BLAS 和 LAPACK 库(用 C 或 Fortran 实现的数值计算库)来提升性能。

Node.js:基于 V8 引擎的 JavaScript 运行时环境

Node.js 是基于 Google V8 引擎的 JavaScript 运行时环境,它直接加载和运行 JavaScript 源代码,其底层运行方式结合了解释和即时编译(JIT)技术,所以它介于解释型语言和编译型语言之间。
在 V8 引擎中,JavaScript 代码首先被解析为抽象语法树(AST),然后通过解释器(Ignition)将代码转换为字节码并执行,同时 JIT 编译器(TurboFan)会动态分析运行时的热点代码,将热点字节码优化为机器码,在 CPU 上直接运行以进一步提升性能。Node.js 的事件驱动模型和非阻塞 I/O 由 libuv 提供支持,主线程通过事件循环调度任务,I/O 操作交给线程池或操作系统异步处理,从而避免阻塞,适合处理高并发的 I/O 密集型任务,如 Web 服务器、聊天应用、API 网关和实时流媒体服务。

理解虚拟机

上面多次提到了虚拟机的概念,在编程语言的语境中,虚拟机(Virtual Machine, VM) 是一个更广义的概念,指的是一种 在抽象层上模拟计算机硬件或软件运行环境的程序
CPython 的虚拟机扮演了解释器、栈操作、异常处理和上下文管理等角色,下面简单介绍一下 CPython 的虚拟机:

作用一:解释器

CPython 的虚拟机首先是一个解释器,用来逐条解释 Python 的字节码指令。字节码是一组简单的、与平台无关的操作码,例如:

x = 1 + 2

编译成的字节码可能是这样的:

LOAD_CONST 1 LOAD_CONST 2 BINARY_ADD STORE_NAME x

虚拟机会逐条执行这些指令,并在内部用 C 语言实现的操作完成任务。

作用二:模拟栈操作

虚拟机通过一个栈结构来管理字节码的执行。比如上例中,BINARY_ADD 会从栈中弹出两个值(12),执行加法操作后将结果(3)压回栈中。

作用三:异常处理和上下文管理

虚拟机负责处理 Python 的异常机制和上下文管理(如 try...exceptwith)。这些逻辑直接在 C 语言中实现。

示例:具体流程分析

下面以 Python 为例,简单分析一下 Python 代码的执行流程,假设有如下代码:

x = 10 y = 20 z = x + y print(z)

CPython 主要有如下三步执行过程:

  1. 解析(C 语言实现的解析器):脚本被解析成抽象语法树(AST)。

  2. 编译(C 语言实现的编译器):AST 被编译成字节码:

    1. LOAD_CONST 10 2. STORE_NAME x 3. LOAD_CONST 20 4. STORE_NAME y 5. LOAD_NAME x 6. LOAD_NAME y 7. BINARY_ADD 8. STORE_NAME z 9. LOAD_NAME z 10. PRINT_EXPR
  3. 执行(C 语言实现的虚拟机解释字节码):每条指令被虚拟机逐条执行:

    • LOAD_CONST:加载常量到栈。
    • STORE_NAME:将值从栈中存储到变量名。
    • BINARY_ADD:从栈中弹出两个值相加并压回栈。
    • PRINT_EXPR:弹出栈顶值并调用 C 函数 builtin_print