函式壓棧及協議棧的基本技術

2021-07-10 05:21:51 字數 4266 閱讀 4165

在編譯器編譯檔案的時候,軟體會根據程式本身的要求對函式作不同的壓棧處理。有的壓棧是按照從左到右進行壓棧,有的壓棧是按照從右到左進行壓棧,有的不壓棧、直接用暫存器代替,有的是需要被呼叫函式自身自己平衡堆疊。下面,我們就可以乙個乙個自己看看。首先,隨便寫乙個函式,

[cpp]view plain

copy

intadd(

inta, 

intb)    

(1)從右到左壓棧

_cdelc是編譯器預設的一種壓棧方式,在函式上定義不定義其實意義不大。不過,我們願意貼上**說明一下,

[cpp]view plain

copy

int__cdecl add(

inta, 

intb)    

下面,在main函式裡面呼叫此add函式,我們看看對應的彙編是怎麼處理的,

[cpp]view plain

copy

14:       

intp = add(2, 3);  

00401068   push        3  

0040106a   push        2  

0040106c   call        @ilt+0(_add) (00401005)  

00401071   add         esp,8  

00401074   mov         dword ptr [ebp-4],eax  

15:       return

1;  

00401077   mov         eax,1  

從上面的**來看,3先壓棧,然後是資料2,很明顯的從右向左壓棧。

(2)從左向右壓棧

其實,在windows之前的編譯器是支援從左向右進行壓棧的,但是現在不支援了。比如說,如果你輸入下面這段**,

[cpp]view plain

copy

int__pascal add(

inta, 

intb)    

此時,編譯器會給你貼上乙個錯誤提示,error c4226: nonstandard extension used : '__pascal' is an obsolete keyword。提示說的很明白,__pascal是乙個過時的關鍵字,現在不支援了。其實堆疊壓棧從左向右、還是從右向左其實無所謂。但是如果遇到的函式是變參的話,那麼此時就存在問題了。因為對於__pascal而言,最後乙個引數不知道究竟是在ebp的哪個偏移位置了?

(3)用暫存器代替壓棧

用暫存器代替資料壓棧是arm、powerpc等cpu使用的比較多的一種方法。因為用暫存器代替壓棧,主要是考慮到速度方面的原因。畢竟取資料、儲存資料相比較暫存器操作還是非常耗時間的,其此就是這兩種cpu的暫存器資源特別豐富。同樣,首先我們要用__fastcall裝飾一下函式,

[cpp]view plain

copy

int__fastcall add(

inta, 

intb)    

那接下來,我們看看呼叫的時候發生了什麼變化,

[cpp]view plain

copy

14:       

intp = add(2, 3);  

00401068   mov         edx,3  

0040106d   mov         ecx,2  

00401072   call        @ilt+10(_add) (0040100f)  

00401077   mov         dword ptr [ebp-4],eax  

15:       return

1;  

0040107a   mov         eax,1  

和上面的壓棧不同,這裡用edx儲存了資料3,用eax儲存了資料eax。畢竟暫存器運算要比記憶體運算快得多。

(4)被呼叫和自行進行壓棧恢復

__stdcall是我們這裡講到的最後一種壓棧模式。在函式壓棧的方面,他和__cdelc是一樣的,但是關鍵就在add函式結束的位置發生了變化。首先,我們需要用__stdcall裝飾了一下函式,

[cpp]view plain

copy

int__stdcall add(

inta, 

intb)    

那麼此時函式彙編的時候,**發生了變化呢?

[cpp]view plain

copy

7:    

int__stdcall add(

inta, 

intb)  

8:      

0040103e   pop         edi  

0040103f   pop         esi  

00401040   pop         ebx  

00401041   mov         esp,ebp  

00401043   pop         ebp  

00401044   ret         8  

這裡的彙編**沒有什麼特別之處。但是最後乙個ret 8是什麼意思呢?其實因為之前有兩個引數a和b,那麼8就是這兩個引數占有的空間。此時ret b事實上就是讓ebp恢復到原來的空間。僅此而已。但是我們發現,可能這一步執行之後,ebp加了不是8,而是12,這又是為什麼呢?因為還有4個位元組的返回位址沒有加上呢。

