《python源代碼剖析》筆記 Python虛擬機框架

本文爲senlie原創,轉載請保留此地址:http://blog.csdn.net/zhengsenliepython


1.
Python虛擬機會從編譯獲得的PyCodeObject對象中依次讀入每一條字節碼指令。
並在當前的上下文環境中運行這條字節碼指令。數組


Python虛擬機其實是在模擬操做中運行文件的過程
PyCodeObject對象中包括了字節碼指令以及程序的所有靜態信息,但沒有包括
程序執行時的動態信息——執行環境(PyFrameObject)


2.Python源代碼中的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;


名字空間其實是維護着變量名和變量值之間關係的PyDictObject對象。
f_builtins, f_globals, f_locals名字空間分別維護了builtin, global, local的name與相應值
之間的映射關係。



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



Code Block。PyFrameObject,做用域,名字空間好像都與一段代碼段一一相應?



PyFrameObject中的動態內存空間
函數

PyFrameObject *PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, PyCodeObject *locals)
{
	PyFrameObject *f;
	Py_ssize_t extras, ncells, nfrees, i;
	ncells = PyTuple_GET_SIZE(code->co_cellvars);
	nfrees = PyTuple_GET_SIZE(code->co_freevars);
	//這四部分構成了 PyFrameObject維護的動態內存區。其大小由extras肯定
	extras = code->co_stacksize + code->co_nlocals + ncells + nfrees;
	f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras);
	//計算初始化時執行時棧的棧頂
	extras = code->co_nlocals + ncells + nfrees;
	//f_valuestack維護執行時棧的棧底,f_stacktop維護執行時棧的棧頂
	f->f_valuestack = f->f_localsplus + extras;
	f->f_stacktop = f->f_valuestack;
	return f;
}
在建立 PyFrameObject對象時,額外申請的內存有一部分是給 PyCodeObject對象用的,
還有一部分纔是給執行時棧用的。


在Python中訪問 PyFrameObject對象
8.1.3的caller.py我執行失敗了。在f_globals.keys()處出錯
看不懂frame_getter.py

3.名字、做用域和名字空間
對於python這類動態語言來講,名字的意義遠比C這類的靜態語言大。因爲名字是python
在執行時能夠找到其所相應的東西的惟一途徑。oop



名字: 一個標識符就是一個名字,比方變量名、函數名、類名等
做用域:做用域是指約束起做用的程序正文區域。ui

Python是具備靜態做用域(詞法做用域)的。
在Python中,一個約束在程序正文的某個位置是否起做用。是由該約束在文本中的位置惟一決定的,
而不是執行時動態決定的。spa


名字空間:由一個 PyDictObject對象實現。每一個名字空間都與一個做用域相應

module
1.將邏輯相關的代碼放在一塊兒,實現代碼複用
2.爲系統劃分名字空間(一個module定義了一個獨立的名字空間)

賦值語句會影響名字空間(「擁有賦值行爲、擁有設置對象屬性的行爲」)
1.建立一個對象obj
2.將obj"賦給"一個名字name
.net

a = 1
def f()
class A(object)
import abc

 嵌套做用域最內嵌套做用域規則:1.閉包實現  2.編譯器實現LEGB規則:名字引用動做沿着local做用域、嵌套做用域、global做用域、builtin做用域的順序查找函數相應local做用域。module源文件相應global做用域,Python自身定義了最頂層的做用域——builtin做用域幾個有助理解LEGB規則的代碼段 
 
<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;"></span><pre name="code" class="cpp">#----------------------
a = 1 #[1]
def f():
	a = 2 #[2]
	print a #[3] 輸出2
print a #[4] 輸出1
#----------------------
#閉包。

在運行func = f()的時候。會運行函數f中的def g():語句,這時Python會將約束a = 2與函數g相應的函數對象捆綁在一塊兒線程

#將捆綁後的結果返回
a = 1
def f():
	a = 2
	def g():
		print a #[1]:輸出結果爲2
	return g
func = f()
func() #[2]
#----------------------
<pre name="code" class="cpp">#由一個賦值語句引進的名字在這個賦值語句所在的做用域是可見的。在 第9章 Python虛擬機中的通常表達式 這章有說明實現原理
#[1]pint a語句處,a已經存在了這個Code Block相應的PyFrameObject相應的co_names符號表裏了。但由於還沒運行[2]的賦值語句,
#在PyFrameObject的f_locals裏並無這個鍵值對,因此在[1]處會出現「local variable 'a' referenced before assignment」的錯誤
a = 1def g():print adef f():print a #[1] error: local variable 'a' referenced before assignment a = 2 #[2]print ag()f()#----------------------a = 1def f():global aprint a #輸出結果:1a = 2f()print a #輸出結果:2#----------------------a = 1def f():a = 2def g():global aprint a #輸出結果:1a += 1return gg = f()g()print a #輸出結果:2
 
 
 
 
4.Python虛擬機的執行框架——字節碼執行引擎
PyEval_EvalFrameEx 
虛擬機的詳細實現-->switch/case結構
Python虛擬機運行字節碼指令序列的過程就是從頭至尾遍歷整個co_code、依次運行字節碼指令(字符數組)的過程。



/* Interpreter main loop */ 
PyObject* PyEval_EvalFrame(PyFrameObject *f) 
{
	//…… 
	why = WHY_NOT;//指示了在退出for循環時Python運行引擎的狀態
	for (;;) {
	//…… 
	fast_next_opcode: 
		f->f_lasti = INSTR_OFFSET();
		/* 得到字節碼指令和參數*/
		opcode = NEXTOP();
		oparg = 0;  
		/* 假設指令需要參數。得到指令參數*/
		if (HAS_ARG(opcode))
			oparg = NEXTARG();
	dispatch_opcode: 
		switch (opcode) {
		case NOP:
		goto fast_next_opcode;
		case LOAD_FAST:
		//…… 
		} 
}

5.Python執行時環境初探
PyInterpreterState --> 進程的抽象
PyThreadState --> 線程狀態信息的抽象
在Win32下,線程是還能獨立存活的,它需要存活在進程的環境中。多個線程可以共享進程的一些資源
進程中會有線程集合,線程中會有函數調用堆棧。


當Python虛擬機開始運行時,會將當前線程狀態對象中的frame設置爲當前的運行環境(frame),而
在創建新的PyFrameObject對象時,則從當前線程的狀態對象中取出舊的frame,創建PyFrameObject鏈表
線程之間的同步靠的是全局解釋鎖(GIL)

//進程
typedef struct _is{
	struct _is *next;
	struct _ts *tstate_head; //模擬進程環境中的線程集合
	
	PyObject *modules;
	PyObject *sysdict;
	PyObject *builtins;
	
	//...
} PyInterpreterState
//線程
typedef struct _ts{
	struct _ts *next
	PyInterpreterState *interp;
	struct _frame *frame; //模擬線程中的函數調用堆棧
	int recursion_depth;
	//...
	PyObject *dict;
	//...
	long thread_id;
} PyThreadState;

相關文章
相關標籤/搜索