python虛擬機運行原理

     近期爲了面試想要了解下python的運行原理方面的東西,奈何關於python沒有找到一本相似於深刻理解Java虛擬機方面的書籍,找到了一本《python源碼剖析》電子書,可是以爲相對來講最近仍是不打算用大布頭時間研究這本書,只能先找來幾篇相關的博客來閱讀,記錄以下:python

    1、過程概述面試

一、python先把代碼(.py文件)編譯成字節碼,交給字節碼虛擬機,而後虛擬機會從編譯獲得的PyCodeObject對象中一條一條執行字節碼指令,並在當前的上下文環境中執行這條字節碼指令,從而完成程序的執行。Python虛擬機其實是在模擬操做中執行文件的過程。PyCodeObject對象中包含了字節碼指令以及程序的全部靜態信息,但沒有包含程序運行時的動態信息——執行環境(PyFrameObject)閉包

二、字節碼在python虛擬機程序裏對應的是PyCodeObject對象; 函數

     .pyc文件是字節碼在磁盤上的表現形式。ui

三、從總體上看:OS中執行程序離不開兩個概念:進程和線程。python中模擬了這兩個概念,模擬進程和線程的分別是PyInterpreterStatePyTreadState。即:每一個PyThreadState都對應着一個幀棧,python虛擬機在多個線程上切換。當python虛擬機開始執行時,它會先進行一些初始化操做,最後進入PyEval_EvalFramEx函數,它的做用是不斷讀取編譯好的字節碼,並一條一條執行,相似CPU執行指令的過程。函數內部主要是一個switch結構,根據字節碼的不一樣執行不一樣的代碼。url

 

     2、關於.pyc文件spa

     PyCodeObject對象的建立時機是模塊加載的時候,即import。.net

一、執行 python test.py 會對test.py進行編譯成字節碼並解釋執行,但不會生成test.pyc
二、若是test.py中加載了其餘模塊,如import urllib2,那麼python會對urllib2.py進行編譯成字節碼,生成urllib2.pyc,而後對字節碼解釋執行。
三、若是想生成test.pyc,咱們可使用python內置模塊py_compile來編譯。
也能夠執行命令 python -m test.py 這樣,就生成了test.pyc
四、加載模塊時,若是同時存在.py和.pyc,python會使用.pyc運行,若是.pyc的編譯時間早於.py的時間,則從新編譯.py,並更新.pyc文件。線程

 

     3、關於PyCodeObjectrest

Python代碼的編譯結果就是PyCodeObject對象,以下:

typedef struct {
    PyObject_HEAD
    int co_argcount;        /* 位置參數個數 */
    int co_nlocals;         /* 局部變量個數 */
    int co_stacksize;       /* 棧大小 */
    int co_flags;   
    PyObject *co_code;      /* 字節碼指令序列 */
    PyObject *co_consts;    /* 全部常量集合 */
    PyObject *co_names;     /* 全部符號名稱集合 */
    PyObject *co_varnames;  /* 局部變量名稱集合 */
    PyObject *co_freevars;  /* 閉包用的變量名集合 */
    PyObject *co_cellvars;  /* 內部嵌套函數引用的變量名集合 */
    /* The rest doesn’t count for hash/cmp */
    PyObject *co_filename;  /* 代碼所在文件名 */
    PyObject *co_name;      /* 模塊名|函數名|類名 */
    int co_firstlineno;     /* 代碼塊在文件中的起始行號 */
    PyObject *co_lnotab;    /* 字節碼指令和行號的對應關係 */
    void *co_zombieframe;   /* for optimization only (see frameobject.c) */
} PyCodeObject;

 

     4、執行字節碼

Python虛擬機的原理就是模擬可執行程序再X86機器上的運行,X86的運行時棧幀以下圖:

                           

 

假如test.py用C語言來實現,會是下面這個樣子:

const char *s = 「hello」;
 
void func() {
    printf(「%s\n」, s);
}
 
int main() {
    func();
    return 0;
}

Python虛擬機的原理就是模擬上述行爲。當發生函數調用時,建立新的棧幀,對應Python的實現就是PyFrameObject對象。

PyFrameObject對象建立程序運行時的動態信息,即執行環境,相關源碼大體以下:

typedef struct _frame{  
    PyObject_VAR_HEAD //"運行時棧"的大小是不肯定的  
    struct _frame *f_back; //執行環境鏈上的前一個frame,不少個PyFrameObject鏈接起來造成執行環境鏈表  
    PyCodeObject *f_code; //PyCodeObject 對象,這個frame就是這個PyCodeObject對象的上下文環境  
    PyObject *f_builtins; //builtin名字空間  
    PyObject *f_globals;  //global名字空間  
    PyObject *f_locals;   //local名字空間  
    PyObject **f_valuestack; //"運行時棧"的棧底位置  
    PyObject **f_stacktop;   //"運行時棧"的棧頂位置  
    //...  
    int f_lasti;  //上一條字節碼指令在f_code中的偏移位置  
    int f_lineno; //當前字節碼對應的源代碼行  
    //...  
      
    //動態內存,維護(局部變量+cell對象集合+free對象集合+運行時棧)所須要的空間  
    PyObject *f_localsplus[1];    
} PyFrameObject; 

每個 PyFrameObject對象都維護了一個 PyCodeObject對象,這代表每個 PyFrameObject中的動態內存空間對象都和源代碼中的一段Code相對應。

 

 

 

參考博客:

http://tech.uc.cn/?p=1932

http://blog.csdn.net/celte/article/details/39405561

http://www.tuicool.com/articles/Q7Rj6rr

相關文章
相關標籤/搜索