[Python源码学习]之Py_InitializeEx

本文深入解析Python初始化过程,包括创建进程状态、线程状态、内置模块与sys模块的初始化,以及设置模块搜索路径、处理__main__模块、初始化第三方模块路径等关键步骤。

Py_InitializeEx进行python的初始化工作。多数东西都不懂,简单记录一下,备忘。

进程状态

首先创建:

  • 进程状态PyInterpreterState对象 interp

  • 线程状态PyThreadState对象 tstate

当前线程状态对象存于一个static变量中,可以通过PyThreadState_Get()获取。通过线程状态对象进而可以获取进程状态对象。

  • interp->modules 保存所有模块

  • interp->sysdict 对应sys模块的md_dict

  • interp->builtins 对应builtins模块的md_dict

typedef struct _is {

    struct _is *next;
    struct _ts *tstate_head;

    PyObject *modules;
    PyObject *modules_by_index;
    PyObject *sysdict;
    PyObject *builtins;
    PyObject *modules_reloading;

    PyObject *codec_search_path;
    PyObject *codec_search_cache;
    PyObject *codec_error_registry;
    int codecs_initialized;
    int fscodec_initialized;

    int dlopenflags;
    int tscdump;

} PyInterpreterState;

builtins模块

buildtins模块中:

内置类型

    SETBUILTIN("None",                  Py_None);
    SETBUILTIN("Ellipsis",              Py_Ellipsis);
    SETBUILTIN("NotImplemented",        Py_NotImplemented);
    SETBUILTIN("False",                 Py_False);
    SETBUILTIN("True",                  Py_True);
    SETBUILTIN("bool",                  &PyBool_Type);
    SETBUILTIN("memoryview",        &PyMemoryView_Type);
    SETBUILTIN("bytearray",             &PyByteArray_Type);
    SETBUILTIN("bytes",                 &PyBytes_Type);
    SETBUILTIN("classmethod",           &PyClassMethod_Type);
...

以及内置函数

    {"__import__",      (PyCFunction)builtin___import__, METH_VARARGS | METH_KEYWORDS, import_doc},
    {"abs",             builtin_abs,        METH_O, abs_doc},
    {"all",             builtin_all,        METH_O, all_doc},
    {"any",             builtin_any,        METH_O, any_doc},
    {"ascii",           builtin_ascii,      METH_O, ascii_doc},
    {"bin",             builtin_bin,        METH_O, bin_doc},
    {"callable",        builtin_callable,   METH_O, callable_doc},
    {"chr",             builtin_chr,        METH_VARARGS, chr_doc},
...

设置模块搜索路径

PySys_SetPath(Py_GetPath());设置模块的搜索路径,即:sys.path

重点在 Py_GetPath()

wchar_t *
Py_GetPath(void)
{
    if (!module_search_path)
        calculate_path();
    return module_search_path;
}

如果已经使用Py_SetPath()设置了搜索路径,将返回该路径;

否则,将按照默认规则查找路径(见 Modules/getpath.c 中的注释)。

__main__模块

初始化__main__模块,并将 builtins 模块以名字__builtins__加入:

static void
initmain(void)
{
    PyObject *m, *d;
    m = PyImport_AddModule("__main__");
    d = PyModule_GetDict(m);
    if (PyDict_GetItemString(d, "__builtins__") == NULL) {
        PyObject *bimod = PyImport_ImportModule("builtins");
        PyDict_SetItemString(d, "__builtins__", bimod);
        Py_DECREF(bimod);
    }
}

恩,有些晕,看两行代码:

>>> __name__
'__main__'
>>> __builtins__.__name__
'builtins'

site.py

通过 initsite() 来初始化第三方模块的路径,它是通过导入site.py 模块实现的。

  • 将site-packages 路径加入到 sys.path
  • 处理site-packages路径下的xx.pth文件,将其指定的路径加入到 sys.path

module

pure Python module

extension module

package

包含有__init__.py的文件夹

root package

不含__init__.py的文件夹,需要加入sys.path

Py_InitializeEx源码

void

Py_InitializeEx(intinstall_sigs)

