_PyRuntime_Initialize函数解析

源码位置:cpython\Python\pylifecycle.c

一._PyRuntime_Initialize

这段代码是Python解释器生命周期管理的关键部分,负责初始化Python运行时环境。该函数确保了Python解释器在启动过程中拥有正确初始化的运行时环境,这是Python能够正常工作的基础。

staticint runtime_initialized = 0;

PyStatus
_PyRuntime_Initialize(void)
{
/* XXX We only initialize once in the process, which aligns with
       the static initialization of the former globals now found in
       _PyRuntime.  However, _PyRuntime *should* be initialized with
       every Py_Initialize() call, but doing so breaks the runtime.
       This is because the runtime state is not properly finalized
       currently. */

if (runtime_initialized) {
return _PyStatus_OK();
    }
    runtime_initialized = 1;

return _PyRuntimeState_Init(&_PyRuntime);
}

1.单次初始化机制

  • 函数通过检查runtime_initialized标志确保运行时只被初始化一次

  • 如果已经初始化过,直接返回成功状态

2.初始化过程

  • 当首次调用时,设置runtime_initialized = 1

  • 然后调用_PyRuntimeState_Init(&_PyRuntime)进行实际的运行时状态初始化

3.设计约束

  • 注释中指出了一个已知问题:理想情况下,_PyRuntime应该在每次调用Py_Initialize()时重新初始化

  • 但目前这样做会导致运行时崩溃,因为运行时状态没有被正确地终结

  • 这解释了为什么函数使用静态标志确保只初始化一次

二._PyRuntimeState_Init

源码位置:cpython\Python\pystate.c

这个函数负责初始化 Python 运行时状态,是 Python 启动过程中的关键步骤。这个函数是 Python 解释器启动过程中的关键部分,确保运行时环境正确初始化,为后续的解释器操作提供基础设施。它处理了运行时状态的创建、重置和配置,特别是在多次初始化/终止 Python 解释器的场景中,保持了关键状态的连续性。

PyStatus
_PyRuntimeState_Init(_PyRuntimeState *runtime)
{
/* We preserve the hook across init, because there is
       currently no public API to set it between runtime
       initialization and interpreter initialization. */

void *open_code_hook = runtime->open_code_hook;
void *open_code_userdata = runtime->open_code_userdata;
    _Py_AuditHookEntry *audit_hook_head = runtime->audit_hooks.head;
// bpo-42882: Preserve next_index value if Py_Initialize()/Py_Finalize()
// is called multiple times.
    Py_ssize_t unicode_next_index = runtime->unicode_state.ids.next_index;

if (runtime->_initialized) {
// Py_Initialize() must be running again.
// Reset to _PyRuntimeState_INIT.
        memcpy(runtime, &initial, sizeof(*runtime));
// Preserve the cookie from the original runtime.
        memcpy(runtime->debug_offsets.cookie, _Py_Debug_Cookie, 8);
assert(!runtime->_initialized);
    }

    PyStatus status = _PyTime_Init(&runtime->time);
if (_PyStatus_EXCEPTION(status)) {
return status;
    }

if (gilstate_tss_init(runtime) != 0) {
        _PyRuntimeState_Fini(runtime);
return _PyStatus_NO_MEMORY();
    }

if (PyThread_tss_create(&runtime->trashTSSkey) != 0) {
        _PyRuntimeState_Fini(runtime);
return _PyStatus_NO_MEMORY();
    }

    init_runtime(runtime, open_code_hook, open_code_userdata, audit_hook_head,
                 unicode_next_index);

return _PyStatus_OK();
}

1.保存关键状态

首先保存几个重要的运行时组件,以便在重置运行时状态后能够恢复它们:

  • open_code_hook 和 open_code_userdata:用于处理代码文件打开的钩子

  • audit_hook_head:审计钩子链表头

  • unicode_next_index:Unicode ID 索引值(在多次调用 Py_Initialize/Py_Finalize 时需要保留)

