今天想跟下Python虚拟机的启动,看看以调试模式跑起python_d.exe是怎么一个过程。

1. d:\Python-2.7.2\Modules\python.c文件是main函数,直接调用Py_Main函数;

2. 在Py_Main函数中,初始化函数为Py_Initialize();,而后者直接调用Py_InitializeEx(1);函数;

3. 在Py_InitializeEx函数中,首先判断是否已经初始化过,如果有则返回,没有就改下标志,开始初始化;第一步也是十分关键的一步,是调用函数(interp=PyInterpreterState_New();)创建PyInterpreterState对象(即解释器进程,现在Python一个解释器只有一个进程,所以存在一个缺点,在多核的机子上也只能存在一个进程)。

4. 在PyInterpreterState_New()函数中,首先为PyInterpreterState对象分配内存:

PyInterpreterState*interp=(PyInterpreterState*)
 
 
malloc(sizeof(PyInterpreterState));

如果分配内存成功,执行HEAD_INIT();宏:

#ifdefWITH_THREAD
 
 
#include"pythread.h"
 
 
staticPyThread_type_lockhead_mutex=NULL;/* Protects interp->tstate_head */
 
 
#defineHEAD_INIT()(void)(head_mutex||(head_mutex=PyThread_allocate_lock()))
 
 
#defineHEAD_LOCK()PyThread_acquire_lock(head_mutex,WAIT_LOCK)
 
 
#defineHEAD_UNLOCK()PyThread_release_lock(head_mutex)

不论是从名称还是注释都可以看出head_mutex是用来保护解释器进程的tstate_head成员的。

在head_mutex为NULL的情况下会调用PyThread_allocate_lock()函数初始化head_mutex变量。

接着初始化其它成员。

于是,解释器进程就这么创建了……

接着立马创建线程:tstate=PyThreadState_New(interp);,继而调用new_threadstate(interp,1);。

同样地,先为线程对象分配空间:PyThreadState*tstate=(PyThreadState*)malloc(sizeof(PyThreadState));。

接着一样是对线程对象进行初始化,然后切换一下线程:(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");
 
   
 
   
if(!_PyLong_Init())
 
   
Py_FatalError("Py_Initialize: can't init longs");
 
   
 
   
if(!PyByteArray_Init())
 
   
Py_FatalError("Py_Initialize: can't init bytearray");
 
   
 
   
_PyFloat_Init();

第一个函数其实就是初始化变量staticPyObject*builtin_object;为字符串“__builtins__”;

第二个函数是初始化小整数的缓存链表,这里的小整数范围是可以自定义再重新编译进行设置的,过程挺有趣的;

第三个函数初始化Long类型信息,第四个函数没做什么;

接着初始化Float类型信息。

然后是初始化interp的modules和modules_reloading成员,都是简单地调用PyDict_New()函数赋值。

如果需要的话,还会初始化Unicode编码:_PyUnicode_Init();。

接着是模块初始化,代码有省略:

bimod=_PyBuiltin_Init();
 
 
interp->builtins=PyModule_GetDict(bimod);
 
  
sysmod=_PySys_Init();
 
 
interp->sysdict=PyModule_GetDict(sysmod);

还有模块导入机制、异常、警告的初始化,代码有省略:

_PyImport_Init();
 
 
_PyExc_Init();
 
  
_PyWarnings_Init();
 
 
 
 
 initmain();/* Module __main__ */

初始化全局锁:

/* auto-thread-state API, if available */
 
 
#ifdefWITH_THREAD
 
 
_PyGILState_Init(interp,tstate);
 
 
#endif/* WITH_THREAD */

最后是检查stdin,stdout,stderr和设置相关信息。

然后回到Py_Main,输出版本信息以及“>>>”,等用户输入。

假设输入“a = 1”,然后按Enter键:

Python会先对用户输入进行编译(co=PyAST_Compile(mod,filename,flags,arena);),生成PyCodeObject,然后再执行:

v=PyEval_EvalCode(co,globals,locals);:
 
 
 
 
PyObject*
 
 
PyEval_EvalCode(PyCodeObject*co,PyObject*globals,PyObject*locals)
 
 
{
 
 
returnPyEval_EvalCodeEx(co,
 
 
globals,locals,
 
 
(PyObject**)NULL,0,
 
 
(PyObject**)NULL,0,
 
 
(PyObject**)NULL,0,
 
 
NULL);
 
 
}

于是我们最终进入到PyEval_EvalFrameEx函数,在这里用大量的switch/case来执行指令。