{

PyInterpreterState*interp;

指针:进程状态、线程状态、内置模块、sys模块、标准出错

PyThreadState*tstate;

PyObject*bimod,*sysmod,*pstderr;

if(initialized)

标记是否已经初始化,可以使用 Py_IsInitialized()获取

return;

initialized=1;

interp=PyInterpreterState_New();

创建进程状态、线程状态对象。当前线程状态存于全局变量 _PyThreadState_Current,可通过PyThreadState_Get()等获取

tstate=PyThreadState_New(interp);

(void)PyThreadState_Swap(tstate);

_PyEval_FiniThreads();

多线程环境初始化

_PyGILState_Init(interp,tstate);

_Py_ReadyTypes();

内置类型等 初始化

_PyFrame_Init();

_PyLong_Init();

PyByteArray_Init();

_PyFloat_Init();

_PyUnicode_Init();

interp->modules=PyDict_New();

将保存所有的模块对象到变量interp->modules

interp->modules_reloading=PyDict_New();

bimod=_PyBuiltin_Init();

builtins模块的初始化,其md_dict存入interp->builtins

_PyImport_FixupBuiltin(bimod,"builtins");

interp->builtins=PyModule_GetDict(bimod);

Py_INCREF(interp->builtins);

_PyExc_Init();

内置异常初始化

sysmod=_PySys_Init();

sys模块的初始化,其md_dict存入interp->sysdict

interp->sysdict=PyModule_GetDict(sysmod);

Py_INCREF(interp->sysdict);

_PyImport_FixupBuiltin(sysmod,"sys");

PySys_SetPath(Py_GetPath());

设置module的搜索路径

PyDict_SetItemString(interp->sysdict,"modules",

interp->modules);

pstderr=PyFile_NewStdPrinter(fileno(stderr));

标准出错

PySys_SetObject("stderr",pstderr);

PySys_SetObject("__stderr__",pstderr);

Py_DECREF(pstderr);

_PyImport_Init();

_PyImportHooks_Init();

_PyWarnings_Init();

_PyTime_Init();

initfsencoding(interp);

initsigs();

initmain();/*Module__main__*/

初始化__main__模块

initstdio();

initsite();/*Modulesite*/

初始化site模块的路径

}

参考

  • Python源码剖析,陈儒

### Py_InitializeExPy_FinalizeEx 在多线程环境中的问题 在 Python 的嵌入式环境中,`Py_InitializeEx` 和 `Py_FinalizeEx` 是用于初始化和终止 Python 解释器的函数。然而,在多线程场景中使用这些函数可能会导致崩溃或未定义行为[^1]。 #### 原因分析 1. **全局解释器锁(GIL)的影响** Python 的 GIL 确保了同一时间只有一个线程可以执行 Python 字节码。然而,当多个线程同时调用 `Py_InitializeEx` 或 `Py_FinalizeEx` 时,可能会出现竞争条件,导致内存管理混乱或数据不一致[^2]。 2. **资源冲突** `Py_InitializeEx` 和 `Py_FinalizeEx` 涉及到全局状态的初始化和清理。如果一个线程正在初始化 Python 解释器,而另一个线程尝试终止它,这将导致不可预测的行为,例如段错误或程序崩溃[^3]。 3. **线程安全问题** Python 的 C API 并不是完全线程安全的。虽然某些操作可以通过 GIL 来保护,但解释器的初始化和终止过程涉及到更复杂的全局状态管理,这超出了 GIL 的保护范围[^4]。 #### 解决方案 以下是几种可能的解决方法: 1. **确保单线程调用** 最简单的解决方案是确保 `Py_InitializeEx` 和 `Py_FinalizeEx` 只在一个线程中被调用。其他线程不应直接参与初始化或终止操作[^5]。 2. **手动同步机制** 使用互斥锁(mutex)或其他同步机制来保护对 `Py_InitializeEx` 和 `Py_FinalizeEx` 的访问。例如: ```c pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; void thread_safe_initialize(int initialize_threads) { pthread_mutex_lock(&init_mutex); Py_InitializeEx(initialize_threads); pthread_mutex_unlock(&init_mutex); } void thread_safe_finalize() { pthread_mutex_lock(&init_mutex); Py_FinalizeEx(); pthread_mutex_unlock(&init_mutex); } ``` 3. **延迟初始化和终止** 如果可能,尽量避免在多线程环境中频繁调用 `Py_InitializeEx` 和 `Py_FinalizeEx`。可以在程序启动时完成初始化,并在程序退出时进行终止[^6]。 4. **使用子解释器** Python 支持创建多个子解释器(通过 `Py_NewInterpreter`),每个子解释器都有独立的状态。这种方法可以减少全局状态的竞争,从而降低崩溃的风险[^7]。 #### 示例代码 以下是一个简单的示例,展示如何通过互斥锁保护 `Py_InitializeEx` 和 `Py_FinalizeEx` 的调用: ```c #include <pthread.h> #include <Python.h> pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; void* thread_function(void* arg) { pthread_mutex_lock(&init_mutex); Py_InitializeEx(1); // 初始化解释器 PyRun_SimpleString("print('Hello from thread')"); // 执行 Python 代码 Py_FinalizeEx(); // 终止解释器 pthread_mutex_unlock(&init_mutex); return NULL; } int main() { pthread_t thread1, thread2; pthread_create(&thread1, NULL, thread_function, NULL); pthread_create(&thread2, NULL, thread_function, NULL); pthread_join(thread1, NULL); pthread_join(thread2, NULL); return 0; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值