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