python怎么启用运行环境_【Python】运行环境初始化

0x01 线程环境初始化

线程模型回顾

Python启动后,真正有意义的初始化动作是从Py_Initialize开始(当然Py_Initialize之前也做了很多复杂的动作),Py_Initialize中调用了Py_InitializeEx。

// python.c -- Minimal main program -- everything is loaded from the library

int

main(int argc, char **argv)

{

/* 754 requires that FP exceptions run in "no stop" mode by default,

* and until C vendors implement C99's ways to control FP exceptions,

* Python requires non-stop mode. Alas, some platforms enable FP

* exceptions by default. Here we disable them.

*/

#ifdef __FreeBSD__

fp_except_t m;

m = fpgetmask();

fpsetmask(m & ~FP_X_OFL);

#endif

return Py_Main(argc, argv);

}

// main.c -- Python interpreter main program

/* Main program */

int

Py_Main(int argc, char **argv)

{

......

Py_Initialize

......

}

//Pythonrun.c

void

Py_Initialize(void)

{

Py_InitializeEx(1);

}

void

Py_InitializeEx(int install_sigs)

{

PyInterpreterState *interp;

PyThreadState *tstate;

PyObject *bimod, *sysmod;

char *p;

extern void _Py_ReadyTypes(void);

if (initialized)

return;

initialized = 1;

if ((p = Py_GETENV("PYTHONDEBUG")) && *p != '\0')

Py_DebugFlag = add_flag(Py_DebugFlag, p);

if ((p = Py_GETENV("PYTHONVERBOSE")) && *p != '\0')

Py_VerboseFlag = add_flag(Py_VerboseFlag, p);

if ((p = Py_GETENV("PYTHONOPTIMIZE")) && *p != '\0')

Py_OptimizeFlag = add_flag(Py_OptimizeFlag, p);

// 创建进程状态

interp = PyInterpreterState_New();

if (interp == NULL)

Py_FatalError("Py_Initialize: can't make first interpreter");

// 创建线程状态

tstate = PyThreadState_New(interp);

if (tstate == NULL)

Py_FatalError("Py_Initialize: can't make first thread");

(void) PyThreadState_Swap(tstate);

// 初始化类型模型

_Py_ReadyTypes();

if (!_PyFrame_Init())

Py_FatalError("Py_Initialize: can't init frames");

if (!_PyInt_Init())

Py_FatalError("Py_Initialize: can't init ints");

_PyFloat_Init();

// 初始化modules

interp->modules = PyDict_New();

if (interp->modules == NULL)

Py_FatalError("Py_Initialize: can't make modules dictionary");

interp->modules_reloading = PyDict_New();

if (interp->modules_reloading == NULL)

Py_FatalError("Py_Initialize: can't make modules_reloading dictionary");

// 初始化__built__ module

bimod = _PyBuiltin_Init();

if (bimod == NULL)

Py_FatalError("Py_Initialize: can't initialize __builtin__");

interp->builtins = PyModule_GetDict(bimod);

if (interp->builtins == NULL)

Py_FatalError("Py_Initialize: can't initialize builtins dict");

Py_INCREF(interp->builtins);

// 初始化sys module

sysmod = _PySys_Init();

if (sysmod == NULL)

Py_FatalError("Py_Initialize: can't initialize sys");

interp->sysdict = PyModule_GetDict(sysmod);

if (interp->sysdict == NULL)

Py_FatalError("Py_Initialize: can't initialize sys dict");

Py_INCREF(interp->sysdict);

_PyImport_FixupExtension("sys", "sys");

// 设置module搜索路径集合

PySys_SetPath(Py_GetPath());

// 设置了sys.modules,他就是interp->modules

PyDict_SetItemString(interp->sysdict, "modules",

interp->modules);

// 初始化import机制的环境

_PyImport_Init();

/* initialize builtin exceptions */

// 初始化Python内建的exceptions

_PyExc_Init();

// 备份exceptions module 和 __builtin__ module

_PyImport_FixupExtension("exceptions", "exceptions");

/* phase 2 of builtins */

_PyImport_FixupExtension("__builtin__", "__builtin__");

// 在sys module中添加一些对象,用于import机制

_PyImportHooks_Init();

if (install_sigs)

initsigs(); /* Signal handling stuff, including initintr() */

initmain(); /* Module __main__ */

if (!Py_NoSiteFlag)

initsite(); /* Module site */

warnings_module = PyImport_ImportModule("warnings");

if (!warnings_module)

PyErr_Clear();

}

Py_InitializeEx中完成了一个重要工作就是加载多个基础的module(__builtin__,sys等),还会完成Python类型系统的初始化和异常系统初始化。

// pystate.h

typedef struct _is {

struct _is *next;

struct _ts *tstate_head; // 模拟进程环境中的线程集合

PyObject *modules;

PyObject *sysdict;

PyObject *builtins;

PyObject *modules_reloading;

PyObject *codec_search_path;

PyObject *codec_search_cache;

PyObject *codec_error_registry;

} PyInterpreterState;