2.重新初始化检查

  • 如果 runtime->_initialized 为真,说明 Py_Initialize() 正在再次运行

  • 此时通过 memcpy 将运行时状态重置为初始状态(_PyRuntimeState_INIT

  • 同时保留原始运行时的调试 cookie

3.时间模块初始化

  • 调用 _PyTime_Init 初始化运行时的时间组件

  • 如果初始化失败,立即返回错误状态

4.线程状态存储初始化

  • 调用 gilstate_tss_init 初始化线程特定存储,用于 GIL 状态管理

  • 如果失败,清理资源并返回内存不足错误

5.垃圾回收 TSS 初始化

  • 创建 trashTSSkey,用于线程特定的垃圾回收

  • 如果失败,清理资源并返回内存不足错误

6.完成运行时初始化

  • 调用 init_runtime 函数,传入之前保存的钩子和索引值

  • 设置运行时状态标记 _initialized

7.返回成功状态

  • 如果所有步骤都成功,返回 _PyStatus_OK()

三.init_runtime

源码位置:cpython\Python\pystate.c

这个init_runtime函数负责初始化Python解释器的运行时状态。它是Python启动过程中的关键环节,设置了各种重要的运行时参数和状态。

staticvoid
init_runtime(_PyRuntimeState *runtime,
void *open_code_hook, void *open_code_userdata,
             _Py_AuditHookEntry *audit_hook_head,
             Py_ssize_t unicode_next_index)

{
assert(!runtime->preinitializing);
assert(!runtime->preinitialized);
assert(!runtime->core_initialized);
assert(!runtime->initialized);
assert(!runtime->_initialized);

    runtime->open_code_hook = open_code_hook;
    runtime->open_code_userdata = open_code_userdata;
    runtime->audit_hooks.head = audit_hook_head;

    PyPreConfig_InitPythonConfig(&runtime->preconfig);

// Set it to the ID of the main thread of the main interpreter.
    runtime->main_thread = PyThread_get_thread_ident();

    runtime->unicode_state.ids.next_index = unicode_next_index;

#ifdefined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
_Py_EmscriptenTrampoline_Init(runtime)
;
#endif

    runtime->_initialized = 1;
}

1.状态检查

通过一系列断言确保运行时状态尚未被初始化,防止重复初始化:

assert(!runtime->preinitializing);
assert(!runtime->preinitialized);
assert(!runtime->core_initialized);
assert(!runtime->initialized);
assert(!runtime->_initialized);

2.设置代码钩子

这些钩子函数用于打开和处理Python代码文件,是实现自定义代码加载机制的关键。

runtime->open_code_hook = open_code_hook;
runtime->open_code_userdata = open_code_userdata;

3.设置审计钩子

这是Python安全审计系统的一部分,允许监控解释器的关键操作。

runtime->audit_hooks.head = audit_hook_head;

4.初始化预配置

设置默认的Python预配置选项。

PyPreConfig_InitPythonConfig(&runtime->preconfig);

5.记录主线程ID

将当前线程标记为主线程,这对后续的线程管理和GIL操作很重要。

runtime->main_thread = PyThread_get_thread_ident();

6.设置Unicode状态

初始化Unicode对象ID的下一个可用索引。

runtime->unicode_state.ids.next_index = unicode_next_index;

7.Emscripten特殊处理

为WebAssembly环境提供支持。

8.标记初始化完成

设置标志表示运行时已成功初始化。

runtime->_initialized = 1;

这个函数是Python启动过程的基础,为后续的解释器操作和线程管理奠定了基础。它处理的是底层运行时状态,而不是直接初始化Python解释器的所有部分。

参考文献

[0] _PyRuntime_Initialize函数解析:https://z0yrmerhgi8.feishu.cn/wiki/C010wBP0SiCATiklNH0csmdmn1g

[1] Python初始化配置:https://docs.python.org/zh-cn/3.14/c-api/init_config.html

[2] Initialization, Finalization, and Threads(初始化,最终化和线程):https://docs.python.org/zh-cn/3.14/c-api/init.html#


知识星球服务内容:Dify源码剖析及答疑,Dify对话系统源码,NLP电子书籍报告下载,公众号所有付费资料。加微信buxingtianxia21进NLP工程化资料群

(文:NLP工程化)

发表评论