零、緒論:特別鳴謝下文博客,自己博客是對這篇博客的學習筆記:
大佬webber博客:https://www.cnblogs.com/webber1992/p/6597166.html
一、三種文件:
1、pyc文件:py文件編譯后的二進制文件。
2、pyo文件:優化后的py編譯的文件。
3、pyd文件:其他語言編程的py庫。
二、python編譯的過程:
python不單純是一種解釋性語言,也需要編譯,需要編譯成字節碼。然后模仿可執行文件的入棧出棧調用順序執行。pyc文件中保存這編譯而成的字節碼,PVM從PyCodeObject讀取字節碼一條一條執行。PyCodeObject保存字節碼以及進程上下文信息。pyc文件在import事后創建PyCodeObject對象,一般是由py_compile模塊操作的。直接執行py文件不會生成pyc,但是可以通過python -m xxx.py生成pyc文件。
從整體上看:OS中執行程序離不開兩個概念:進程和線程。python中模擬了這兩個概念,模擬進程和線程的分別是PyInterpreterState和PyTreadState。即:每個PyThreadState都對應着一個幀棧,python虛擬機在多個線程上切換。當python虛擬機開始執行時,它會先進行一些初始化操作,最后進入PyEval_EvalFramEx函數,它的作用是不斷讀取編譯好的字節碼,並一條一條執行,類似CPU執行指令的過程。函數內部主要是一個switch結構,根據字節碼的不同執行不同的代碼。
三、PyCodeObject的結構:
1 typedef struct { 2 PyObject_HEAD 3 int co_argcount; /* 位置參數個數 */ 4 int co_nlocals; /* 局部變量個數 */ 5 int co_stacksize; /* 棧大小 */ 6 int co_flags; 7 PyObject *co_code; /* 字節碼指令序列 */ 8 PyObject *co_consts; /* 所有常量集合 */ 9 PyObject *co_names; /* 所有符號名稱集合 */ 10 PyObject *co_varnames; /* 局部變量名稱集合 */ 11 PyObject *co_freevars; /* 閉包用的變量名集合 */ 12 PyObject *co_cellvars; /* 內部嵌套函數引用的變量名集合 */ 13 /* The rest doesn’t count for hash/cmp */ 14 PyObject *co_filename; /* 代碼所在文件名 */ 15 PyObject *co_name; /* 模塊名|函數名|類名 */ 16 int co_firstlineno; /* 代碼塊在文件中的起始行號 */ 17 PyObject *co_lnotab; /* 字節碼指令和行號的對應關系 */ 18 void *co_zombieframe; /* for optimization only (see frameobject.c) */ 19 } PyCodeObject;引用的C語言的PyCodeObject的結構體
四、執行過程:
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;