天天看點

sqlserver函數大全及舉例_圖解 | Linux是如何進行函數調用的?

 先抛出幾個問題:

  • 程序虛拟位址空間是如何分布的?
  • 函數調用的棧幀結構是什麼樣子?
  • 函數調用涉及到的寄存器都起了什麼作用?
  • 函數參數是如何傳遞的?傳遞順序如何?
  • 函數的傳回值是如何傳遞的?

如果您對上述問題有些困惑,請繼續往下看吧!

程序的記憶體布局

如圖:

sqlserver函數大全及舉例_圖解 | Linux是如何進行函數調用的?

高位址的一部分空間會配置設定給核心,稱為核心空間,剩下的記憶體空間給使用者使用,稱為使用者空間。

使用者空間中有幾個主要的記憶體區域:

  • 棧:用于維護函數調用的上下文,離開了棧,函數調用就沒法實作,棧通常在使用者空間的最高位址處配置設定,通常有數兆位元組的大小。
  • 堆:堆用來容納程式動态配置設定的記憶體區域,程式中malloc或new配置設定的記憶體就來自堆裡。堆通常存在于棧的下方(低位址方向),在某些時候,堆也可能沒有固定統一的存儲區域,堆一般比棧大很多,可以有百兆甚至幾G的大小。
  • 動态連結庫映射區:這個區域用于映射裝載的動态連結庫,Linux下如果可執行檔案依賴其它共享庫,那系統就會在這個區域配置設定相應空間,并将共享庫裝入該空間。
  • 可執行檔案映像:存儲着可執行檔案在記憶體裡的映像,由裝載器在裝載時将可執行檔案的記憶體讀取或映射到這裡。
  • 保留區:保留區并不是一個單一的記憶體區域,而是堆記憶體中受到保護而禁止通路的記憶體區域的總稱,例如在大多數作業系統裡,極小的位址通常都是不允許通路的,如NULL,通常C語言将無效位址指派為0也是出于這個考慮,因為0位址正常情況下不可能有有效的可通路資料。

函數調用的棧幀結構

我們都知道函數調用都是以棧幀為機關,機器通常用棧來傳遞函數參數、儲存傳回位址、儲存寄存器(即函數調用的上下文)及存儲本地局部變量等。

一個單獨的棧幀結構如圖所示:

sqlserver函數大全及舉例_圖解 | Linux是如何進行函數調用的?

為單個函數調用配置設定的那部分棧稱為棧幀,棧幀的邊界由兩個指針界定:寄存器%ebp為幀指針,指向目前棧幀的起始處,通常較為固定;寄存器%esp為棧指針,指向目前棧幀的棧頂位置,當程式執行時,棧指針可以移動,是以大多數資料的通路都是相對于幀指針的。

一次函數調用的棧幀圖如下:

sqlserver函數大全及舉例_圖解 | Linux是如何進行函數調用的?

寄存器使用約定

直接看圖:

sqlserver函數大全及舉例_圖解 | Linux是如何進行函數調用的?

圖檔來源于網絡,侵權删

上圖表達的應該已經很清楚啦,簡單示例解釋一下,函數調用需要傳遞參數時,第一個參數存到%edi裡,第二個參數會存到%esi裡,如果有傳回值會存到%eax裡,這裡如果是64位的傳回值,會使用%rax。

函數的調用約定

這裡主要涉及三種約定:

  • 函數參數的傳遞順序和方式:這裡可以有很多參數傳遞方式,棧傳遞和寄存器傳遞,函數的調用方将參數壓入棧中,函數自己再從棧中将參數取出,需要規定壓棧的順序,是從左到右,還是從右到左,有的也使用寄存器傳遞,這都需要約定好。
  • 棧的維護方式:在函數将參數壓棧後,函數體會被調用,此後需要将被壓入棧中的參數全部彈出,以使得棧在函數調用前後保持一緻,這個彈出的工作可以是由函數的調用方完成還是函數本身來完成需要約定好。
  • 名字修飾政策:為了連結的時候對調用約定進行區分,需要對函數本身的名字進行修飾,不同的調用約定有不同的名字修飾政策。一般都是前面加個下劃線。

C語言預設的調用約定是cdecl方式,可以通過__attribute__((cdecl))标明使用cdecl約定,其實還有其它一些調用約定,如圖:

sqlserver函數大全及舉例_圖解 | Linux是如何進行函數調用的?

函數的傳回值傳遞

這裡有幾種情況:

4位元組:當函數傳回值是4個位元組會通過%eax寄存器作為通道,函數将傳回值存儲在%eax中,傳回後函數的調用方再讀取%eax。

5-8個位元組:通過rax寄存器作為通道。

大于8個位元組:以如下代碼舉例:

struct A {// ...大于8位元組  };A func() {A b;return b;}A x = func();
           

傳回值傳遞方式如圖:

sqlserver函數大全及舉例_圖解 | Linux是如何進行函數調用的?
  1. 調用函數首先在棧上額外開辟一片空間,作為臨時對象(temp)
  2. temp作為隐藏參數傳遞給被調用函數
  3. 函數将資料拷貝給temp,同時%eax為指向temp的指針
  4. 傳回傳回後将%eax指向的temp拷貝回被賦予的對象

傳回值類型的尺寸太大導緻函數傳回時,會開辟一段區域作為中介,傳回值對象會被拷貝兩次,而C++在有些情況下會做傳回值優化,減少拷貝的次數,具體可以看我之前的文章:左值引用、右值引用、移動語義、完美轉發,你知道的不知道的都在這裡

參考資料

https://blog.csdn.net/slvher/article/details/8831885
https://blog.csdn.net/slvher/article/details/8831983
https://www.cnblogs.com/alantu2018/p/8465904.html
https://mp.weixin.qq.com/s/fpf4qRRLN3wVDUrWka3HfQ
https://mp.weixin.qq.com/s/j7SKtrMCmYs6g8yH75OH4A
https://www.sec4.fun/2018/05/29/stack/
https://murphypei.github.io/blog/2019/01/linux-heap
https://cloud.tencent.com/developer/article/1515763
《程式員的自我修養:連結裝載與庫》

推薦好文  點選藍色字型即可跳轉

☞ 專輯|Linux應用程式程式設計大全

sqlserver函數大全及舉例_圖解 | Linux是如何進行函數調用的?

☞ 專輯|手撕C語言

sqlserver函數大全及舉例_圖解 | Linux是如何進行函數調用的?

我是情報小哥,一名嵌入式玩家!

sqlserver函數大全及舉例_圖解 | Linux是如何進行函數調用的?

長按前往圖中包含的公衆号關注