typedef struct _ts {

struct _ts *next;

PyInterpreterState *interp;

struct _frame *frame; // 模拟线程中的函数调用堆栈

int recursion_depth;

int tracing;

int use_tracing;

Py_tracefunc c_profilefunc;

Py_tracefunc c_tracefunc;

PyObject *c_profileobj;

PyObject *c_traceobj;

PyObject *curexc_type;

PyObject *curexc_value;

PyObject *curexc_traceback;

PyObject *exc_type;

PyObject *exc_value;

PyObject *exc_traceback;

PyObject *dict; /* Stores per-thread state */

int tick_counter;

int gilstate_counter;

PyObject *async_exc; /* Asynchronous exception to raise */

long thread_id; /* Thread id where this tstate was created */

int trash_delete_nesting;

PyObject *trash_delete_later;

} PyThreadState;

某时刻Python运行时的整个环境

初始化线程环境

在win32环境,当执行一个可执行文件时,操作系统首先会创建一个进程内核对象。同样,在Python中也是如此,在Py_InitializeEx的开始处,Python会首先调用PyInterpreterState_New创建一个崭新的PyInterpreterState对象。

// pystate.c

static PyInterpreterState *interp_head = NULL;

PyInterpreterState *

PyInterpreterState_New(void)

{

PyInterpreterState *interp = (PyInterpreterState *)

malloc(sizeof(PyInterpreterState));

if (interp != NULL) {

HEAD_INIT();

#ifdef WITH_THREAD

if (head_mutex == NULL)

Py_FatalError("Can't initialize threads for interpreter");

#endif

interp->modules = NULL;

interp->modules_reloading = NULL;

interp->sysdict = NULL;

interp->builtins = NULL;

interp->tstate_head = NULL;

interp->codec_search_path = NULL;

interp->codec_search_cache = NULL;

interp->codec_error_registry = NULL;

HEAD_LOCK();

interp->next = interp_head;

interp_head = interp;

HEAD_UNLOCK();

}

return interp;

}

在Python的运行时环境中,有一个全局变量interp_head(一个PyInterpreterState链表)来管理PyInterpreterState对象,在Python运行时可能会有多个PyInterpreterState对象链接在一起(这个就是对操作系统多进程的模拟)。新创建的PyInterpreterState对象如下所示:

新创建的PyInterpreterState对象

在创建了PyInterpreterState(进程状态)对象之后,Python会调用PyThreadState_New函数来创建PyThreadState(线程状态)对象。

// pystate.c

PyThreadState *

PyThreadState_New(PyInterpreterState *interp)

{

PyThreadState *tstate = (PyThreadState *)malloc(sizeof(PyThreadState));

// 设置获得线程中函数调用栈的操作

if (_PyThreadState_GetFrame == NULL)

_PyThreadState_GetFrame = threadstate_getframe;

if (tstate != NULL) {

// 在PyThreadState对象中关联PyInterpreterState对象

tstate->interp = interp;

tstate->frame = NULL;

tstate->recursion_depth = 0;

tstate->tracing = 0;

tstate->use_tracing = 0;

tstate->tick_counter = 0;

tstate->gilstate_counter = 0;

tstate->async_exc = NULL;

tstate->dict = NULL;

tstate->curexc_type = NULL;

tstate->curexc_value = NULL;

tstate->curexc_traceback = NULL;

tstate->exc_type = NULL;

tstate->exc_value = NULL;

tstate->exc_traceback = NULL;

tstate->c_profilefunc = NULL;

tstate->c_tracefunc = NULL;

tstate->c_profileobj = NULL;

tstate->c_traceobj = NULL;

HEAD_LOCK();

tstate->next = interp->tstate_head;

// 在PyInterpreterState对象中关联PyThreadState对象

interp->tstate_head = tstate;

HEAD_UNLOCK();

}

return tstate;

}

与PyThreadState结构体中也存在一个next指针,PyThreadState在Python运行时也会创建一个链表(模拟多线程)。

还会设置从线程中获得函数调用栈(PyFrameObject对象链表)的方法(threadstate_getframe)。

然后建立PyThreadState对象和PyInterpreterState对象的关系,结果如下所示:

PyThreadState对象和PyInterpreterState对象的关系图示

在Python的运行时环境中,有一个全局变量_PyThreadState_Current(_PyThreadState_Current维护了当前活动的线程,更准确的说是当前活动线程对应的PyThreadState对象),初始化时该变量为NULL。创建完PyThreadState对象后,会通过PyThreadState_Swap函数设置_PyThreadState_Current的值。

// pystate.c

PyThreadState * PyThreadState_Swap(PyThreadState *newts)

{

PyThreadState *oldts = _PyThreadState_Current;

_PyThreadState_Current = newts;

return oldts;

}

接下来会通过_Py_ReadyTypes函数初始化Python类型系统(参考类机制剖析)。

然后通过_PyFrame_Init函数设置全局变量builtin_object。

