起步
前面虚拟机的行为分析大半. 为了更进一步了解整个python运行时的行为. 从起点开始, python程序被执行, 一步一步的跟随python的踪迹. 真正有意义的初始化从 _Py_InitializeCore
开始的.
线程模型
在对Python初始化介绍之前, 需要先知道python的运行模式, 或者说线程模型. 在前面将指令调用函数有稍微提到这个线程模型. 对此,要重新认识一下线程:
[pystate.h]
typedef struct _is {
struct _is *next;
struct _ts *tstate_head;// 模拟进程环境中的线程集合
int64_t id;
PyObject *modules;
PyObject *modules_by_index;
PyObject *sysdict;
PyObject *builtins;
PyObject *importlib;
...
} PyInterpreterState;
typedef struct _ts {
struct _ts *prev;
struct _ts *next;
PyInterpreterState *interp;
struct _frame *frame;// 帧栈对象, 模拟线程中函数调用堆栈
...
}PyThreadState;
PyInterpreterState
结构是对进程的模拟, 而 PyThreadState
是对线程的模拟, 这个结构组成了用来运行字节码的巨大的for和switc的:
for(;;) {
...
switch(opcode) {
...
}
...
}
以及进程, 线程, 栈帧布局大致关系:
进程->next 进程 -> next 进程
\
线程->next 线程 -> next 线程
| |
栈帧(frame) ...
|
f_back
|
f_back
|
...
线程环境初始化
当执行一个可执行文件时, 操作系统首先会创建一个进程内核对象, 同样, python模拟的进程也要, 通过 PyInterpreterState_New
来创建一个 PyInterpreterState
对象:
[pystate.c]
static PyInterpreterState *interp_head = NULL;
static int64_t _next_interp_id = -1;
PyInterpreterState * PyInterpreterState_New(void)
{
PyInterpreterState *interp = (PyInterpreterState *) PyMem_RawMalloc(sizeof(PyInterpreterState));
if (interp != NULL) {
HEAD_INIT();
interp->modules = NULL;
interp->modules_by_index = NULL;
interp->sysdict = NULL;
... // 域初始很多null
HEAD_LOCK();
interp->next = interp_head;
if (interp_main == NULL) {
interp_main = interp;
}
interp_head = interp;
if (_next_interp_id < 0) {
PyErr_SetString(PyExc_RuntimeError,
"failed to get an interpreter ID");
interp = NULL;
} else {
interp->id = _next_interp_id;
_next_interp_id += 1;
}
HEAD_UNLOCK();
}
return interp;
}
在Python的运行时环境中, 创建的进程对象会通过 next
域形成一个链表, 表头就是 interp_head
. _next_interp_id
是进程的一个编号, 依次递增. 得到进程对象后还需要创建一个全新的线程对象, 一个进程至少包含一个线程:
[pystate.c]
PyThreadState * PyThreadState_New(PyInterpreterState *interp)
{
return new_threadstate(interp, 1);
}
static PyThreadState * new_threadstate(PyInterpreterState *interp, int init)
{
PyThreadState *tstate = (PyThreadState *)PyMem_RawMalloc(sizeof(PyThreadState));
if (_PyThreadState_GetFrame == NULL) // 设置获得线程中函数调用栈的操作
_PyThreadState_GetFrame = threadstate_getframe;
if (tstate != NULL) { // 在线程对象中关联进程对象
tstate->interp = interp;
tstate->frame = NULL;
tstate->recursion_depth = 0;
tstate->overflowed = 0;
... // 域初始很多null
if (init)
_PyThreadState_Init(tstate);
HEAD_LOCK();
tstate->prev = NULL;
tstate->next = interp->tstate_head;
if (tstate->next)
tstate->next->prev = tstate;
interp->tstate_head = tstate; // 在进程对象中关联线程
HEAD_UNLOCK();
}
return tstate;
}
可以看出在 PyThreadState
也有 next
指针, 线程的next指针都设为了关联的进程对象的 tstate_head
, 而tstate_head
是进程对象中用来存放线程的列表, 这部分和多线程有关. 进程对象和线程对象都彼此建立了关系, 使得在他们之间穿梭变得很容易.
在python运行环境中, 有一个全局变量 _PyThreadState_Current
,这个变量用来维护当前活动的线程. 初始时为NULL:
[pystate.c]
_Py_atomic_address _PyThreadState_Current = {0};
[pyatomic.h]
typedef struct _Py_atomic_address {
atomic_uintptr_t _value;
} _Py_atomic_address;
在 _Py_atomic_address
类型中声明了 _value
是 原子变量
. 原子变量的操作是使用原子API进行的. 所谓的 原子操作
是指该操作绝不会在执行完毕前被任何其他任务或事件打断. 也就是说它是最小的执行单位, 没有比它更小的了. 所以在同时执行多个线程的情境中, 当一个线程对一个原子类型的对象执行原子操作时,其他线程不能访问该对象。 对原子变量的操作都要通过系统提供的API来进行:
[pystate.c]
#define GET_TSTATE() \
((PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current))
#define SET_TSTATE(value) \
_Py_atomic_store_relaxed(&_PyThreadState_Current, (uintptr_t)(value))
// 对于赋值语句的宏进行展开
[pyatomic.h]
#define _Py_atomic_store_relaxed(ATOMIC_VAL, NEW_VAL) \
_Py_atomic_store_explicit(ATOMIC_VAL, NEW_VAL, _Py_memory_order_relaxed)
#define _Py_atomic_store_explicit(ATOMIC_VAL, NEW_VAL, ORDER) \
atomic_store_explicit(&(ATOMIC_VAL)->_value, NEW_VAL, ORDER)
atomic_store_explicit
就是在系统头文件 stdatomic.h
定义的API. 关于 _PyThreadState_Current
的修改可以说是线程安全的.
在python启动后的第一个 PyThreadState
对象后, 会调用 PyThreadState_Swap
来设置这个变量:
[pystate.c]
PyThreadState * PyThreadState_Swap(PyThreadState *newts)
{
PyThreadState *oldts = GET_TSTATE();
SET_TSTATE(newts);
return oldts;
}
接着, python的初始化动作开始类型系统的初始化, 接下来的动作是全局变量 __builtins__
, 这个变量在创建新的 PyFrameObject
对象时发挥作用.
在此之后就是一些其他边边角角的杂碎的动作, 如 _PyLong_Init
里创建小整数池等. 而后还需要进入一个环节:设置系统module
系统 module 初始化
module的初始化简单的两句话完成:
interp->modules = PyDict_New();
bimod = _PyBuiltin_Init();
内建对象主要由 _PyBuiltin_Init
来完成, 在调用它之前 interp->modules
先创建一个dict对象, 这个对象将维护系统所有module, 这也是所有线程对象所共享的资源:
[bltinmodule.c]
PyObject * _PyBuiltin_Init(void)
{
PyObject *mod, *dict, *debug;
mod = PyModule_Create(&builtinsmodule);
if (mod == NULL)
return NULL;
dict = PyModule_GetDict(mod);
// 将所有内建类型加入到 __builtins__ 中
#define SETBUILTIN(NAME, OBJECT) \
if (PyDict_SetItemString(dict, NAME, (PyObject *)OBJECT) < 0) \
return NULL;
SETBUILTIN("None", Py_None);
SETBUILTIN("False", Py_False);
SETBUILTIN("True", Py_True);
SETBUILTIN("bool", &PyBool_Type);
SETBUILTIN("bytes", &PyBytes_Type);
SETBUILTIN("classmethod", &PyClassMethod_Type);
SETBUILTIN("complex", &PyComplex_Type);
SETBUILTIN("dict", &PyDict_Type);
SETBUILTIN("enumerate", &PyEnum_Type);
SETBUILTIN("filter", &PyFilter_Type);
SETBUILTIN("float", &PyFloat_Type);
SETBUILTIN("frozenset", &PyFrozenSet_Type);
SETBUILTIN("property", &PyProperty_Type);
SETBUILTIN("int", &PyLong_Type);
SETBUILTIN("list", &PyList_Type);
SETBUILTIN("map", &PyMap_Type);
SETBUILTIN("str", &PyUnicode_Type);
...
return mod;
}
_PyBuiltin_Init
的功能就是设置好 __builtins__
这个module. 模块的申请由 PyModule_Create
完成:
[modsupport.h]
#define PyModule_Create(module) \
PyModule_Create2(module, PYTHON_API_VERSION)
[moduleobject.c]
PyObject * PyModule_Create2(struct PyModuleDef* module, int module_api_version)
{
const char* name;
PyModuleObject *m;
...
name = module->m_name;
m = (PyModuleObject*)PyModule_New(name);
if (module->m_size > 0) {
m->md_state = PyMem_MALLOC(module->m_size);
memset(m->md_state, 0, module->m_size);
}
if (module->m_methods != NULL) {
// 遍历methods指定module对象中应包含的操作集合
if (PyModule_AddFunctions((PyObject *) m, module->m_methods) != 0) {
Py_DECREF(m);
return NULL;
}
}
if (module->m_doc != NULL) { // 设置文档说明
if (PyModule_SetDocString((PyObject *) m, module->m_doc) != 0) {
Py_DECREF(m);
return NULL;
}
}
m->md_def = module;
return (PyObject*)m;
}
这部分做了两件事, 一个是创建module对象, 一个是将方法放置到刚创建的module中.
创建对象
module对象通过 PyModule_New
函数创建, 参数是模块名:
[moduleobject.c]
typedef struct {
PyObject_HEAD
PyObject *md_dict;
struct PyModuleDef *md_def;
void *md_state;
PyObject *md_weaklist;
PyObject *md_name; /* for logging purposes after md_dict is cleared */
} PyModuleObject;
PyObject * PyModule_New(const char *name)
{
PyObject *nameobj, *module;
nameobj = PyUnicode_FromString(name);
if (nameobj == NULL)
return NULL;
module = PyModule_NewObject(nameobj);
Py_DECREF(nameobj);
return module;
}
PyObject * PyModule_NewObject(PyObject *name)
{
PyModuleObject *m;
m = PyObject_GC_New(PyModuleObject, &PyModule_Type);
if (m == NULL)
return NULL;
m->md_def = NULL;
m->md_state = NULL;
m->md_weaklist = NULL;
m->md_name = NULL;
m->md_dict = PyDict_New();
if (module_init_dict(m, m->md_dict, name, NULL) != 0)
goto fail;
PyObject_GC_Track(m);
return (PyObject *)m;
fail:
Py_DECREF(m);
return NULL;
}
可见, PyModule_NewObject
才是幕后创建模块对象的英雄, 创建后 m->md_dict
是个字典, 需要进行一些基础的设置 module_init_dict
:
[moduleobject.c]
static int module_init_dict(PyModuleObject *mod, PyObject *md_dict, PyObject *name, PyObject *doc)
{
_Py_IDENTIFIER(__name__);
_Py_IDENTIFIER(__doc__);
_Py_IDENTIFIER(__package__);
_Py_IDENTIFIER(__loader__);
_Py_IDENTIFIER(__spec__);
if (md_dict == NULL)
return -1;
if (doc == NULL)
doc = Py_None;
if (_PyDict_SetItemId(md_dict, &PyId___name__, name) != 0)
return -1;
if (_PyDict_SetItemId(md_dict, &PyId___doc__, doc) != 0)
return -1;
if (_PyDict_SetItemId(md_dict, &PyId___package__, Py_None) != 0)
return -1;
if (_PyDict_SetItemId(md_dict, &PyId___loader__, Py_None) != 0)
return -1;
if (_PyDict_SetItemId(md_dict, &PyId___spec__, Py_None) != 0)
return -1;
if (PyUnicode_CheckExact(name)) {
Py_INCREF(name);
Py_XSETREF(mod->md_name, name);
}
return 0;
}
也可以这么说, PyModuleObject
是对 PyDictObject
的包装. 所以, 在 PyModule_New
创建模块对象时, 虽然有放入基础的 __name__
和 __doc__
, 但还是算一个空的module, 没有包含操作和数据.
设置 module 对象
创建完module对象后, 将代码回溯到函数 PyModule_Create2
中的 m = (PyModuleObject*)PyModule_New(name);
之后, dict = PyModule_GetDict(mod);
就是获取module对象中的m_dict域:
[moduleobject.c]
PyObject * PyModule_GetDict(PyObject *m)
{
PyObject *d;
if (!PyModule_Check(m)) {
PyErr_BadInternalCall();
return NULL;
}
d = ((PyModuleObject *)m) -> md_dict;
assert(d != NULL);
return d;
}
接着就是通过 PyModule_AddFunctions((PyObject *) m, module->m_methods)
将 builtin_methods
中的方法遍历, 加入到module中, 先来看看 builtin_methods
:
[methodobject.h]
struct PyMethodDef {
const char *ml_name; /* The name of the built-in function/method */
PyCFunction ml_meth; /* The C function that implements it */
int ml_flags; /* Combination of METH_xxx flags, which mostly
describe the args expected by the C func */
const char *ml_doc; /* The __doc__ attribute, or NULL */
};
typedef struct PyMethodDef PyMethodDef;
[bltinmodule.c]
static PyMethodDef builtin_methods[] = {
...
{"dir", builtin_dir, METH_VARARGS, dir_doc},
{"getattr", (PyCFunction)builtin_getattr, METH_FASTCALL, getattr_doc},
{"print", (PyCFunction)builtin_print, METH_FASTCALL | METH_KEYWORDS, print_doc},
...
{NULL, NULL},
}
这些都是python的内置函数, 对于每个 PyMethodDef
结构, python都会为它创建一个 PyCFunctionObject
对象, 这个对象是对函数指针的包装:
[methodobject.h]
typedef struct {
PyObject_HEAD
PyMethodDef *m_ml; /* Description of the C function to call */
PyObject *m_self; /* Passed as 'self' arg to the C func, can be NULL */
PyObject *m_module; /* The __module__ attribute, can be anything */
PyObject *m_weakreflist; /* List of weak references */
} PyCFunctionObject;
[methodobject.c]
PyObject * PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module)
{
PyCFunctionObject *op;
op = free_list;
if (op != NULL) {
free_list = (PyCFunctionObject *)(op->m_self);
(void)PyObject_INIT(op, &PyCFunction_Type);
numfree--;
}
else {
op = PyObject_GC_New(PyCFunctionObject, &PyCFunction_Type);
if (op == NULL)
return NULL;
}
op->m_weakreflist = NULL;
op->m_ml = ml;
Py_XINCREF(self);
op->m_self = self;
Py_XINCREF(module);
op->m_module = module;
_PyObject_GC_TRACK(op);
return (PyObject *)op;
}
free_list
看得出来这边也使用了缓冲池策略. 参数中的 self
也就是调用时传入的module, func = PyCFunction_NewEx(fdef, (PyObject*)module, name);
, 差点被参数列表搞混了. 也就是说每个PyCFunctionObject对象的域 m_self
都指向自己所属的module. 而 m_module
存放的则是一个PyUnicodeObject对象, 这个则是对应这PyModuleObject对象的名字.
小结一下, 初始化函数 _Py_InitializeCore
数里面, 先创建一个进程, 一个线程, 并进行关联; 对类型系统进行初始化主要调用 _Py_ReadyTypes
进行各个类型的Ready操作. 调用每个类型中的初始函数 *_Init()
; 为进程创建dict对象用来存放modules; 初始化内建函数和对象 __builtins__
:
[pylifecycle.c]
void _Py_InitializeCore(const _PyCoreConfig *config)
{
... // 一些环境设置
_PyInterpreterState_Init();
interp = PyInterpreterState_New(); // 创建进程对象
tstate = PyThreadState_New(interp); // 创建线程对象
(void) PyThreadState_Swap(tstate); // 设置当前活动线程指针
_Py_ReadyTypes(); // 类型系统初始化
// 各个类型的init函数
if (!_PyFrame_Init())
Py_FatalError("Py_InitializeCore: can't init frames");
if (!_PyLong_Init())
Py_FatalError("Py_InitializeCore: can't init longs");
...
interp->modules = PyDict_New();
bimod = _PyBuiltin_Init(); // 设置内建函数和对象
interp->builtins = PyModule_GetDict(bimod);
.... // 后续动作
}
后续的动作也是很重要的, 设置完 interp->builtins
,照猫画虎的设置 interp->sysdict
.
创建 sys module
[pylifecycle.c]
void _Py_InitializeCore(const _PyCoreConfig *config)
{
...
interp->builtins = PyModule_GetDict(bimod);
// sys模块
sysmod = _PySys_BeginInit();
interp->sysdict = PyModule_GetDict(sysmod);
_PyImport_FixupBuiltin(sysmod, "sys"); // 备份sys模块
PyDict_SetItemString(interp->sysdict, "modules", interp->modules);
...
}
_PySys_BeginInit
函数中主要是对 sys
模块中加入python的基本信息, 如python版本, copyright, api版本等.在完成对 __builtin__
和 sys
两个module设置后, 他们在内存中的情形如图.
[示意图]
图中sys和builtin对象以虚线引出的 PyDictObject
对象表示PyModuleObject内部维护的字典对象,即md_dict. 从前面的分析可以看到 interp->builtins
和 interp->sysdict
指向的确实是PyDictObject对象, 而不是PyModuleObject对象.
对于扩展的模块, 为了避免对sys再进行初始化, python会将所有扩展module通过一个全局的PyDIctObject对象来进行维护.备份sys的动作中 _PyImport_FixupExtensionObject
完成:
[import.c]
int _PyImport_FixupBuiltin(PyObject *mod, const char *name)
{
int res;
PyObject *nameobj;
nameobj = PyUnicode_InternFromString(name);
if (nameobj == NULL)
return -1;
res = _PyImport_FixupExtensionObject(mod, nameobj, nameobj);
Py_DECREF(nameobj);
return res;
}
备份的实际动作是 _PyImport_FixupExtensionObject
:
[import.c]
static PyObject *extensions = NULL; // 全局变量, 一个dict对象
int _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, PyObject *filename)
{
PyObject *modules, *dict, *key;
struct PyModuleDef *def;
int res;
if (extensions == NULL) { // 如果extensions为空, 创建dict对象
extensions = PyDict_New();
}
def = PyModule_GetDef(mod); // 获得mod中的md_def域,是一个PyMethodDef. 这里的mod是 sysmod
modules = PyImport_GetModuleDict(); // 获得interp->modules
if (PyDict_SetItem(modules, name, mod) < 0) // 设置(name, mod)的对应关系
return -1;
if (_PyState_AddModule(mod, def) < 0) {
PyDict_DelItem(modules, name);
return -1;
}
if (def->m_size == -1) {
if (def->m_base.m_copy) {
Py_CLEAR(def->m_base.m_copy);
}
dict = PyModule_GetDict(mod);
def->m_base.m_copy = PyDict_Copy(dict); // 对dict进行拷贝
if (def->m_base.m_copy == NULL)
return -1;
}
key = PyTuple_Pack(2, filename, name);
if (key == NULL)
return -1;
res = PyDict_SetItem(extensions, key, (PyObject *)def); // 将存有拷贝的新的dict的PyMethodDef存储在extensions中
Py_DECREF(key);
if (res < 0)
return -1;
return 0;
}
python内部有个全局变量 extensions
用来存放所有已经被加载的module中的PyMethodDef. 当python谢勇的module集合中某个扩展被删除后又被重新加载时, python就不需要再次为其初始化了. 只需要用extensions中备份的PyMethodDef即可. 后文再说明.
设置 module 搜索路径
在此需要为如 import xxx
设置搜索顺序:
[pylifecycle.c]
int _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config)
{
...
PySys_SetPath(Py_GetPath());
...
}
[sysmodule.c]
void PySys_SetPath(const wchar_t *path)
{
PyObject *v;
if ((v = makepathobject(path, DELIM)) == NULL)
Py_FatalError("can't create sys.path");
if (_PySys_SetObjectId(&PyId_path, v) != 0)
Py_FatalError("can't assign sys.path");
Py_DECREF(v);
}
int _PySys_SetObjectId(_Py_Identifier *key, PyObject *v)
{
PyThreadState *tstate = PyThreadState_GET();
PyObject *sd = tstate->interp->sysdict;
if (v == NULL) {
if (_PyDict_GetItemId(sd, key) == NULL)
return 0;
else
return _PyDict_DelItemId(sd, key);
}
else
return _PyDict_SetItemId(sd, key, v);
}
省略了 makepathobject(path, DELIM)
的代码, 在它函数内, 会创建一个PyListObject对象, 这个list包含PyStringObject, 其顺序就是import模块时的搜索顺序
最终这个表示路径搜索顺序的list对呗放置到 interp->sysdict
中, 这个列表就是 sys.path
看到的.
回到 _Py_InitializeCore
函数的动作中, 再其备份 sys 模块后, 设置错误输出流 stderr
. 然后初始化 import 机制. 关于import机制下一篇再说. 会有动态模块加载. 初始化import机制, 函数 _Py_InitializeCore
就算是完成工作了. 但对于python的初始化来说, 初始化还未完成. 不过也就剩一个步骤了, 就是内建异常初始化:
[pylifecycle.c]
int _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config)
{
...
if (_PySys_EndInit(interp->sysdict) < 0)
Py_FatalError("Py_InitializeMainInterpreter: can't finish initializing sys");
initexternalimport(interp);
/* initialize the faulthandler module */
if (_PyFaulthandler_Init())
Py_FatalError("Py_InitializeMainInterpreter: can't initialize faulthandler");
if (initfsencoding(interp) < 0)
Py_FatalError("Py_InitializeMainInterpreter: unable to load the file system codec");
if (config->install_signal_handlers)
initsigs(); /* Signal handling stuff, including initintr() */
if (_PyTraceMalloc_Init() < 0)
Py_FatalError("Py_InitializeMainInterpreter: can't initialize tracemalloc");
initmain(interp); /* Module __main__ */
...
_Py_Initialized = 1; // 初始化已完成
}
创建 main 模块
在python初始化接近尾声时, python将创建一个非常特殊的模块, 名为 "__main__"
的模块.
[pylifecycle.c]
static void initmain(PyInterpreterState *interp)
{
PyObject *m, *d, *loader, *ann_dict;
// 创建 __main__ 模块, 并将其插入至 interp->modules 中
m = PyImport_AddModule("__main__");
d = PyModule_GetDict(m); // 获得 __main__ 模块中的dict
ann_dict = PyDict_New();
Py_DECREF(ann_dict);
if (PyDict_GetItemString(d, "__builtins__") == NULL) {
// 获得interp->modules中的 __builtins__
PyObject *bimod = PyImport_ImportModule("builtins");
// 将 ("__builtins__", builtin module) 插入到__mian__模块中
if (PyDict_SetItemString(d, "__builtins__", bimod) < 0) {
Py_FatalError("Failed to initialize __main__.__builtins__");
}
Py_DECREF(bimod);
}
// 设置 __loader__ 这部分讲解跳过
loader = PyDict_GetItemString(d, "__loader__");
if (loader == NULL || loader == Py_None) {
PyObject *loader = PyObject_GetAttrString(interp->importlib,
if (PyDict_SetItemString(d, "__loader__", loader) < 0) {
Py_FatalError("Failed to initialize __main__.__loader__");
}
Py_DECREF(loader);
}
}
这个 __main__
模块,就是我们经常写的:
if __name__ == "__main__":
pass
这样的单元测试机制. 在 PyImport_AddModule("__main__")
中, 创建一个名为 main 的模块, 并且并将这个名称已PyUnicodeObject的形式赋值给模块中dict的 __name__
. 这个模块作为主程序运行的python源文件被视为主模块.
当以 python xxx.py
的方式运行时, python在沿着名字空间查找 __name__
时, 就会最终在 main 模块中发现name了. 而如果一个py文件是以import方式加载的, 那么该文件里的name不会是"main".
你现在是不是有疑惑, 根据变量搜索的LGB规则, 在builtin内建中是有name的, 为什么不会找到这里面的而是去main模块中找呢.
>>> getattr(__builtins__, '__name__')
'builtins'
这个的答案在名字空间初始化中.
设置 site-packages
一般来说, 第三方模块放在 %PythonHome%\Lib\site-packages
目录下, Python初始化过程中也有 PySys_SetPath(Py_GetPath())
来设置模块的搜索路径, 但遗憾的事, site-packages
并不在其中.
设置这个路径到module的搜索路径中是 %PythonHome%\Lib\site.py
.
[pylifecycle.c]
int _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config)
{
...
initmain(interp); /* Module __main__ */
if (!Py_NoSiteFlag)
initsite(); /* Module site */
}
static void initsite(void)
{
PyObject *m;
m = PyImport_ImportModule("site");
if (m == NULL) {
fprintf(stderr, "Failed to import the site module\n");
PyErr_Print();
Py_Finalize();
exit(1);
}
else {
Py_DECREF(m);
}
}
在 initsite()
函数中, 只是调用了 PyImport_ImportModule
函数, 这个函数也就是python的import机制的核心所在.
激活虚拟机
在这步之前, python已经完成了执行程序所必须的基础设施, 通常我们以 python xxx.py
方式来启动, 在初始化 _Py_InitializeCore
函数后调用 run_file
最终通过 PyRun_AnyFileExFlags
:
[Modules/main.c]
int Py_Main(int argc, wchar_t **argv)
{
...
FILE *fp = stdin; // 默认以stdin作为输入流
_Py_InitializeCore(&core_config);
...
_Py_ReadMainInterpreterConfig(&config);
_Py_InitializeMainInterpreter(&config);
...
if (sts==-1 && cmdline.filename != NULL) { // 如果有指定py脚本
fp = _Py_wfopen(cmdline.filename, L"r");
}
if (sts == -1)
sts = run_file(fp, cmdline.filename, &cf);
...
return sts;
}
static int run_file(FILE *fp, const wchar_t *filename, PyCompilerFlags *p_cf)
{
PyObject *unicode, *bytes = NULL;
char *filename_str;
int run;
...
if (filename) {
unicode = PyUnicode_FromWideChar(filename, wcslen(filename));
...
}
else
filename_str = "<stdin>";
run = PyRun_AnyFileExFlags(fp, filename_str, filename != NULL, p_cf);
Py_XDECREF(bytes);
return run != 0;
}
当python指令后面没有跟上py文件名的时候, 文件名会默认设置为 filename_str = "<stdin>";
, 会为PyRun_AnyFileExFlags
传入, 这意思是以系统标准输入流stdin.
[pythonrun.c]
int PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit, PyCompilerFlags *flags)
{
if (filename == NULL)
filename = "???";
// 根据fp是否交互环境还是py脚本
if (Py_FdIsInteractive(fp, filename)) {
int err = PyRun_InteractiveLoopFlags(fp, filename, flags);
if (closeit)
fclose(fp);
return err;
}
else
return PyRun_SimpleFileExFlags(fp, filename, closeit, flags);
}
python有两种启动模式, 一种是 python xxx.py
运行python脚本, 一种是单单 python
命令进入交互模式. 这个判断采用 Py_FdIsInteractive(fp, filename)
来进行区分. 如果是交互式, 那它就进入 PyRun_InteractiveLoopFlags
来等待下一个输入.
交互式运行方式
[pythonrun.c]
int PyRun_InteractiveLoopFlags(FILE *fp, const char *filename_str, PyCompilerFlags *flags)
{
PyObject *filename, *v;
int ret, err;
PyCompilerFlags local_flags;
filename = PyUnicode_DecodeFSDefault(filename_str);
// 创建交互式提示符 >>>
v = _PySys_GetObjectId(&PyId_ps1);
if (v == NULL) {
_PySys_SetObjectId(&PyId_ps1, v = PyUnicode_FromString(">>> "));
Py_XDECREF(v);
}
// 创建交互式提示符 ...
v = _PySys_GetObjectId(&PyId_ps2);
if (v == NULL) {
_PySys_SetObjectId(&PyId_ps2, v = PyUnicode_FromString("... "));
Py_XDECREF(v);
}
err = -1;
// 进入交互式环境
for (;;) {
ret = PyRun_InteractiveOneObject(fp, filename, flags);
_PY_DEBUG_PRINT_TOTAL_REFS();
if (ret == E_EOF) {
err = 0;
break;
}
}
Py_DECREF(filename);
return err;
}
int PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags)
{
PyObject *m, *d, *v, *w, *oenc = NULL, *mod_name;
mod_ty mod;
PyArena *arena;
const char *ps1 = "", *ps2 = "", *enc = NULL;
int errcode = 0;
_Py_IDENTIFIER(encoding);
_Py_IDENTIFIER(__main__);
mod_name = _PyUnicode_FromId(&PyId___main__); /* borrowed */
...
v = _PySys_GetObjectId(&PyId_ps1);
w = _PySys_GetObjectId(&PyId_ps2);
// 编译交互环境中输入的语句
arena = PyArena_New();
mod = PyParser_ASTFromFileObject(fp, filename, enc,
Py_single_input, ps1, ps2,
flags, &errcode, arena);
m = PyImport_AddModuleObject(mod_name); // 获得__main__ 模块
if (m == NULL) {
PyArena_Free(arena);
return -1;
}
d = PyModule_GetDict(m);
v = run_mod(mod, filename, d, d, flags, arena); // 执行用户的python语句
PyArena_Free(arena);
if (v == NULL) {
PyErr_Print();
flush_io();
return -1;
}
Py_DECREF(v);
flush_io();
return 0;
}
这部分创建了交互环境用到的 ">>>"
和 "..."
的提示符, 调用 PyParser_ASTFromFileObject
对用户输入python语句进行编译, 其结果是结构与python语句一样的抽象语法树AST. 调用 run_mod
将最终完成对输入语句的执行. 这里的参数d就将作为当前活动的frame对象的local名字空间和global名字空间.
脚本文件运行方式
如果有执行运行的py脚本, 那么程序会调用 PyRun_SimpleFileExFlags(fp, filename, closeit, flags);
:
[compile.h]
#define Py_file_input 257
int PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
PyCompilerFlags *flags)
{
PyObject *m, *d, *v;
const char *ext;
int set_file_name = 0, ret = -1;
size_t len;
m = PyImport_AddModule("__main__");
Py_INCREF(m);
d = PyModule_GetDict(m);
if (PyDict_GetItemString(d, "__file__") == NULL) {// 设置__main__中__file__属性
PyObject *f;
f = PyUnicode_DecodeFSDefault(filename);
PyDict_SetItemString(d, "__file__", f);
set_file_name = 1;
}
len = strlen(filename);
ext = filename + len - (len > 4 ? 4 : 0);
if (maybe_pyc_file(fp, filename, ext, closeit)) {
// 如果是pyc文件
} else {
...
v = PyRun_FileExFlags(fp, filename, Py_file_input, d, d,
closeit, flags);
}
...
}
PyObject * PyRun_FileExFlags(FILE *fp, const char *filename_str, int start, PyObject *globals,
PyObject *locals, int closeit, PyCompilerFlags *flags)
{
PyObject *ret = NULL;
mod_ty mod;
PyArena *arena = NULL;
PyObject *filename;
filename = PyUnicode_DecodeFSDefault(filename_str);
arena = PyArena_New();
if (arena == NULL)
goto exit;
// 编译
mod = PyParser_ASTFromFileObject(fp, filename, NULL, start, 0, 0,
flags, NULL, arena);
if (closeit)
fclose(fp);
// 执行
ret = run_mod(mod, filename, globals, locals, flags, arena);
exit:
Py_XDECREF(filename);
if (arena != NULL)
PyArena_Free(arena);
return ret;
}
很显然, 脚本文件的执行流程和交互式方式有着相同的动作. 脚本文件一样经过编译最后进入 run_mod
. 而且一样把 main 模块中的dict作为local名字空间和global名字空间传入. 是时候看看 run_mod
里发生什么了.
启动虚拟机
从 run_mod
开始, python就剩下启动字节码虚拟机了, 待执行的python代码都已经编译过了.
[pythonrun.c]
static PyObject * run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals,
PyCompilerFlags *flags, PyArena *arena)
{
PyCodeObject *co;
PyObject *v;
co = PyAST_CompileObject(mod, filename, flags, -1, arena);
if (co == NULL)
return NULL;
v = PyEval_EvalCode((PyObject*)co, globals, locals);
Py_DECREF(co);
return v;
}
在参数含有已经处理过的AST, 从中获得PyCodeObject对象, 开始通过 PyEval_EvalCode
来唤醒字节码虚拟机.
[ceval.c]
PyObject * PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals)
{
return PyEval_EvalCodeEx(co,
globals, locals,
(PyObject **)NULL, 0,
(PyObject **)NULL, 0,
(PyObject **)NULL, 0,
NULL, NULL);
}
PyObject * PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
PyObject **args, int argcount, PyObject **kws, int kwcount,
PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure)
{
return _PyEval_EvalCodeWithName(_co, globals, locals,
args, argcount,
kws, kws != NULL ? kws + 1 : NULL,
kwcount, 2,
defs, defcount,
kwdefs, closure,
NULL, NULL);
}
PyObject *
_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
PyObject **args, Py_ssize_t argcount,
PyObject **kwnames, PyObject **kwargs,
Py_ssize_t kwcount, int kwstep,
PyObject **defs, Py_ssize_t defcount,
PyObject *kwdefs, PyObject *closure,
PyObject *name, PyObject *qualname)
{
...
retval = PyEval_EvalFrameEx(f,0);
return retval;
}
正如 <<虚拟机框架>>
那篇介绍的, PyEval_EvalFrameEx
就是推到多米诺骨牌的第一篇骨牌. 在此, python进程被创建, python字节码虚拟机被唤醒, 之后就是执行引擎循环往复的执行字节码了.
名字空间
对于主模块来说, 它的local名字空间等于global名字空间没什么问题. main 模块维护的dict就作为local的名字空间, 这也是为什么 __name__
取的不是内建 __builtins__
里的, 因为它在local名字空间就命中了.
在新创建的PyFunctionObject中设置的builtin名字空间实际上也是 __builtins__
, 这意味着python所有线程都共享builtin名字空间.