1. 程式人生 > >python程序執行原理

python程序執行原理

技術 進行 state head 行號 線程 當前 compile for

Python程序的執行原理

1. 過程概述
Python先把代碼(.py文件)編譯成字節碼,交給字節碼虛擬機,然後解釋器一條一條執行字節碼指令,從而完成程序的執行。

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

2. 字節碼
字節碼在python解釋器程序裏對應的是PyCodeObject對象
.pyc文件是字節碼在磁盤上的表現形式

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

3. .pyc文件
PyCodeObject對象的創建時機是模塊加載的時候,及import
Python test.py會對test.py進行編譯成字節碼並解釋執行,但是不會生成test.pyc
如果test.py加載了其他模塊,如import urlib2, Python會對urlib2.py進行編譯成字節碼,生成urlib2.pyc,然後對字節碼進行解釋
如果想生成test.pyc,我們可以使用Python內置模塊py_compile來編譯。
加載模塊時,如果同時存在.py和pyc,Python會嘗試使用.pyc,如果.pyc的編譯時間早於.py的修改時間,則重新編譯.py並更新.pyc。

4. PyCodeObject
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;


5. .pyc文件格式
加載模塊時,模塊對應的PyCodeObject對象被寫入.pyc文件

6.分析字節碼

6.1解析PyCodeObject
Python提供了內置函數compile可以編譯python代碼和查看PyCodeObject對象

6.2指令序列co_code的格式

opcode	oparg	opcode	opcode	oparg	…
1 byte	2 bytes	1 byte	1 byte	2 bytes	 
Python內置的dis模塊可以解析co_code

7. 執行字節碼
Python解釋器的原理就是模擬可執行程序再X86機器上的運行,X86的運行時棧幀如下圖
技術分享圖片

 

Python解釋器的原理就是模擬上述行為。當發生函數調用時,創建新的棧幀,對應Python的實現就是PyFrameObject對象。
PyFrameObject對象創建程序運行時的動態信息,即執行環境

7.1 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相對應。

  

python程序執行原理