// pystate.c

static PyObject *builtin_object;

int _PyFrame_Init()

{

builtin_object = PyString_InternFromString("__builtins__");

return (builtin_object != NULL);

}

这个内容为__builtins__的PyStringObject对象builtin_object在PyFrame_New创建一个新的PyFrameObject对象时会发挥作用。

接下来会初始化一些边边角角的东西。

至此,Py_InitializeEx有了一个阶段性结果,创建了代表进程和线程的PyInterpreterState和PyThreadState对象,并且在他们之间建立了联系。接下来会进入相对独立的环节:设置系统module。

0x02 系统module初始化

创建__builtin__ module

在Py_INitializeEx中,当Python创建了PyThreadState和PyInterpreterState对象之后,就会开始通过_PyBuiltin_Init来设置系统的__builtin__ module了。在调用_PyBuiltin_Init之前,Python会通过interp->modules创建为一个PyDictObject对象,这个对象中维护着所有的PyThreadState对象共享的资源。

// Bltinmodule.c

PyObject *

_PyBuiltin_Init(void)

{

PyObject *mod, *dict, *debug;

// 创建并设置__builtin__ module

mod = Py_InitModule4("__builtin__", builtin_methods,

builtin_doc, (PyObject *)NULL,

PYTHON_API_VERSION);

if (mod == NULL)

return NULL;

// 将所有内建类型加到__builtin__ module中

dict = PyModule_GetDict(mod);

#ifdef Py_TRACE_REFS

/* __builtin__ exposes a number of statically allocated objects

* that, before this code was added in 2.3, never showed up in

* the list of "all objects" maintained by Py_TRACE_REFS. As a

* result, programs leaking references to None and False (etc)

* couldn't be diagnosed by examining sys.getobjects(0).

*/

#define ADD_TO_ALL(OBJECT) _Py_AddToAllObjects((PyObject *)(OBJECT), 0)

#else

#define ADD_TO_ALL(OBJECT) (void)0

#endif

#define SETBUILTIN(NAME, OBJECT) \

if (PyDict_SetItemString(dict, NAME, (PyObject *)OBJECT) < 0) \

return NULL; \

ADD_TO_ALL(OBJECT)

SETBUILTIN("None", Py_None);

SETBUILTIN("Ellipsis", Py_Ellipsis);

SETBUILTIN("NotImplemented", Py_NotImplemented);

SETBUILTIN("False", Py_False);

SETBUILTIN("True", Py_True);

SETBUILTIN("basestring", &PyBaseString_Type);

SETBUILTIN("bool", &PyBool_Type);

SETBUILTIN("buffer", &PyBuffer_Type);

SETBUILTIN("classmethod", &PyClassMethod_Type);

#ifndef WITHOUT_COMPLEX

SETBUILTIN("complex", &PyComplex_Type);

#endif

SETBUILTIN("dict", &PyDict_Type);

SETBUILTIN("enumerate", &PyEnum_Type);

SETBUILTIN("file", &PyFile_Type);

SETBUILTIN("float", &PyFloat_Type);

SETBUILTIN("frozenset", &PyFrozenSet_Type);

SETBUILTIN("property", &PyProperty_Type);

SETBUILTIN("int", &PyInt_Type);

SETBUILTIN("list", &PyList_Type);

SETBUILTIN("long", &PyLong_Type);

SETBUILTIN("object", &PyBaseObject_Type);

SETBUILTIN("reversed", &PyReversed_Type);

SETBUILTIN("set", &PySet_Type);

SETBUILTIN("slice", &PySlice_Type);

SETBUILTIN("staticmethod", &PyStaticMethod_Type);

SETBUILTIN("str", &PyString_Type);

SETBUILTIN("super", &PySuper_Type);

SETBUILTIN("tuple", &PyTuple_Type);

SETBUILTIN("type", &PyType_Type);

SETBUILTIN("xrange", &PyRange_Type);

#ifdef Py_USING_UNICODE

SETBUILTIN("unicode", &PyUnicode_Type);

#endif

debug = PyBool_FromLong(Py_OptimizeFlag == 0);

if (PyDict_SetItemString(dict, "__debug__", debug) < 0) {

Py_XDECREF(debug);

return NULL;

}

Py_XDECREF(debug);

return mod;

#undef ADD_TO_ALL

#undef SETBUILTIN

}

整个函数的功能就是设置好__builtin__ module。总共分为两个步骤:

创建PyModuleObject对象,在Python中,module正是通过和这个对象来设置的

设置module,将Python中的所有的类型对象全塞进新创建的__builtin__ module中。

在第一步中已经设置好了大部分的内容,由函数Py_InitModule4实现:

// modsupport.c

PyObject *

Py_InitModule4(const char *name, PyMethodDef *methods, const char *doc,

PyObject *passthrough, int module_api_version)

