[oeasy]python0011 - python虛擬機的本質(zhì)_cpu架構(gòu)_二進制字節(jié)碼_匯編語言

程序本質(zhì)
回憶上次內(nèi)容
我們把python源文件
詞法分析 得到 詞流(token stream)
語法分析 得到 抽象語法樹(Abstract Syntax Tree)
編譯 得到 字節(jié)碼 (bytecode)
字節(jié)碼我們看不懂
所以反編譯 得到 指令文件(opcode)

指令文件是基于python虛擬機的虛擬cpu的指令集
什么是python虛擬機呢???
在了解虛擬cpu之前
我們先看看真實的cpu
真實的cpu
無論手機還是計算機
最核心器件的器件就是cpu

這個東西是個實實在在存在的實體
我們所說的python虛擬機能看到么?
就是用來運行py文件的
python3
到底是個啥?
which python3 ll /usr/bin/python3
這個 python3
是一個符號鏈接文件
只有9字節(jié)
他指向 python3.8
python3.8
也在 /usr/bin 里面
就是/usr/bin/python3.8

python3.8是一個5.3M的文件
可以看得見
可以直接運行這個phthon3.8嗎?
直接運行
/usr/bin/python3.8
python3.8
就在硬盤里呆著usr 是 unix software resource
bin 是二進制 binary
python3.8 是這個文件的名稱
位置就在/usr/bin/python3.8
在運行命令的時候
把這個文件從硬盤裝載到內(nèi)存
然后用 cpu 開始逐行執(zhí)行文件中的0101指令
可以把他復(fù)制到shiyanlou用戶的宿主文件夾下嗎?
復(fù)制

復(fù)制到shiyanlou下
再觀察
把/usr/bin/python3這個py文件的解釋器拷貝到~(當(dāng)前用戶文件夾)cp的意思是copycp /usr/bin/python3 ~確認python3已經(jīng)拷到~(當(dāng)前用戶文件夾)ls的意思是listls ~/python3.8查看python3文件細節(jié)ls -lah ~/python3.8
python3 指向的 python3.8 只有 5.3M
這個可執(zhí)行文件怎么這么小?
5.3M 這也就是一張照片的大小
以前的 Python3.5 只有 4.3M
更小
目前這 5.3M 的 Python3 里面到底有什么呢???
研究 python3
用vi打開這個剛拷貝過來的python3vi ~/python3.8
這個樣子看起來
全是亂碼
完全看不懂啊

這個東西我們確實看不懂
但是有人能看懂
誰呢?
cpu
cpu能看懂!?。?/p>
這些我們看不懂的亂碼
cpu能看懂
這是屬于cpu的機器語言
這就是cpu的一條條的機器指令(instruction)

機器指令碼都是二進制形式的
我們嘗試把python3.8轉(zhuǎn)化為字節(jié)表現(xiàn)形式
以字節(jié)形式觀察python3.8
vi ~/python3.8
用vim打開~/python3.8

:
進入命令行模式
:%!xxd
我們可以看到這個文件的二進制形態(tài)%
是指的對于所有行的范圍!是執(zhí)行外部命令
xxd
指的是轉(zhuǎn)化為 16 進制形式什么是xxd命令呢?
xxd

xxd 可以查看文件的二進制形態(tài)
dump的本意是(傾倒垃圾)
這里指的是轉(zhuǎn)儲
把文件轉(zhuǎn)儲為16進制形式匯編代碼形式
:xxd –r
可以還原回去 ??:%!xxd
轉(zhuǎn)成字節(jié)形態(tài):%!xxd –r
轉(zhuǎn)回文本形態(tài)反復(fù)橫跳...
另存為python3.8hex
一行是(16)10 進制 ?個字節(jié)
G到最后一行
總共有 343148 行
這就是 真正的機器語言??
cpu能執(zhí)行的東西
真真切切看到了的
真的存在硬盤上 01010 的二進制可執(zhí)行指令??!
這些指令執(zhí)行出來就是我們的游樂場?。?!
或者說是我們的python虛擬機
可是這個指令我們看不懂怎么辦???
先把他另存出來
:w python3.8hex
把當(dāng)前緩存(buffer)另存(write)為
python3.8hex
對python3.8強制退出
:q!
不保存修改強制退出
python3.8hex就是我們要的機器語言的字節(jié)形態(tài)
可是這字節(jié)形態(tài)我們看不懂啊
匯編語言助記符
先把~/python3對應(yīng)的機器語言輸出為匯編指令形式(反匯編)objdump -d python3.8 > python3.8.asm vi python3.8.asm
這次真的可以看懂了
減法(sub)
移動(mov)
這些指令