現代數字通訊技術讓我們的生活發生了徹底地改變,而通訊協議無疑是這一改變的始作俑者。硬體有自己的通訊協議,比如pci匯流排、usb匯流排、i2c匯流排等等。軟體也有自己的協議棧,無線的3gpp、gprs,有線的tcp/ip協議棧、atm協議等等。基於物理層的協議,很大程度是由晶元完成的,但是涉及到交換路由、資料傳輸、業務處理,則很大程度上是由軟體負責的。協議棧看上去複雜,但是它所使用的技術都是一些基本技術,熟悉這些常用的技術和方法有利於我們在後面的開發中能夠高效的利用這些協議。那麼,下面我們就要看看,實現協議棧需要怎樣的一些基本技術。

(1)狀態機

狀態機是協議棧使用最多的一種方法。當協議處於不同的狀態的時候,就會對不同的報文內容作出不同的處理方法。

(2)定時器

計時器也是協議棧經常使用的方法。通常協議本身在某段時間內需要收到對端傳送的響應報文,如果沒有收到報文我們就認為通訊失敗。因此,我們完全可以通過設定定時器的方法,在一段時間之後判斷當前的報文有沒有傳送成功。

(3)重發機制

因為網路的鏈路狀態是十分複雜的,所以不同的協議對報文的響應時間是不同的。只要協議本身允許,一段時間內的重發都是可以的。

(4)校驗和

為了驗證報文在傳輸的過程中0和1沒有發生改變,在報文中新增校驗和也是十分必要的。這種校驗方法很多,奇偶校驗、crc校驗都是可以的。當然,至於具體使用哪種方法需要根據rfc標準來判斷。

(5)報文排序

在tcp/ip協議層中,ip層作用就是分片和路由的功能。我們知道有的時候傳送的報文是很長的,所以有必要對這些報文進行排序處理。在傳輸中,我們需要確認所有的報文都能正確地得到傳輸和處理。

(6)位元組序

x86的cpu是高位址高資料,而powerpc是低位址高資料。而報文中的內容需要的是低位址高資料,所以在處理的時候需要十分小心。

(7)其他的基本技術

7.1 互斥                                       傳輸涉及到多執行緒的設計

7.2 鍊錶                                        鍊錶是模組設計中的基本結構

7.3 最小生成樹演算法                      ospf中需要涉及到基本的圖論演算法

7.4 rfc標準                                   rfc是我們一切工作的準繩

7.5 開源軟體                               開源軟體可以幫助我們快速熟悉相關的開發工作

7.6 除錯日誌                               除錯日誌有助於我們對故障快速進行定位 

協議棧的基本技術

現代數字通訊技術讓我們的生活發生了徹底地改變,而通訊協議無疑是這一改變的始作俑者。硬體有自己的通訊協議,比如pci匯流排 usb匯流排 i2c匯流排等等。軟體也有自己的協議棧,無線的3gpp gprs,有線的tcp ip協議棧 atm協議等等。基於物理層的協議,很大程度是由晶元完成的,但是涉及到交換...

python完成棧的基本操作,壓棧,出棧 計算棧長

在python中不存在指標,所有需要進行鏈棧的操作時,自己初始化乙個棧的結點類,其中定義棧每乙個結點的屬性,乙個next,乙個資料 後邊的佇列和二叉樹的完成使用同樣的原理,自己定義每乙個資料結構的結點屬性值 順序棧的操作 棧的基本操作 class stacklist object def init ...

printf 函式壓棧方式

c語言,c 函式呼叫壓棧方式取決與編譯器。但是一般編譯器是右序壓棧的。下面介紹一下c 語言是如何右序壓棧的 如下函式 include int main 一般人會認為輸出結果是 3,4 5 可是實際結果卻是 4,3 5 為什麼呢,原因就取決於c 語言的函式壓棧方式是右序的。在比如有 int x 2 y...