如何編譯和調試Python內核源碼?

博客:blog.shinelee.me | 博客園 | CSDNpython

寫在前面

若是對Python源碼感興趣,那「窺探」其實現的最佳方式就是調教它,不,調試它。git

獲取源代碼

Python的官方默認實現爲CPython,即C語言實現(主要指解釋器的實現,其餘實現見Other Interpreter Implementations)。CPython的源代碼能夠從官網pyhton.org或者github.com/python/cpython獲取,目前最新的穩定版本爲3.8.0,於2019.10.14發佈。這裏,從官網 https://www.python.org/downloads/release/python-380/ 下載源碼壓縮包,以下圖所示,github

python source code

源代碼的組織

解壓後,目錄結構以下windows

{ Python-3.8.0 }  » tree -d -L 1 .
.
├── Doc         # rst(reStructuredText)格式官方文檔,用其生成https://docs.python.org/
├── Grammar     # Python的EBNF(Extended Backus–Naur form)語法定義文件
├── Include     # .h 頭文件
├── Lib         # .py 純Python實現的標準庫
├── m4          # ?
├── Mac         # Mac-specific code,支持MacOS
├── Misc        # Things that do not belong elsewhere.
├── Modules     # C實現的標準庫,內含.c .asm .macros .h
├── Objects     # 內置數據類型實現
├── Parser      # Python語法分析器源碼
├── PC          # Windows-specific code,支持Windows
├── PCbuild     # Windows生成文件,for MSVC
├── Programs    # main函數文件,用於生成可執行文件,如python.exe的入口文件
├── Python      # CPython解釋器源碼
└── Tools       # 獨立工具代碼,used to maintain Python

CPython的源碼組織結構以下,摘抄自CPython Source Code Layoutbash

CPython Source Code Layout

源碼文件分門別類存放,並且,不管是py實現的標準庫、c實現的標準庫、內置數據類型仍是內置函數,在Lib/test/Doc/library/目錄下都有與之對應的test_x.py測試文件和rst文檔文件(對於內置數據類型和函數,其文檔集中保存在stdtypes.rst和functions.rst)。好比,內置類型int位於Objects/longobject.c文件中。ide

下面正式開始編譯CPython。函數

windows下編譯CPython

Compile and build on Windows,Python3.6及以後的版本可使用VS2017編譯,安裝VS2017時,記得勾選 Python developmentPython native development tools,有備無患。工具

安裝好VS2017後,雙擊PCbuild/pcbuild.sln,打開解決方案。由於咱們的關注點僅在Python內核和解釋器部分,因此僅編譯python和pythoncore,其餘模塊暫時忽略,具體地,測試

  • 切換到debug win32
  • 右鍵解決方案→屬性→配置屬性
  • 僅勾選項目python和pythoncore
  • 肯定

vs2017 python build configuration

此時再「生成解決方案」,生成目錄爲PCbuild/win32,內容以下,含解釋器python_d.exe和內核python38_d.dll,

PCbuild build dir

接下來,將項目python設爲啓動項目(默認狀態便是啓動項目),點擊調試,運行獲得以下控制檯,能夠像平時使用python同樣,與之交互。

python38_d debug console

若是想生成所有模塊,須要運行PCbuild\get_externals.bat下載依賴,再編譯,具體可參見Build CPython on Windows

調試CPython

只要程序能運行起來,一切就好辦了。憑藉「宇宙最強IDE」,咱們能夠任性地設斷點調試甚至修改代碼。

F5從新啓動調試,彈出控制檯。在上面咱們知道int類型位於Objects/longobject.c文件,打開文件,簡單瀏覽後在函數PyObject * PyLong_FromLong(long ival)入口處打個斷點。而後,在彈出的控制檯中輸入a = 1來建立int對象,回車,程序停在了斷點處,查看變量ival的值爲1——恰爲咱們輸入的數值,這個函數會跟根據輸入的C long int建立一個int對象,返回對象指針

debug int

再來看看函數調用堆棧,以下圖所示,

call stack

調用順序從下至上,從中能夠推斷出,

  • 從python_d.exe的入口main運行起來後,進入python38_d.dll
  • 從標準輸入stdin中讀取鍵入的字符串
  • 解析字符串,創建了語法樹AST(abstract syntax tree)
  • 解析語法樹中的節點,判斷字符爲number,將字符串轉化爲C long int
  • 由C long int建立Python的int對象

繼續運行,彈出的控制檯中光標前出現<<<,等待輸入。這時若是咱們點擊調試中的中止按鈕(所有中斷),會發現程序停在Parser/myreadline.c文件_PyOS_WindowsConsoleReadline函數中的ReadConsoleW一行,

if (!ReadConsoleW(hStdIn, &wbuf[total_read], wbuflen - total_read, &n_read, NULL)) {
    err = GetLastError();
    goto exit;
}

ReadConsoleW爲WINAPI,詳見ReadConsole function,其等待並讀取控制檯的輸入,讀取的字符保存在wbuf中。若是有輸入,則進入上面的流程,解析→創建語法樹→……

小結

至此,咱們揭開了Python面紗的一角——不過是一個可運行、可調試的程序而已(微笑)。

參考

相關文章
相關標籤/搜索