{

PyObject *m, *d, *v, *n;

PyMethodDef *ml;

......

// 创建module对象

if ((m = PyImport_AddModule(name)) == NULL)

return NULL;

// 设置module中的(符号,值)对应关系

d = PyModule_GetDict(m);

if (methods != NULL) {

n = PyString_FromString(name);

if (n == NULL)

return NULL;

// 遍历methods指定的module对象中应该包含的操作集合

for (ml = methods; ml->ml_name != NULL; ml++) {

if ((ml->ml_flags & METH_CLASS) ||

(ml->ml_flags & METH_STATIC)) {

PyErr_SetString(PyExc_ValueError,

"module functions cannot set"

" METH_CLASS or METH_STATIC");

Py_DECREF(n);

return NULL;

}

v = PyCFunction_NewEx(ml, passthrough, n);

if (v == NULL) {

Py_DECREF(n);

return NULL;

}

if (PyDict_SetItemString(d, ml->ml_name, v) != 0) {

Py_DECREF(v);

Py_DECREF(n);

return NULL;

}

Py_DECREF(v);

}

Py_DECREF(n);

}

if (doc != NULL) {

v = PyString_FromString(doc);

if (v == NULL || PyDict_SetItemString(d, "__doc__", v) != 0) {

Py_XDECREF(v);

return NULL;

}

Py_DECREF(v);

}

return m;

}

Py_InitModule4参数的含义:

name:module的名称,在这里是__builtin__

methods:该module中所包含的函数的集合,在这里是builtin_methods

doc:module的文档,在这里是builtin_doc

passthrough:这个参数在Python 2.5中没有使用,为NULL

module_api_version:Python内部使用的version值,用于比较

Py_InitModule4函数可以分为2个独立的部分:创建module对象;将(符号,值)对应关系放置到module中。

创建module对象

// import.c

PyObject *

PyImport_AddModule(const char *name)

{

// 获得Python维护的module集合

PyObject *modules = PyImport_GetModuleDict();

PyObject *m;

// 如果module集合中没有名为name的module对象,就创建它,否则直接返回

if ((m = PyDict_GetItemString(modules, name)) != NULL &&

PyModule_Check(m))

return m;

m = PyModule_New(name);

if (m == NULL)

return NULL;

// 将创建的module对象放入Python的全局module集合中

if (PyDict_SetItemString(modules, name, m) != 0) {

Py_DECREF(m);

return NULL;

}

Py_DECREF(m); /* Yes, it still exists, in modules! */

return m;

}

Python中维护了一个存放所有加载到内存中的module集合,在这个集合中,存放着所有的(module名,module对象)这样的对应关系,对应到Python中就是sys.modules。在创建一个新的module对象之前,会先到这个全局module中查看是否已经存在。通过PyImport_GetModuleDict函数获得PyInterpreterState(进程状态)对象中的module域的值。

如果全局modules字典中没有存在该name的module对象,则通过PyModule_New函数创建一个新的module对象,然后将(name,module)对应关系插入到modules集合中。

// moduleobject.c

typedef struct {

PyObject_HEAD

PyObject *md_dict;

} PyModuleObject;

PyObject * PyModule_New(const char *name)

{

PyModuleObject *m;

PyObject *nameobj;

m = PyObject_GC_New(PyModuleObject, &PyModule_Type);

if (m == NULL)

return NULL;

nameobj = PyString_FromString(name);

m->md_dict = PyDict_New();

if (m->md_dict == NULL || nameobj == NULL)

goto fail;

if (PyDict_SetItemString(m->md_dict, "__name__", nameobj) != 0)

goto fail;

if (PyDict_SetItemString(m->md_dict, "__doc__", Py_None) != 0)

goto fail;

Py_DECREF(nameobj);

PyObject_GC_Track(m);

return (PyObject *)m;

fail:

Py_XDECREF(nameobj);

Py_DECREF(m);

return NULL;

}

实际上,PyModuleObject对象就是对PyDictObject对象的简单包装,创建PyModuleObject对象的动作很简单,注意在这里设置了module的__name__属性,但是没有设置其__doc__属性。

至此,创建的PyModuleObject对象还算是空的,接下来就开始设置module对象。

设置module对象

设置module对象的流程回到Py_InitModule4函数中,设置属性主要依赖Py_InitModule4函数的第二个参数(methods),在这里为builtin_methods,遍历builtin_methods,处理其中的每一个元素。

// methodobject.h

typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);

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[] = {

{"__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},

{"apply", builtin_apply, METH_VARARGS, apply_doc},

{"callable", builtin_callable, METH_O, callable_doc},

{"chr", builtin_chr, METH_VARARGS, chr_doc},

{"cmp", builtin_cmp, METH_VARARGS, cmp_doc},

{"coerce", builtin_coerce, METH_VARARGS, coerce_doc},

{"compile", builtin_compile, METH_VARARGS, compile_doc},

{"delattr", builtin_delattr, METH_VARARGS, delattr_doc},

{"dir", builtin_dir, METH_VARARGS, dir_doc},

{"divmod", builtin_divmod, METH_VARARGS, divmod_doc},

{"eval", builtin_eval, METH_VARARGS, eval_doc},

{"execfile", builtin_execfile, METH_VARARGS, execfile_doc},

{"filter", builtin_filter, METH_VARARGS, filter_doc},

{"getattr", builtin_getattr, METH_VARARGS, getattr_doc},

{"globals", (PyCFunction)builtin_globals, METH_NOARGS, globals_doc},

{"hasattr", builtin_hasattr, METH_VARARGS, hasattr_doc},

{"hash", builtin_hash, METH_O, hash_doc},

{"hex", builtin_hex, METH_O, hex_doc},

{"id", builtin_id, METH_O, id_doc},

{"input", builtin_input, METH_VARARGS, input_doc},

{"intern", builtin_intern, METH_VARARGS, intern_doc},

{"isinstance", builtin_isinstance, METH_VARARGS, isinstance_doc},

{"issubclass", builtin_issubclass, METH_VARARGS, issubclass_doc},

{"iter", builtin_iter, METH_VARARGS, iter_doc},

{"len", builtin_len, METH_O, len_doc},

{"locals", (PyCFunction)builtin_locals, METH_NOARGS, locals_doc},

{"map", builtin_map, METH_VARARGS, map_doc},

{"max", (PyCFunction)builtin_max, METH_VARARGS | METH_KEYWORDS, max_doc},

{"min", (PyCFunction)builtin_min, METH_VARARGS | METH_KEYWORDS, min_doc},

{"oct", builtin_oct, METH_O, oct_doc},

{"open", (PyCFunction)builtin_open, METH_VARARGS | METH_KEYWORDS, open_doc},

{"ord", builtin_ord, METH_O, ord_doc},

{"pow", builtin_pow, METH_VARARGS, pow_doc},

{"range", builtin_range, METH_VARARGS, range_doc},

{"raw_input", builtin_raw_input, METH_VARARGS, raw_input_doc},

{"reduce", builtin_reduce, METH_VARARGS, reduce_doc},

{"reload", builtin_reload, METH_O, reload_doc},

{"repr", builtin_repr, METH_O, repr_doc},

{"round", (PyCFunction)builtin_round, METH_VARARGS | METH_KEYWORDS, round_doc},

{"setattr", builtin_setattr, METH_VARARGS, setattr_doc},

{"sorted", (PyCFunction)builtin_sorted, METH_VARARGS | METH_KEYWORDS, sorted_doc},

{"sum", builtin_sum, METH_VARARGS, sum_doc},

#ifdef Py_USING_UNICODE

{"unichr", builtin_unichr, METH_VARARGS, unichr_doc},

#endif

{"vars", builtin_vars, METH_VARARGS, vars_doc},

{"zip", builtin_zip, METH_VARARGS, zip_doc},

{NULL, NULL},

};

len函数对应的PyMethodDef结构

对于builtin_methods中的每一个PyMethodDef结构,PyInitModule4都会基于它创建一个PyCFunctionObject对象(Python中对函数指针的包装)。

// 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 */

} 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);

PyObject_INIT(op, &PyCFunction_Type);

}

else {

op = PyObject_GC_New(PyCFunctionObject, &PyCFunction_Type);

if (op == NULL)

return 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;

}

PyCFunctionObject对象中的那个self,也就是在Py_InitModule4中传入的passthrough(之前说这个参数在Python 2.5中没用,所以这里的self也就为NULL)。

注意,PyCFunctionObject对象中的m_module域并不是指向一个真正的PyModuleObject对象,而是一个PyStringObject对象,但是这个PyStringObject对象正是PyModuleObject对象的名字(根据PyModuleObject对象的名字可以在全局modules中找到对应的PyModuleObject对象)。

创建完成的 __builtin__ module

__builtin__ module创建完以后,也就是bimod = _PyBuiltin_Init()执行完以后,将PyModuleObject对象中维护的那个PyDictObject对象赋值给interp->builtins。以后Python在需要访问__builtin__ module时,直接访问interp->builtins就行,不需要再到interp->modules中去找到__builtin__ module对象然后再去访问。因为Python中使用__builtin__ module非常频繁,这样的机制会速度会更快。

创建sys module

sys module的备份

Python在创建并设置了__builtin__ module之后,会类似的以同样流程设置sys module,并像设置interp->builtins一样设置interp->sysdict。

设置完成__builtin__和sys两个module之后,PyInterpreterState和PyThreadState对象在内存中的情形如下所示:

此时内存中情形

由于Python的module集合interp->modules是一个PyDictObject对象,而PyDictObject对象在Python中是一个可变对象(其中维护的(module name,PyModuleObject)元素对在运行时有可能被删除)。

对于Python的扩展module(sys等),为了避免被删除后的重新初始化,Python会将所有的扩展module通过一个全局PyDictObject对象来进行备份维护,这里通过_PyImport_FixupExtension("sys", "sys")函数完成:

// import.c

static PyObject *extensions = NULL;

PyObject *

