CPython — Python 解释器内核

Python 的”操作系统内核”——字节码解释器 + 引用计数 GC + GIL。

概念

CPython 是 Python 语言的参考实现,用 C 编写。它的”内核”是一个栈式字节码解释器。与传统操作系统相似,CPython 管理内存 (GC)、调度线程 (GIL)、提供 I/O 抽象。理解 CPython 内核等于理解”动态语言如何用静态语言实现”。

核心组件

组件职责关键源文件
PyObject所有对象的基类型: ob_refcnt + ob_typeobject.h
引用计数 GC即时回收 + 分代循环检测gcmodule.c
GIL同一时刻只有一个线程执行 Python 字节码ceval_gil.c
字节码解释器巨大的 switch-case 循环执行 200+ 条字节码ceval.c
import 机制sys.path → finder → loader → moduleimport.c
类型系统PyTypeObject 定义 tp_new, tp_call, tp_hash 等typeobject.c
内存分配器pymalloc (256 KB 以下对象) + C mallocobmalloc.c

PyObject 对象模型

// 所有 Python 对象的"基类"——纯 C 结构体
typedef struct _object {
 Py_ssize_t ob_refcnt; // 引用计数
 PyTypeObject *ob_type; // 指向类型对象的指针 (类似 vtable)
} PyObject;
 
// Python int (任意精度)
typedef struct {
 PyObject_HEAD
 int32_t *digits; // 指向 lims 数组的指针
 size_t length; // 数组长度 (以 digit 为单位)
 int sign; // 符号位
} PyLongObject;
 
// Python list
typedef struct {
 PyObject_HEAD
 Py_ssize_t ob_size;
 PyObject **ob_item; // 指向 C 指针数组的指针
 Py_ssize_t allocated; // 分配的容量
} PyListObject;

引用计数 GC

# 简化版 CPython GC 逻辑 (Python 视角)
def increment_refcount(obj):
 obj.ob_refcnt += 1
 
def decrement_refcount(obj):
 obj.ob_refcnt -= 1
 if obj.ob_refcnt == 0:
 # 立即释放内存
 obj.tp_dealloc(obj)
 # 递归减少该对象引用其他对象的计数
 for child in obj.tp_traverse():
 decrement_refcount(child)
 
# 分代 GC: 检测引用计数无法回收的循环引用
def collect_generation(gen):
 # STW (Stop The World): 暂停所有线程
 # 标记: 遍历 gen 中的容器对象
 # 清除: 回收不可达的循环引用
 # 分代: gen0 最频繁, gen1 次之, gen2 最慢
 pass

GIL (Global Interpreter Lock)

// GIL 的核心——一个互斥锁 + 条件变量
// 线程 A 持有 GIL 执行 15ms 后强制释放
// 线程 B 争抢 GIL 继续执行
 
// Python 伪代码表示字节码执行循环
while (1) {
 opcode = *next_instr++;
 
 switch (opcode) {
 case LOAD_FAST: // 加载局部变量
 PUSH(GETLOCAL(oparg));
 break;
 case BINARY_ADD: // 加法
 x = POP(); y = TOP();
 SET_TOP(PyNumber_Add(y, x));
 break;
 case CALL_FUNCTION: // 函数调用
 // 构建新栈帧, 递归调用解释器
 break;
 // ... 200+ 条字节码
 }
 
 // 每执行 100 条指令检查是否需要释放 GIL
 if (--eval_breaker == 0) {
 // 释放 GIL, 让其他线程有机会运行
 release_gil();
 acquire_gil();
 }
}

import 机制

import numpy as np
 |
 v
sys.modules["numpy"]? --Y--> 返回已缓存的 module
 |N
 v
遍历 sys.path 中的每个路径:
 对于每个 sys.meta_path 中的 finder:
 spec = finder.find_spec("numpy")
 如果找到加载器 (loader):
 module = loader.create_module(spec)
 loader.exec_module(module) # 执行字节码初始化
 sys.modules["numpy"] = module
 返回 module