Python 代码如何被 CPU 执行
Python 代码执行的起点
想运行任何 Python 代码,我们会在终端中输入 python xxx,似乎这里是一个可执行文件。
- 在 Windows 中,我们可以找到一个
python.exe,在 Linux 或 Mac 中,是 ELF(Executable and Linkable Format)格式的二进制文件,总之就是一个 CPU 能直接运行的可执行文件,主要内容是机器码。
关于 exe,PE,ELF,可执行文件,机器码 一系列概念PE(Portable Executable)指的是文件的内部格式,类似一个规范,很多文件后缀对应的文件,其实都用 PE 格式进行内部的字节排列,比如
.exe .dll .sys等等。至于为什么要有这么多不同的文件后缀,可以查阅相关资料。机器码:01010,是汇编语言的二进制翻译,是操作 CPU 的最底层数据流。可执行文件 = 机器码 + 头部信息 + 数据区 + 资源。用炒菜来类比可执行文件运行的过程,当我们双击
.exe时,操作系统是先读取可执行文件的头部(说明书),进行内存空间分配(分配锅灶)和其他操作,然后按照说明书把机器码(饭菜)倒进内存(锅)里,最后启动进程(开始炒菜)。
.dll 的作用与 Python 解释器的定义
那为什么这个 python.exe 可以执行我们写的复杂 Python 代码呢,毕竟这是一个极小的可执行文件(KB 级)。
- 原因在于,干活的是
.dll文件,它是一个编译器,而python.exe只是一个启动器,一个入口。 .dll会将 Python 代码进行词法语法解析,然后编译为字节码,再交给内部的 Python 虚拟机(PVM,用 C 语言写的,编译进.dll内的一段机器码,一个很臭的大循环)。
PVM 根据这个字节码,找到相应的 C 语言函数(CPython),这个 C 函数也已经预先编译为了机器码,放在 .dll 里面,所以其实 PVM 就是根据字节码,找对应的机器码(某种快速映射关系),然后再调用 CPU,也就是说,环境里不需要 gcc 等 C 的编译器,就能执行 .py 代码。
为解释器下个定义 结合上两节,我们可以下个定义,Python 解释器 = 一个轻量级的入口程序 + 一个包含编译器和虚拟机的运行时核心库。
Python 解释器所在的文件结构
来看这个 cpython3.11 的文件结构。
- 核心的
python311.dll和python.exe紧挨在一起,python3.dll是一个 python3 的版本转发器。 DLLs/,官方提供的、为了追求速度用 C 语言写的标准库扩展,里面是.pyd和.dll,前者本质其实就是.dll。Lib/存放标准模块,.py文件,其中专门有个子文件夹 site-packages 用来存放第三方库,比如 numpy。libs/静态链接库,.lib文件。Include/通常没啥用,一些.h文件,是对外暴露的 API 接口定义,用于拓展开发。Scripts/命令行工具。
真正重要的文件 python311.dll, python.exe, Lib/DLLs/
Lib 中的 .py 有时会调用一些函数,这些函数已经被翻译为机器码,放在 Dlls/,这样就省去了复杂的翻译开销。 同个版本,不同硬件架构的 python,Dlls 里放的机器码也不同,但是 Lib 文件是相同的。

