解釋運作程式 回憶上次内容 我們這次設定了斷點 設定斷點的目的是更快地調試 調試的目的是去除 bug 别害怕 bug 一步步地總能找到 bug 這就是程式員基本功 我心中還是有疑問 python3 是怎麼解釋 hello.py 的? 純文字 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 print("1982------Guido in cwi")
print("1995------Guido in cnri")
print("2000------Guido in beopen")
print("2005------Guido in google")
print("2012------Guido in dropbox")
print("2020------Guido in microsoft")
傳統文本 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 在字元的基礎上組織起篇章結構 字組成詞 詞組成句 句組成段 段組成章節 最後成書 tokenize 首先把一個個字元組成詞 分析一下哪些字可以組成詞 術語叫詞法分析(lexical analysis) 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 token 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 古人說聽我号令 怎麼把源檔案變成一個詞(token)流呢? python3子產品 幫助手冊裡面有這個内容 這個tokenize是python3的一個子產品(module) 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 token流 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 我們嘗試運作 python3 -m tokenize guido.py 對guido.py進行詞法分析 分析出來的詞(token)流什麼樣子呢? 這個詞的流怎麼了解呢? token流 第0行設定了編碼格式 第1行[0,5)字元是第1行第1個token 第1行[5,6)字元是第1行第2個token 第1行[6,30)字元是第1行第3個token "1982------Guido in cwi" 這是一個String(字元串) 第1行[30,31)字元是第1行第4個token 第1行[31,32)字元是第1行第5個token \n \n是一個NewLine(換行符) 換行符意味着第一行結束 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 組詞 詞分析出來就是怎麼組詞的問題 生成一棵抽象文法樹 AST(Abstract Syntax Tree) 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 引入ast子產品 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 流程 先把這個ast子產品導入(import)進來 第一句就是import ast 回車之後沒有任何報錯 那就是執行成功了 後面也一樣 沒有報錯就是執行成功了 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 然後讀取guido.py并送到s 然後對于s進行文法分析(parse) 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 再把分析(parse)的結果進行轉儲(dump) 看起來有點亂 更新Python 目前lanqiao.cn上面的python是3.8 這個清晰縮進的格式需要在3.9以上完成 需要更新 sudo apt update
sudo apt install python3.9
「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 縮進換行 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 這個就是把詞組成文法樹的樣子 如何了解這棵樹呢? 我們看一個例子 表達式運算 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 前兩個先結合 得到的結果作為下一個運算的左操作數 然後和第3個結合 結合序 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 後兩個會先結合 得到的結果作為下一個運算的右操作數 然後再和1進行加法運算 有了文法樹 這棵文法樹我們能看懂 但是cpu需要的是能執行的一條條位元組碼指令 翻譯成位元組碼 要把源程式翻譯成位元組碼才能執行 怎麼把ast轉化為位元組碼(指令)呢? 從一種語言到另一種語言 compile 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 編譯結果 編譯(compile)之後得到是位元組碼指令檔案 是以擴充名是pyc 其中c代表compiled pyc是位元組碼(bytecode)檔案 python虛拟機的虛拟cpu就可以直接執行了 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 先看看這個pyc檔案 注意他在__pycache__檔案夾下 cache的意思是緩存 pycache兩端各有2條下劃線(_) 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 進入__pycache__檔案夾 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 得到的位元組碼看起來完全是亂碼 vi打開這個這個pyc檔案 二進制形态 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 這樣看到了他的字元串形态 可以看到他的二進制位元組形态麼? 機器語言 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 指令 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 最早指的是教的行為或者過程 計算機領域裡面特指指令 比如加法指令 減法指令 可以讓cpu做特定運算的指令 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 由于計算機隻認識0和1 是以要把這些加加減減的指令 對應到0和1的二進制形态上去 0和1的二進制形态我們記不住 于是有了彙編助記符 助記符告訴我們這條0和1的二進制形态 到底對應什麼指令 助記符的語言就是彙編語言 彙編assemble 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 assemble指的是收集、集結 在計算機中特指彙編語言 可以讓我們把0和1的機器指令 收集起來形成的助記符集合 就是彙編語言指令集 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 反編譯 disassemble 這個詞由兩部分組成 dis (反着來的) dislike disgrace disagree assembler (彙編語言) 把py源檔案編譯成的位元組碼(指令)我們人類看不明白 把這些位元組碼(指令)反編譯(disassemble)成彙編語言助記符 有了助記符我們就知道指令的含義了 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 反編譯(dis) python3 -m dis guido.py -m 代表使用子產品 dis 代表反編譯(disassemble) 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 我們可以看見 前面是行号 每行對應4條指令 LOAD_NAME 裝載(函數)名字 LOAD_CONST 裝載常量 CALL_FUNCTION 調用函數 POP_TOP 彈棧 總共6句 那具體這個 LOAD_NAME 是要做些什麼呢? 指令 LOAD_NAME 把一個值壓入堆棧co_names 把print這個函數名壓入了堆棧 一會兒就要調用這個被壓入堆棧的print函數 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 python源頭 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 python 是開源程式設計語言 整個的源代碼都是開放的 我們可以去github找到他的源代碼 https://github.com/python/cpython 二進制狀态 搜尋LOAD_NAME并且排查 指令對應着一個位元組碼狀态值 https://github.com/python/cpython/blob/main/Lib/opcode.py 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 4條指令 指令助記符 指令含義 十進制狀态 十六進制狀态 LOAD_NAME 裝載函數名稱 101 0x65 LOAD_CONST 裝載參數 100 0x64 CALL_FUNCTION 調用函數 142 0x8e POP_TOP 彈棧傳回 1 0x01
「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 好像找到了 64XX 64 00是從表中的00号位置取得字元串"Guido in cwi" 64 01是從表中的01号位置取字元串"Guido in cnri" ... 以此類推,直到05 83取出字元串"Guido in microsoft" 0x83 對應的是 GET_AWAITABLE 那這些二進制代碼究竟是什麼指令集的呢? 首先我們得弄懂什麼是指令集呢? 指令集 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 上圖是arm的指令集 也常被稱作arm架構 那什麼又是架構呢? architect 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 架構師 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 虛拟機的虛拟cpu 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 總結 我們把python源檔案 詞法分析 得到 詞流(token stream) 文法分析 得到 抽象文法樹(Abstract Syntax Tree) 編譯 得到 位元組碼 (bytecode) 位元組碼我們看不懂 是以反編譯 得到 指令檔案(opcode) 「oeasy」python0010 - python虛拟機解釋執行py檔案的原理 指令檔案是基于python虛拟機的虛拟cpu的指令集 什麼是python虛拟機呢? 我們下次再說 藍橋->https://www.lanqiao.cn/teacher/3584 github->https://github.com/overmind1980/oeasy-python-tutorial gitee->https://gitee.com/overmind1980/oeasypython 視訊->https://www.bilibili.com/video/BV1CU4y1Z7gQ 作者:oeasy