閱讀850 返回首頁    go 技術社區[雲棲]


Python虛擬機的初始化概覽

今天想跟下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();宏:
#ifdef WITH_THREAD
#include "pythread.h"
static PyThread_type_lock head_mutex = NULL; /* Protects interp->tstate_head */
#define HEAD_INIT() (void)(head_mutex || (head_mutex = PyThread_allocate_lock()))
#define HEAD_LOCK() PyThread_acquire_lock(head_mutex, WAIT_LOCK)
#define HEAD_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();

第一個函數其實就是初始化變量static PyObject *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 */
#ifdef WITH_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)
{
    return PyEval_EvalCodeEx(co,
                      globals, locals,
                      (PyObject **)NULL, 0,
                      (PyObject **)NULL, 0,
                      (PyObject **)NULL, 0,
                      NULL);
}

於是我們最終進入到PyEval_EvalFrameEx函數,在這裏用大量的switch/case來執行指令。


最後更新:2017-04-02 06:51:55

  上一篇:go ImageView 用法總結
  下一篇:go IE(微軟)瀏覽器擴展開發初探