一步步编写操作系统 11 实模式下程序分段的原因
cpu中本來是沒有實模式這一稱呼的,是因為有了保護模式后,為了將老的模式區別開來,所以稱老的模式為實模式。這情況就像所有同學坐在同一個教室里,本來沒有老同學這一概念,但某天老師領著一個陌生人進入教室并和大家宣布:“這是新轉到我們班的韓梅梅,大家歡迎新同學”。得,無形之中,大伙兒就成了老同學。
實模式的“實”體現在:程序中用到的地址都是真實的物理地址,“段基址:段內偏移”產生的邏輯地址就是物理地址,也就是程序員看到的完全是真實的內存。
不過要說實模式,咱們還得從cpu的發展說起,任何事物發展到今天,都是有一段“合理”的過程,了解這一過程是怎么來的,有助于理解它今天的形態。
不知道各位同學當初學習匯編語言時有沒有這樣疑問:“老師都是拿8086型號的cpu舉例,為什么不拿最新型號的呢?用那么古老的cpu講解知識,是否已經落伍太久了,我們學習的知識到社會上能用嗎?”我記得當初學習匯編時,那時的cpu都是奔騰2.8了。我帶著這樣的疑問請教了老師,老師回答我說:“8086是intel歷史上第一個x86的cpu,也就是自那以后的cpu稱為286、386、486、586…即使是現在的奔騰也是屬于x86體系,道理是不變的,而且,用最簡單的8086 cpu學習,這才更容易理解和看透cpu運行機制。”一番話徹底打消了我的疑慮,自那以后我才理解x86中的x原來是個變量^_^,它是指代intel所有86系列的產品。
在8086之前的cpu是什么樣呢?為什么8086就可以稱為cpu界的里程碑呢?原因是這樣的,在它之前的cpu前輩們,對內存的訪問比8086還要“實誠”,它們沒有段的概念,程序中要訪問內存,需要把地址寫死,也就是所謂的“硬編碼”,這其實很麻煩的,首先程序無法重定位,必須加載到內存中固定的位置,如果在此位置有其它程序在用,得,您先睡會,等它運行完成后我叫您。您看,得等人家運行完了騰出內存后才輪得到自己,可見程序對地址的依賴性之強。當可用內存很多但卻因為某一個字節的內存被占用而讓后來的程序等很久,這是很平常的事。有些開發人員等不及了,干脆把程序中的地址改成別的吧,重新編譯后發現還是有某個地址被占用,還是沒法上cpu運行,怎么辦?再改地址…所以我估計那時的開發人員脾氣都會很差,這人脾氣差就容易傷肝,肝火一旺就會兩鬢斑白,所以IT工程師還是很值得體恤的。
看著越來越多的程序員兩鬢斑白,intel早期的工程師難以承受內心的自責,不顧自己的滿頭白發,熬了無數通宵之后,終于發明了“段”,即cpu訪問內存用“段+偏移”的形式。這就是前面曾經講解過訪問內存用“段基址:段內偏移地址”的策略,它就是首次在8086上出現的。自那之后的cpu都是用這類思想訪問內存,只是在形式上有所小改動,難怪8086如此極富盛名了。為了支持段機制,cpu中新增了段寄存器,如cs、ds、es等。
8086的地址總線是20位寬,也就是其尋址范圍是2的20次方=1M。但其內部寄存器都是16位的,若用單一寄存器來尋址的話,只能訪問到2的16次方等于64K的空間。
由于地址線位寬和寄存器位寬是沒有必然聯系的,所以大家不要覺得為什么寄存器不是20位的?這樣通過寄存器尋址就能訪問到1M空間了,顯得多“配套”。
如8086的多種尋址方式中,有一種是基址尋址,這是用基址寄存器bx或bp來提供偏移地址。如mov [bx], 0x5;這條指令便是將立即數0x5存入ds:bx指向的內存。
大家看,bx寄存器是16位的,它最大只能表示0xFFFF的地址,也就是單一的一個寄存器無法表示20位的地址空間:1M。也許有人會說,段基址和段內偏移地址都搞到最大,都為0xFFFF。對不起,方案不成立,首先說會溢出,結果是0xFFFE,地址不增反小了個1。即使不溢出的話,其結果也只是由16位變成了17位,即兩個n位的數字無論多大,其相加的結果也超不過n+1位,道理很簡單,即使兩個數都是n位能表示的最大數,兩個相同的數相加,相當于乘以2,也就是數值上等于左移一位而已。依然無法訪問20位的地址空間。也許有同學又有好建議了:cpu的尋址方式又不是僅僅這一種,上面的限制是因為寄存器是16位,只要不全部通過寄存器尋址不就行了嗎。段寄存器也是寄存器,同樣也是16位,既然它必須得用,那就在偏移地址上下功夫,不要把偏移地址寫在寄存器里了,把它直接寫成20位立即數不就行啦。如mov ax, [0x12345],這樣最終的地址是ds+0x12345,肯定是20位,解決啦。不錯,這種是直接尋址方式,至少道理上講得通,這是通過編程技巧來突破這一瓶頸,能想到這一點我覺得非常nice。但是做為一個嚴謹的cpu,既然宣稱支持通過寄存器來尋址,那就要能夠自圓其說才行,不能靠程序員的軟實力來克服cpu自身的缺陷。于是,一個大膽的想法出現了。
為了讓16位的寄存器尋址能夠訪問20位的地址空間(注意啦親,我這里一直說的是通過寄存器尋址,因為只有通過16位的寄存器去尋址才會受到16位的限制),cpu工程師定位到根本瓶頸是在段寄存器,它要是能提供20位的段地址,哪怕偏移地址是1也照樣可以訪問到內存的各個角落。于是,通過先把16位的段基址左移4位后變成20位,再加段內偏移地址,這樣便形成了20位地址,只要保證了段基址是20位的,偏移地址是多少位都不關心了,從而突破了16位寄存器做為偏移地址而無法訪問1M空間的限制。
有了20位地址便能訪問到20位的空間,雖然解決了一個大問題,但是引入了一個小問題。還拿0xFFFF來說,現在能訪問的最大的地址是0xFFFF:0xFFFF,經過左移段基址4位后得到的最大地址是:0xFFFF*16+0xFFFF=0xFFFF0+0xFFFF=0xFFFFF+0xFFF0=1M+16*4k-16-1=0x10FFEF。這公式有點暈是嗎?其實最后結果是0x10FFEF是最重要的,前面的推算就是想告訴大家,按照新方法獲取地址,可以得到的最大地址是1M+64K-16字節,因為這是空間范圍,所以要減去1得到地址范圍。
大家看到了,當初費了好大周折才搞定了能夠訪問20位地址空間,現在反而有點過了,過頭的原因是段基址為0xFFFF0,偏移地址應該小于等于F就對啦,而這個偏移地址卻是0xFFFF,超出了0xFFF0的空間,也就是多出來的64K-16字節,這部分內存就是傳說中的高端內存區HMA(High Memory Area)。可是這部分內存不存在,怎么處理呢。
答案說出來嚇你一跳:不用處理,哈哈。您想,8086一共就20條地址,地址線是從0開始的,即A0~A19,所以其地址空間才是1M的啊。內存地址0xFFFFF+是要用到A20地址線,可是8086它沒有啊,只能接收20位長的地址。所以由于超過了20位而產生的進位,就給丟掉了。其作用相當于把地址對1M取模了。舉例,如0xFFFFF+2,理論上是變成了0x100001。但由于只能容納20位長的數據,所以最終結果是0x00001。這是地址回卷的效果:即超過最大范圍后,從0重新開始計數。回卷英文稱為wrap-around,示意如圖
?
這就引出了從實模式到保護模式要打開A20地址線的問題,不過這部分在講保護模式時咱們再說。兄弟們下次再來玩兒喲。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的一步步编写操作系统 11 实模式下程序分段的原因的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最接近Android 13正式版的系统来
- 下一篇: 一步步编写操作系统 10 cpu的实模式