_PyImport_FixupExtension(char *name, char *filename)

{

PyObject *modules, *mod, *dict, *copy;

// 如果extensions为空,则创建PyDictObject对象赋给extensions

if (extensions == NULL) {

extensions = PyDict_New();

if (extensions == NULL)

return NULL;

}

// 获得进程对象的modules集合,interp->modules

modules = PyImport_GetModuleDict();

// 在interp->modules中找到名字为name的module

mod = PyDict_GetItemString(modules, name);

if (mod == NULL || !PyModule_Check(mod)) {

PyErr_Format(PyExc_SystemError,

"_PyImport_FixupExtension: module %.200s not loaded", name);

return NULL;

}

// 抽取module中的dict

dict = PyModule_GetDict(mod);

if (dict == NULL)

return NULL;

// 对dict进行拷贝

copy = PyDict_Copy(dict);

if (copy == NULL)

return NULL;

// 将拷贝得到的心dict存储在extensions中

PyDict_SetItemString(extensions, filename, copy);

Py_DECREF(copy);

return copy;

}

上面代码介绍了备份的过程,第一次会创建一个新的PyDictObject对象,这个对象将维护所有已经被Python加载的module中的PyDictObject的一个佩芬。当系统中的modules集合中的某个标准扩展module被删除后又被重新加载时,Python就不需要再次初始化这些module,只需extensions中备份的PyDictObject对象来创建一个新的module即可。

设置module搜索路径

在创建完sys module以后,会在此module中设置Python搜索一个module时的默认搜索路径集合(PySys_SetPath(Py_GetPath()))。这个路径集合就是在Python执行import xyz时将查看的路径的集合。

// sysmodule.c

void

PySys_SetPath(char *path)

{

PyObject *v;

if ((v = makepathobject(path, DELIM)) == NULL)

Py_FatalError("can't create sys.path");

if (PySys_SetObject("path", v) != 0)

Py_FatalError("can't assign sys.path");

Py_DECREF(v);

}

int

PySys_SetObject(char *name, PyObject *v)

{

PyThreadState *tstate = PyThreadState_GET();

PyObject *sd = tstate->interp->sysdict;

if (v == NULL) {

if (PyDict_GetItemString(sd, name) == NULL)

return 0;

else

return PyDict_DelItemString(sd, name);

}

else

return PyDict_SetItemString(sd, name, v);

}

在makepathobject中会创建一个PyListObject对象,这个PyListObject对象中包含一组PyStringObject对象,每一个PyStringObject对象的内容就是一个module的搜索路径(也就是Python中的sys.path)。

最终,这个代表搜索路径集合的list对象会在PySys_SetObject中被插入到interp->sysdict这个PyDictObject对象(sys module中维护的那个PyDictObject对象)中。

接下来,Python会进行一些琐碎的动作,其中包含初始化Python的import环境,初始化Python的内建异常(其实就是调用PyType_Ready初始化各个异常类)。

创建__main__ module

在_PyImportHooks_Init之后,Python会创建一个非常特殊的module:__main__的module。

// pythonrun.c

/* Create __main__ module */

static void

initmain(void)

{

PyObject *m, *d;

// 创建__main__ module,并设置到interp->modules中

m = PyImport_AddModule("__main__");

if (m == NULL)

Py_FatalError("can't create __main__ module");

// 获得__main__ module中的dict

d = PyModule_GetDict(m);

if (PyDict_GetItemString(d, "__builtins__") == NULL) {

// 获得interp->modules中的__builtin__ module

PyObject *bimod = PyImport_ImportModule("__builtin__");

if (bimod == NULL ||

// 将(“__builtins__”, __builtin__ module)插入到__main__ module中

PyDict_SetItemString(d, "__builtins__", bimod) != 0)

Py_FatalError("can't add __builtins__ to __main__");

Py_DECREF(bimod);

}

}

__main__ module是什么?在PyImport_AddModule时,创建了一个名为name的module后,会在module对应的PyDictObject对象(mp_dict)设置一个名为__name__的项。__main__ module的这一项就是"__main__",作为主程序运行的Python源文件就可以被视为名为__main__的module。

当Python以python abc.py运行时,Python在沿着名字空间寻找__name__时,就会最终在__main__ module中发现__name__为"__main__";而如果一个py文件是以import的方式加载的,则__name__不会为"__main__"。

设置site-specific的module的搜索路径

Python的第三方库通常都是由module提供,一般来说,一些规模较大的第三方库将放在%PythonHome%/lib/site-packages目录下,但是之前在初始化搜索路径集合的时候并没有将site-packages包含在内。

接下来Python通过initsite()函数将site-packages加入到搜索路径中。

// pythonrun.c

static void

initsite(void)

{

PyObject *m, *f;

// 获得interp->modules中的site module

m = PyImport_ImportModule("site");

if (m == NULL) {

f = PySys_GetObject("stderr");

if (Py_VerboseFlag) {

PyFile_WriteString(

"'import site' failed; traceback:\n", f);

PyErr_Print();

}

else {

PyFile_WriteString(

"'import site' failed; use -v for traceback\n", f);

PyErr_Clear();

}

}

else {

Py_DECREF(m);

}

}