可以發(fā)現(xiàn)當(dāng)前系統(tǒng)的架構(gòu)(指令集)是x86-64
這些和我們剛才的字節(jié)形態(tài)有關(guān)系嗎?
對比
用vi分窗口分別打開打開python3 和 python3.asm
vi -o python3.8hex python3.8.asm
下圖中上半部分是機器語言

上圖下半部分是機器語言對應(yīng)的匯編指令助記符
ctrl+j、ctrl+k可以上下切換
我們來試著找找
python3文件中
機器語言的0101和cpu的匯編指令的對應(yīng)關(guān)系??
找到了
先跳過下面窗格的第8行
endbr64 意味著 64位結(jié)束分支
下面的sub執(zhí)行的是減法

下面窗格的 第9行
/48 83
找到上下的對應(yīng)關(guān)系也就是第一條執(zhí)行的匯編指令減法(sub)
匯編指令是計算機 cpu 機器指令的助記符
查找對應(yīng)關(guān)系
423000
就是初始化(init)的 cpu 開始執(zhí)行指令的地址我們在上面查找48 83 有沒有對應(yīng)的字節(jié)
/4883 ec08 488b...
在上面的窗格中
搜索這些字節(jié)形態(tài)

好像找到了對應(yīng)關(guān)系
具體怎么對應(yīng)的呢?
這臺計算機用的是什么指令集呢?
什么是指令集來著?
指令集
指令集就是指令的集合

指令集也叫計算機的架構(gòu)
不同架構(gòu)的 cpu 有不同的指令集
我們目前的這個瀏覽器里面的系統(tǒng)用的是
x86-64
除此之外
arm
、MIPS
、RISC-V
也是常用的指令集指令助記符和機器語言到底是則怎么對應(yīng)的呢?
回到代碼
代碼會有不同的
section
模塊入口是
init
作用是初始化
initialization

模塊里面是具體的指令
比如第一句
48 83 ec 08
為什么48 83 就可以代表減法
這是誰規(guī)定的呢?
查看指令集
這是cpu架構(gòu)規(guī)定的
首先要明確到當(dāng)前機器cpu的架構(gòu)
反匯編里面說是x86-64

到shell里面驗證一下

當(dāng)前機器所用的架構(gòu)指令集確實是x86_64
這是誰的架構(gòu)呢?
搜索
不會了就去搜索??

去intel官網(wǎng)找指令集
查詢x86_64指令集
找到cpu的手冊
https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf
可以找到指令和二進制狀態(tài)之間的關(guān)系么?
先要找到x86-64指令集中 48 83 這條指令

注意上圖中
100B中的B是0或1
100B可以是1000
也可以是1001
這確實是一條減法指令
而且是8位立即數(shù)和寄存器的減法運算
逐步搜索

