深入理解電腦系統(3.3)---資料傳送(或者說複製)指令詳解

來源:互聯網
上載者:User

標籤:

 

本文轉載地址:http://www.cnblogs.com/zuoxiaolong/p/computer15.html

 

引言

 

  上一章我們已經介紹了組合語言的基礎部分,包括資料格式、寄存器以及運算元的標識方式,接下來我們就應該去認識一下組合語言當中的各個指令了。這些指令大多數都非常簡單,但是組合在一起卻能類比出我們程式當中想要的任何效果,確實是十分神奇的一件事。

  

資料傳送指令

 

  資料傳送指令的目的是為了將一個資料從一個位置複製到另外一個位置。既然如此,那麼資料傳送指令就會包含一個源運算元和一個目的運算元,指令會將原運算元的值複製到目的運算元並覆蓋。

  資料傳送指令一共可分為五種,分別是mov、movs、movz、push以及pop,下面LZ依次介紹一下這五個指令的作用。

  

mov指令

 

  mov指令的作用是將源運算元S中的資料複製到目的運算元D中,mov指令有一個資料格式和兩個運算元,因此一般的形式為[movx S D]。其中x為資料格式,S為源運算元,D為目的運算元。

  這裡舉一個簡單的例子,比如我們有一條指令為movl %edx %eax。那麼它的執行過程就如所示。

               

  可以看到,在指令執行之後,%edx寄存器當中的內容會被複製到%eax寄存器。需要一提的是,mov指令可以在後面加上任何資料格式,比如上面這一過程中,資料格式則為四個位元組,也就是雙字。因此不難推斷出,我們還可以使用movb和movw去複製一個位元組或者兩個位元組。

 

movs指令

 

  movs指令的作用是將源運算元S中的資料做符號擴充後,再複製到目的運算元D中,movs指令有兩個資料格式和兩個運算元,因此一般的形式為[movsxy S D]。其中x、y為資料格式,S為源運算元,D為目的運算元。其中x、y的組合一共有三種,分別是bw、bl、wl,這三個組合代表的意思分別是單位元組到雙位元組,單位元組到雙字以及雙位元組到雙字。

  這裡LZ依然舉一個例子,對於指令movswl %dx %eax來講,它的作用如所示。

               

  這裡為了可以看出符號位的擴充,因此LZ這裡使用了十六進位的整數表示方式。可以看到,movs指令將0x8FFF擴充以後存入%eax寄存器,其中%dx為寄存器%edx的後16位表示。

  

movz指令

 

  movz指令的作用是將源運算元S做零擴充後,再複製到目的運算元中。它與movs指令十分相似,也有兩個資料格式和兩個運算元,因此一般的形式為[movzxy S D]。其中x、y為資料格式,S為源運算元,D為目的運算元。其中x、y的組合一共有三種,分別是bw、bl、wl,這三個組合代表的意思分別是單位元組到雙位元組,單位元組到雙字以及雙位元組到雙字。

  這裡依然採用相似的樣本,我們來看看對於指令movzwl %dx %eax來講,它的作用與上面的movs有何不同。

               

  可以看出,movz與movs指令是十分相似的,只是這裡擴充後,目標寄存器%eax的前16位為0而不再是1。

 

push指令

 

  push指令與上面的mov族指令有著不同,它的目的運算元被固定為棧頂,因此它的指令當中沒有目的運算元。另外有一點需要注意的是,它在進行複製操作之前,需要移動棧頂指標(-4)。push指令的一般形式為[pushl S],其中l代表資料格式為雙字,S為源運算元,目的運算元預設為棧頂。

  這裡LZ舉一個簡單的例子,比如pushl %edx這條命令,它的任務是將%edx寄存器的值複製到棧頂。我們首先來看一下命令執行前,寄存器以及儲存空間的狀態。

  可以看到,寄存器%ebp和%esp分別指向幀指標和棧指標,而%esp實際上就是指向的棧頂。由於現在棧頂位於-16的位置,因此若要將%edx壓入棧,則先需要將棧頂移動到-20的位置,然後再進行複製,移動後的狀態如所示。

  可以看到,這裡棧指標的位置已經發生了變化,向下移動了四位,並且將%edx寄存器的值放入新的棧頂,因此pushl %edx指令就相當於下面兩條指令。

                  subl $4,%esp

                  movl %edx,(%esp)

  這裡可以看出來,其實pushl指令做了一個隱藏操作,就是移動棧指標(-4),這一點希望各位猿友們注意。

 