PyImport_ImportModule函数时Python中import机制的核心,调用这个函数相当于Python中的import xxx,在这里进入site.py,其中会进行两个动作:

将site-packages路径加入到sys.path中,对于不同平台有不同操作

win32平台:%PythonHome%/lib/site-packages

+Unix/Linux平台:

%sys.prefix%/lib/python/site-packages(其中%sys.prefix%为Python的sys.predix)

%sys.prefix%/lib/site-python

%sys.exec_prefix%/lib/python/site-packages

%sys.exec_prefix%/lib/site-python

处理site-packages目录下的所有.pth文件中的所有路径加入到sys.path中

至此,Python中绝大部分重要的初始化动作都已经完成了,下图是完成初始化后所有可以利用的资源:

完成初始化后的Python环境

0x03 激活Python虚拟机

上面部分算是完成了Python执行程序所必须的基础设施建设,但是初始化动作还没有真正完成,当Python真正进入到之前讲的字节码虚拟机后,初始化阶段才算真正完成。

Python在Py_Initialize成功完成后,最终将调用PyRun_AnyFileExFlags(fp, filename == NULL ? "" : filename, filename != NULL, &cf)。如果以脚本方式运行Python,则filename是文件名;如果以交互方式运行Python,则filename会传入""。第一个参数fp指向打开的脚本或者是系统的标准输入流stdin。

// pythonrun.c

/* Parse input from a file and execute it */

int

PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit,

PyCompilerFlags *flags)

{

if (filename == NULL)

filename = "???";

// 根据fp是否代表交互环境,对程序流程进行分流

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);

}

通过Py_FdIsInteractive函数判断fp是否指向标准输入流。如果是,则进入PyRun_InteractiveLoopFlags,否则进入PyRun_SimpleFileExFlags。

交互式运行方式

// pythonrun.c

int

PyRun_InteractiveLoopFlags(FILE *fp, const char *filename, PyCompilerFlags *flags)

{

PyObject *v;

int ret;

PyCompilerFlags local_flags;

if (flags == NULL) {

flags = &local_flags;

local_flags.cf_flags = 0;

}

// 创建交互式环境提示符“>>>”

v = PySys_GetObject("ps1");

if (v == NULL) {

PySys_SetObject("ps1", v = PyString_FromString(">>> "));

Py_XDECREF(v);

}

// 创建交互式环境提示符“...”

v = PySys_GetObject("ps2");

if (v == NULL) {

PySys_SetObject("ps2", v = PyString_FromString("... "));

Py_XDECREF(v);

}

// 进入一个死循环,即交互式环境

for (;;) {

ret = PyRun_InteractiveOneFlags(fp, filename, flags);

PRINT_TOTAL_REFS();

if (ret == E_EOF)

return 0;

/*

if (ret == E_NOMEM)

return -1;

*/

}

}

int

PyRun_InteractiveOneFlags(FILE *fp, const char *filename, PyCompilerFlags *flags)

{

PyObject *m, *d, *v, *w;

mod_ty mod;

PyArena *arena;

char *ps1 = "", *ps2 = "";

int errcode = 0;

v = PySys_GetObject("ps1");

if (v != NULL) {

v = PyObject_Str(v);

if (v == NULL)

PyErr_Clear();

else if (PyString_Check(v))

ps1 = PyString_AsString(v);

}

w = PySys_GetObject("ps2");

if (w != NULL) {

w = PyObject_Str(w);

if (w == NULL)

PyErr_Clear();

else if (PyString_Check(w))

ps2 = PyString_AsString(w);

}

// 编译用户在交互式环境下输入的Python语句

arena = PyArena_New();

if (arena == NULL) {

Py_XDECREF(v);

Py_XDECREF(w);

return -1;

}

mod = PyParser_ASTFromFile(fp, filename,

Py_single_input, ps1, ps2,

flags, &errcode, arena);

Py_XDECREF(v);

Py_XDECREF(w);

if (mod == NULL) {

PyArena_Free(arena);

if (errcode == E_EOF) {

PyErr_Clear();

return E_EOF;

}

PyErr_Print();

return -1;

}

// 获得中维护的dict

m = PyImport_AddModule("__main__");

if (m == NULL) {

PyArena_Free(arena);

return -1;

}

d = PyModule_GetDict(m);

// 执行用户输入的Python语句

v = run_mod(mod, filename, d, d, flags, arena);

PyArena_Free(arena);

if (v == NULL) {

PyErr_Print();

return -1;

}

Py_DECREF(v);

if (Py_FlushLine())

PyErr_Clear();

return 0;

}

调用PyParser_ASTFromFile函数,对用户在交互式环境下输入的Python语句进行编译,其结果是构造与Python语句对应的抽象语法树(AST)。