找起來真的很費勁
48 83 ec 08
對應(yīng)sub $0x8,%rsp
確實是一條減法指令
確實是8位立即數(shù)和寄存器的減法運算
和objdump的結(jié)果是一致的
廢話?。?!??
除了減法指令sub之外
還有什么別的指令呢?
各種cpu指令
指令那可還有很多的
有運算的
有移位的
加減乘除都有
這些指令的集合就是指令集
指令集就是cpu運行的基礎(chǔ)!
這些機器語言的指令不能在別的指令集架構(gòu)上運行么?
移植 port
想在別的指令集架構(gòu)上運行程序
就需要移植(port)
移植(port)指的是從一種指令集移植到另一種指令集
從這個詞的詞源
移植
port 港口
可以看出歐美的航海文化基礎(chǔ)
也可以看出我們的農(nóng)耕文化基礎(chǔ)
不移植會如何呢?
不移植
這是playstation2的架構(gòu)圖
cpu是mips架構(gòu)的
不移植的話
就是讓x86架構(gòu)的pc
去直接執(zhí)行這些基于mips架構(gòu)的的0101...
就像讓一個意大利泥瓦匠看一份中文寫成的烹飪書來砌墻
雞同鴨講
驢唇不對馬嘴
0101的文件執(zhí)行出來全是亂的
完全不能用
而且不全是軟件的問題
也涉及到硬件等方面
可能某個寄存器在新架構(gòu)中根本就不存在
架構(gòu)師
這個時候架構(gòu)師要解決相當(dāng)多的問題
很不容易的
落實到我們的python3.8游樂場
我們的python3.8就是這樣的一系列的cpu指令
可以解釋py文件的
python3.8 又是如何解釋py文件的來著?
python3 執(zhí)行過程
不管是python3這個游樂場
還是hello.py這個python程序
都在我們的硬盤上
先得把文件從硬盤讀到內(nèi)存
python3 執(zhí)行的過程大致是這樣
先把python3.8這個主解釋器加載到內(nèi)存中
然后在x86-64的cpu上執(zhí)行
模擬出一臺python虛擬機
準備開始對py文件解釋執(zhí)行
先編譯
然后把參數(shù)
hello.py
這個需要執(zhí)行的程序加載到內(nèi)存詞法分析 得到 詞流(token stream)
語法分析 得到 抽象語法樹(Abstract Syntax Tree)
編譯 得到 字節(jié)碼 (bytecode)
也就是編譯后的pyc文件
解釋執(zhí)行
不過這個pyc指令文件
是基于python虛擬機的虛擬cpu的指令集的
需要放到模擬好的python虛擬機中
一條條指令進行執(zhí)行
換句話說
簡化版的 hello.py 的執(zhí)行過程是:
給了
python3
一個參數(shù)hello.py
使用
python3
這個解釋器來解釋執(zhí)行hello.py
hello.py
中的語句一句句地依次解釋執(zhí)行全解釋完成后
退出python這個程序
把控制權(quán)交回到shell
這些都是基于解釋器python3.8的
先編譯成python虛擬機的字節(jié)碼
然后用python虛擬機解釋直接執(zhí)行
所謂的解釋器也是
而解釋器(python3)是在不同系統(tǒng)不同架構(gòu)的cpu語言上運行的
那不同的系統(tǒng)、cpu架構(gòu)
python都能正確地解釋么?
架構(gòu)的層次
不同架構(gòu)的 cpu 都可以運行 python
risc-v
arm
x64
mips
龍芯
不同系統(tǒng)的環(huán)境都可以運行 python
win
mac
linux
freebsd
跨架構(gòu)跨平臺原理
由于python3可以運行在不同的cpu架構(gòu)和系統(tǒng)上
所以同樣的py文件被加載之后
python程序可以對py文件跨架構(gòu)、跨系統(tǒng)進行解釋執(zhí)行
一次編寫到處運行
不同的架構(gòu)
二進制對應(yīng)的匯編指令都不一樣
怎么能正確解釋執(zhí)行同樣的python程序呢?
跨架構(gòu)跨平臺原理
/usr/bin/python3.8
本身是二進制文件是基于當(dāng)前操作系統(tǒng)當(dāng)前架構(gòu)編譯出來的可執(zhí)行二進制文件
不同的架構(gòu)有不同的編譯器
不同的編譯器編譯出來的python3.8
是不同的二進制指令序列
python3.8
構(gòu)建了一個運行時環(huán)境這個環(huán)境可以解釋讀到的
python語句
把
python語句
翻譯成系統(tǒng)能讀懂輸入輸出翻譯成當(dāng)前架構(gòu)能夠執(zhí)行的代碼
然后邊解釋邊執(zhí)行
恭喜您完成了非常燒腦一個實驗!
我們?nèi)タ偨Y(jié)吧?。?!
總結(jié)
python3
的程序是一個 5.3M 的可執(zhí)行文件objdump -d ~/python3 > python3.asm
python3
里面全都是 cpu 指令可以執(zhí)行的那種
我們可以把指令對應(yīng)的匯編找到
匯編語句是和當(dāng)前機器架構(gòu)的指令集相關(guān)的
uname -a
可以查詢指令集我們執(zhí)行的過程其實就
系統(tǒng)執(zhí)行
python3
這個可執(zhí)行文件給了
python3
一個參數(shù)hello.py
python3
對于hello.py
一句句的解釋執(zhí)行在顯示器輸出了
hello world
python3
執(zhí)行完畢把控制權(quán)交回給 shell
這就是我們執(zhí)行
hello.py
的過程為什么我們學(xué)編程總是從
hello world
開始呢???我們下次再說!??
藍橋->https://www.lanqiao.cn/teacher/3584
github->https://github.com/overmind1980/oeasy-python-tutorial
gitee->https://gitee.com/overmind1980/oeasypython