內(nèi)核級Python:調(diào)試Python編譯器源碼
python編譯器在執(zhí)行時,給它指定要執(zhí)行的源碼文件,或者說直接輸入源碼字符串就可以驅動腳本的執(zhí)行流程,其基本框架如下:
input層是python編譯器用于獲取源碼的輸入方式,事實上Python能夠有多種方式將源碼信息傳遞給編譯器,例如:
1,執(zhí)行python -c 然后接著python代碼字符串。
2,python -m 然后跟著要執(zhí)行的模塊名
3,python 然后跟著腳本文件的路徑
4,通過管道連接方式執(zhí)行,例如 cat [file] | python
Python解釋器不關心代碼如何輸入,只要它能獲取源碼內(nèi)容即可,因此它專門設立了一個輸入層來處理源碼的讀入。一旦獲得源碼內(nèi)容后,解釋器需要做三個動作,第一個是設置編譯選項,如果你用過g++, gcc這類編譯器,你一定了解執(zhí)行時要有很多設置開關或選項,圖中的configuration模塊就負責這些選項的設置,State用來存儲腳本中設定的各種變量,Module通過解讀腳本后生成的一種便于腳本執(zhí)行的數(shù)據(jù)結構。
以下我們會描述一些代碼和數(shù)據(jù)結構,我們大概知道即可,不需要掌握或完全理解。我們看看解釋器在運行腳本前進行相關配置的代碼,相關代碼在python目錄下的initconfig.h和initconfig.c中。打開initconfig.c,然后搜索PyPreConfig結構體對象,然后按住ctrl并點擊它就可以打開它的定義,它有些字段需要注意:
1,int allocator , 該字段對應內(nèi)存分配器類型,它其實是個枚舉值,用來選取不同的內(nèi)存分配器。
2,int isolatd, 設置隔離模式,應該對應python虛擬執(zhí)行環(huán)境,在該環(huán)境里進行pip安裝或是環(huán)境變量配置不會對全局環(huán)境產(chǎn)生影響。
3,int utf8_mode , 設置utf-8模式
在initconfig.c中搜索PyConfig,這個結構體用于運行時配置,例如設置解釋器在執(zhí)行腳本時是出于調(diào)試模式還是優(yōu)化模式,它還記錄了一些涉及到運行時的環(huán)境變量配置。接下來我們在解釋器源碼中設置斷點對其執(zhí)行進行調(diào)試體驗,操作如下圖所示:
首先在python模塊右鍵,選中屬性,點擊調(diào)試,在命令參數(shù)中輸入python -v -c “print(‘hello world’)”,然后在函數(shù)config_parse_cmdline中設置斷點,該函數(shù)應該在1875行,這個函數(shù)用于解讀執(zhí)行python解釋器時的命令行參數(shù),設置好后點擊F5啟動調(diào)試,我們會看到VS停在斷點設置的地方,然后點擊F10單步,我們可以看看該函數(shù)前面幾個變量的內(nèi)容:
從中我們能看到Python解釋器對應的可執(zhí)行文件為python_d.exe,繼續(xù)往下走可以看到代碼進入case ‘v’,這里打開了verbose模式,這樣Python解釋器執(zhí)行時會把很多信息打印出來。接下來在main.c中的pymain_run_command函數(shù)中設置斷點,這個函數(shù)會調(diào)用一系列函數(shù)執(zhí)行源碼,該文件在Module目錄下,
里面的PyRunSimpleStringFlags函數(shù)作用就是執(zhí)行源碼,我們單步運行該函數(shù),然后打開控制臺就會看到hello輸出來了。上面代碼中函數(shù)PyRunSimpleStringFlags的作用就是創(chuàng)建一個Module對象,一個Module對象就是含有__main入口的可執(zhí)行模塊。