然后调用run_mode,将最终完成对用户输入的语句的执行动作。需要注意的是:在进入run_mode之前,会将__main__ module中维护的PyDictObject对象取出,传递给run_mode,这个参数很重要,它将作为Python虚拟机开始执行时当前活动的frame对象的local名字空间和global名字空间。

脚本文件运行方式

// python.h

#define Py_file_input 257

// pythonrun.c

int

PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,

PyCompilerFlags *flags)

{

PyObject *m, *d, *v;

const char *ext;

// 在__main__module中设置“__file__”属性

m = PyImport_AddModule("__main__");

if (m == NULL)

return -1;

d = PyModule_GetDict(m);

if (PyDict_GetItemString(d, "__file__") == NULL) {

PyObject *f = PyString_FromString(filename);

if (f == NULL)

return -1;

if (PyDict_SetItemString(d, "__file__", f) < 0) {

Py_DECREF(f);

return -1;

}

Py_DECREF(f);

}

ext = filename + strlen(filename) - 4;

// 首先尝试去执行pyc文件

if (maybe_pyc_file(fp, filename, ext, closeit)) {

/* Try to run a pyc file. First, re-open in binary */

if (closeit)

fclose(fp);

if ((fp = fopen(filename, "rb")) == NULL) {

fprintf(stderr, "python: Can't reopen .pyc file\n");

return -1;

}

/* Turn on optimization if a .pyo file is given */

if (strcmp(ext, ".pyo") == 0)

Py_OptimizeFlag = 1;

v = run_pyc_file(fp, filename, d, d, flags);

} else {

// 执行py脚本文件

v = PyRun_FileExFlags(fp, filename, Py_file_input, d, d,

closeit, flags);

}

......

return 0;

}

PyObject *

PyRun_FileExFlags(FILE *fp, const char *filename, int start, PyObject *globals,

PyObject *locals, int closeit, PyCompilerFlags *flags)

{

PyObject *ret;

mod_ty mod;

PyArena *arena = PyArena_New();

if (arena == NULL)

return NULL;

// 编译

mod = PyParser_ASTFromFile(fp, filename, start, 0, 0,

flags, NULL, arena);

if (closeit)

fclose(fp);

if (mod == NULL) {

PyArena_Free(arena);

return NULL;

}

// 执行

ret = run_mod(mod, filename, globals, locals, flags, arena);

PyArena_Free(arena);

return ret;

}

同交互式执行方式一样,脚本文件的执行流程最后也进入了run_mode,也同样将__main__ module中维护的PyDictObject对象作为local和global名字空间传入run_mode中。

启动虚拟机

从run_mode开始,Python现在只剩下最后一件需要完成的工作了,那就是启动字节码虚拟机。

// pythonrun.c

static PyObject *

run_mod(mod_ty mod, const char *filename, PyObject *globals, PyObject *locals,

PyCompilerFlags *flags, PyArena *arena)

{

PyCodeObject *co;

PyObject *v;

// 基于AST编译字节码指令序列,创建PyCodeObject对象

co = PyAST_Compile(mod, filename, flags, arena);

if (co == NULL)

return NULL;

// 创建PyFrameObject对象,执行PyCodeObject对象中的字节码指令序列

v = PyEval_EvalCode(co, globals, locals);

Py_DECREF(co);

return v;

}

首先是根据AST得到PyCodeObject对象,然后通过PyEval_EvalCode函数开始唤醒字节码虚拟机。

// ceval.c

PyObject *

PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals)

{

/* XXX raise SystemError if globals is NULL */

return PyEval_EvalCodeEx(co,

globals, locals,

(PyObject **)NULL, 0,

(PyObject **)NULL, 0,

(PyObject **)NULL, 0,

NULL);

}

PyObject *

PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,

PyObject **args, int argcount, PyObject **kws, int kwcount,

PyObject **defs, int defcount, PyObject *closure)

{

register PyFrameObject *f;

register PyObject *retval = NULL;

register PyObject **fastlocals, **freevars;

PyThreadState *tstate = PyThreadState_GET();

PyObject *x, *u;

......

f = PyFrame_New(tstate, co, globals, locals);

......

fastlocals = f->f_localsplus;

freevars = f->f_localsplus + co->co_nlocals;

......

// 真正的字节码虚拟机(伪CPU)

retval = PyEval_EvalFrameEx(f,0);

return retval;

}

从操作系统为Python创建进程开始,到Python虚拟机被唤醒,再到执行引擎循环执行字节码,这个过程已经很清晰了。

名字空间

在创建PyFrameObject对象时,设置的3个名字空间:local、global、builtin。

builtin名字空间就是初始化时创建的__builtin__ module,Python所有线程都共享同样的builtin名字空间(节省空间,加快速度)

global名字空间被设置为__main__ module的dict

local名字空间和global名字空间一样

欢迎关注微信公众号(coder0x00)或扫描下方二维码关注,我们将持续搜寻程序员必备基础技能包提供给大家。

你可能感兴趣的:(python怎么启用运行环境)