pop指令

 

  pop指令與push指令是做的相反的操作,一個是入棧一個是出棧。對於pop指令來講,它的源運算元被固定為棧頂,相反,它會先進行複製操作,然後再移動棧指標。pop指令的一般形式為[popl D],其中l代表資料格式為雙字,D為目的運算元,源運算元預設為棧頂。

  接下來我們舉一個例子,與上面的例子類似,我們考慮popl %edx這條指令的效果,它會將棧頂的值彈出到寄存器%edx。首先來看執行之前,寄存器以及儲存空間的狀態。

  接下來執行pop指令時,會先將棧頂的值複製到%edx,然後再將棧指標移動(+4)。我們來看一下它執行後的狀態。

  可以看到,之前棧頂的內容已經被彈出到%edx寄存器,並且當前棧頂已經移動到了-16的位置,也就是進行了+4操作。因此popl %edx指令就相當於下面兩條指令。

                  movl (%esp),%edx

                  addl $4,%esp     

  這裡可以看出來,其實popl指令也同樣做了一個隱藏操作,就是移動棧指標(+4)。

 

資料複製樣本

 

  上面我們已經瞭解了幾乎所有的資料複製指令,接下來我們寫一小段程式,來看下這些資料複製指令,如何完成我們的程式操作。

simple(int *xp,int y){    int t = *xp;    *xp=y;    return t;}

  上面是一個簡單的C程式sum.c,它其中包含了一些賦值操作,我們來看看它的彙編代碼。使用GCC -O1 -S sum.c來擷取我們的彙編代碼,並使用cat sum.s來查看一下。

    .file    "sum.c"    .text.globl simple    .type    simple, @functionsimple:    pushl    %ebp    movl    %esp, %ebp
//以上為棧的建立部分 movl 8(%ebp), %edx movl (%edx), %eax movl 12(%ebp), %ecx movl %ecx, (%edx)
//以下為棧的完成部分 popl %ebp ret .size simple, .-simple .ident "GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3" .section .note.GNU-stack,"",@progbits

  分析這段彙編代碼的時候,我們應該分為三個部分來看待,首先是棧的建立、然後是使用、最後是完成部分。可以看到,裡面幾乎全是資料複製指令,我們先來看看棧的建立部分。

  其實對於一開始pushl和movl指令來講,它主要做了兩件事。第一個是將原來的幀指標備份到棧頂,然後再將幀指標和棧指標統一指向這個新的棧頂,也就是完成了一個新棧的建立。它在完成後,棧的狀態如下所示。

  可以看到,寄存器%ebp和寄存器%esp都指向當前幀指標的位置,其中變數xp位於+8的位置,而y位於+12的位置。由於xp是一個指標變數,因此它會指向一個記憶體中的地區,其中的值為*xp。

  瞭解完寄存器和儲存空間的狀態,此時棧已經建立完畢,接下來我們看緊接著的一句彙編代碼的作用。

    movl    8(%ebp), %edx

  這一句將記憶體位址為%ebp+8的值複製到%edx,很明顯,從上面的圖中可以看出,%ebp+8這個位置儲存著xp變數。這一句指令做了一個簡單的操作,就是將xp提取到%edx寄存器,如下所示。

  此時已經將%edx的值改為了變數xp,看接下來的一句操作。

movl (%edx), %eax

  這一句將記憶體位址為%edx的值賦給寄存器%eax,並準備傳回值。此時%edx寄存器的值已經改為了xp變數,因此(%edx)其實就是*xp,而%eax寄存器一般會作為函數的傳回值,因此它其實替代了臨時變數t。執行後的狀態如下所示。

  此時其實已經完成了程式中的int t = *xp以及為return t準備好了傳回值,接下來的一句彙編代碼作用也非常簡單,如下。

    movl    12(%ebp), %ecx

  它的作用是將地址為%ebp+12的值複製到寄存器%ecx,可以看出,%ebp+12就是儲存的變數y。因此它的作用就是將y複製到寄存器%ecx,如下所示。

  上面這一步挺簡單,我們來看最後一步操作,如下。

    movl    %ecx, (%edx)

  它的作用是將%ecx寄存器的值複製到記憶體中%edx的位置。此時%ecx的值為y,而%edx中為xp,因此目的運算元則為xp指向的位置,也就是*xp。這一句話執行的就是程式碼當中,*xp=y這個操作,它執行後的狀態如下所示。

  可以看到,在執行了*xp=y以後,xp指標所指向的位置,其值已經變為了y。此時程式其實已經基本運行完畢,剩下的工作也就是棧的完成操作了,也就是popl指令。在棧完成之後,也就是pop指令執行之後,當前幀會恢複到調用者的幀上面去,如下所示。

  此時當前幀已經恢複到了調用者的幀,最後ret指令會改變程式計數器(PC)的值,然後跳出子函數,繼續執行調用者當中的代碼。到此,我們的資料複製樣本就結束了,儘管這個例子並不難,但是麻雀雖小五髒俱全,如果理解了這個過程,相信就算是再複雜一些的彙編指令,也只是分析的時間長一點罷了。

 

文章小結

 

  本章內容比較長,LZ為了便於理解,插入了不少圖,希望對各位有協助,不過這些圖是真不好畫,浪費了LZ不少時間。本次主要介紹了一些資料複製指令,都是複製來複製去的,下一章我們將討論一下有關計算的指令,那裡會有一些加減乘除、移位、取地址等操作。

深入理解電腦系統(3.3)---資料傳送(或者說複製)指令詳解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.