圖解Linux是如何進(jìn)行函數(shù)調(diào)用的涩笤?

開篇依舊先提出幾個問題:

  1. 進(jìn)程虛擬地址空間是如何分布的?

  2. 函數(shù)調(diào)用的棧幀結(jié)構(gòu)是什么樣子盒件?

  3. 函數(shù)調(diào)用涉及到的寄存器都起了什么作用蹬碧?

  4. 函數(shù)參數(shù)是如何傳遞的?傳遞順序如何炒刁?

  5. 函數(shù)的返回值是如何傳遞的恩沽?

如果您對上述問題有些困惑,請繼續(xù)往下看吧翔始!

進(jìn)程的內(nèi)存布局

如圖:

image.png

高地址的一部分空間會分配給內(nèi)核罗心,稱為內(nèi)核空間里伯,剩下的內(nèi)存空間給用戶使用,稱為用戶空間渤闷。

用戶空間中有幾個主要的內(nèi)存區(qū)域:

  • 棧:用于維護(hù)函數(shù)調(diào)用的上下文疾瓮,離開了棧,函數(shù)調(diào)用就沒法實現(xiàn)飒箭,棧通常在用戶空間的最高地址處分配狼电,通常有數(shù)兆字節(jié)的大小。

  • 堆:堆用來容納程序動態(tài)分配的內(nèi)存區(qū)域弦蹂,程序中malloc或new分配的內(nèi)存就來自堆里肩碟。堆通常存在于棧的下方(低地址方向),在某些時候凸椿,堆也可能沒有固定統(tǒng)一的存儲區(qū)域削祈,堆一般比棧大很多,可以有百兆甚至幾G的大小削饵。

  • 動態(tài)鏈接庫映射區(qū):這個區(qū)域用于映射裝載的動態(tài)鏈接庫岩瘦,Linux下如果可執(zhí)行文件依賴其它共享庫,那系統(tǒng)就會在這個區(qū)域分配相應(yīng)空間窿撬,并將共享庫裝入該空間。

  • 可執(zhí)行文件映像:存儲著可執(zhí)行文件在內(nèi)存里的映像叙凡,由裝載器在裝載時將可執(zhí)行文件的內(nèi)存讀取或映射到這里劈伴。

  • 保留區(qū):保留區(qū)并不是一個單一的內(nèi)存區(qū)域,而是堆內(nèi)存中受到保護(hù)而禁止訪問的內(nèi)存區(qū)域的總稱握爷,例如在大多數(shù)操作系統(tǒng)里跛璧,極小的地址通常都是不允許訪問的,如NULL新啼,通常C語言將無效地址賦值為0也是出于這個考慮追城,因為0地址正常情況下不可能有有效的可訪問數(shù)據(jù)。

函數(shù)調(diào)用的棧幀結(jié)構(gòu)

我們都知道函數(shù)調(diào)用都是以棧幀為單位燥撞,機器通常用棧來傳遞函數(shù)參數(shù)座柱、保存返回地址、保存寄存器(即函數(shù)調(diào)用的上下文)及存儲本地局部變量等物舒。

一個單獨的棧幀結(jié)構(gòu)如圖所示:

image.png

為單個函數(shù)調(diào)用分配的那部分棧稱為棧幀色洞,棧幀的邊界由兩個指針界定:寄存器%ebp為幀指針,指向當(dāng)前棧幀的起始處冠胯,通常較為固定火诸;寄存器%esp為棧指針,指向當(dāng)前棧幀的棧頂位置荠察,當(dāng)程序執(zhí)行時置蜀,棧指針可以移動奈搜,因此大多數(shù)數(shù)據(jù)的訪問都是相對于幀指針的。

一次函數(shù)調(diào)用的棧幀圖如下:


image.png

寄存器使用約定

直接看圖:

image.png

圖片來源于網(wǎng)絡(luò)盯荤,侵權(quán)刪

上圖表達(dá)的應(yīng)該已經(jīng)很清楚啦馋吗,簡單示例解釋一下,函數(shù)調(diào)用需要傳遞參數(shù)時廷雅,第一個參數(shù)存到%edi里耗美,第二個參數(shù)會存到%esi里,如果有返回值會存到%eax里航缀,這里如果是64位的返回值商架,會使用%rax。

函數(shù)的調(diào)用約定

這里主要涉及三種約定:

  • 函數(shù)參數(shù)的傳遞順序和方式:這里可以有很多參數(shù)傳遞方式芥玉,棧傳遞和寄存器傳遞蛇摸,函數(shù)的調(diào)用方將參數(shù)壓入棧中,函數(shù)自己再從棧中將參數(shù)取出灿巧,需要規(guī)定壓棧的順序赶袄,是從左到右,還是從右到左抠藕,有的也使用寄存器傳遞饿肺,這都需要約定好。

  • 棧的維護(hù)方式:在函數(shù)將參數(shù)壓棧后盾似,函數(shù)體會被調(diào)用敬辣,此后需要將被壓入棧中的參數(shù)全部彈出,以使得棧在函數(shù)調(diào)用前后保持一致零院,這個彈出的工作可以是由函數(shù)的調(diào)用方完成還是函數(shù)本身來完成需要約定好溉跃。

  • 名字修飾策略:為了鏈接的時候?qū)φ{(diào)用約定進(jìn)行區(qū)分,需要對函數(shù)本身的名字進(jìn)行修飾告抄,不同的調(diào)用約定有不同的名字修飾策略撰茎。一般都是前面加個下劃線。

C語言默認(rèn)的調(diào)用約定是cdecl方式打洼,可以通過attribute___((cdecl))標(biāo)明使用cdecl約定龄糊,其實還有其它一些調(diào)用約定,如圖:


image.png

函數(shù)的返回值傳遞

這里有幾種情況:

4字節(jié):當(dāng)函數(shù)返回值是4個字節(jié)會通過%eax寄存器作為通道拟蜻,函數(shù)將返回值存儲在%eax中绎签,返回后函數(shù)的調(diào)用方再讀取%eax。

5-8個字節(jié):通過rax寄存器作為通道酝锅。

大于8個字節(jié):以如下代碼舉例:

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="c++" cid="n58" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">struct A {
// ...大于8字節(jié)
};
A func() {
A b;
return b;
}
A x = func();</pre>

返回值傳遞方式如圖:


image.png
  1. 調(diào)用函數(shù)首先在棧上額外開辟一片空間诡必,作為臨時對象(temp)

  2. temp作為隱藏參數(shù)傳遞給被調(diào)用函數(shù)

  3. 函數(shù)將數(shù)據(jù)拷貝給temp,同時%eax為指向temp的指針

  4. 返回返回后將%eax指向的temp拷貝回被賦予的對象

返回值類型的尺寸太大導(dǎo)致函數(shù)返回時,會開辟一段區(qū)域作為中介爸舒,返回值對象會被拷貝兩次蟋字,而C++在有些情況下會做返回值優(yōu)化,減少拷貝的次數(shù)扭勉,具體可以看我之前的文章:

參考資料

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

《程序員的自我修養(yǎng):鏈接裝載與庫》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鹊奖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子涂炎,更是在濱河造成了極大的恐慌忠聚,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件唱捣,死亡現(xiàn)場離奇詭異两蟀,居然都是意外死亡,警方通過查閱死者的電腦和手機震缭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進(jìn)店門赂毯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拣宰,你說我怎么就攤上這事党涕。” “怎么了巡社?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵膛堤,是天一觀的道長。 經(jīng)常有香客問我晌该,道長骑祟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任气笙,我火速辦了婚禮,結(jié)果婚禮上怯晕,老公的妹妹穿的比我還像新娘潜圃。我一直安慰自己,他們只是感情好舟茶,可當(dāng)我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布谭期。 她就那樣靜靜地躺著,像睡著了一般吧凉。 火紅的嫁衣襯著肌膚如雪隧出。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天阀捅,我揣著相機與錄音胀瞪,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛凄诞,可吹牛的內(nèi)容都是我干的圆雁。 我是一名探鬼主播,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼帆谍,長吁一口氣:“原來是場噩夢啊……” “哼伪朽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起汛蝙,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤烈涮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后窖剑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坚洽,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年苛吱,在試婚紗的時候發(fā)現(xiàn)自己被綠了酪术。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡翠储,死狀恐怖绘雁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情援所,我是刑警寧澤庐舟,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站住拭,受9級特大地震影響挪略,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜滔岳,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一杠娱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧谱煤,春花似錦摊求、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至硫惕,卻和暖如春茧痕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背恼除。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工踪旷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓埃脏,卻偏偏與公主長得像搪锣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子彩掐,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,509評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 棧: 在函數(shù)調(diào)用時构舟,第一個進(jìn)棧的是主函數(shù)中函數(shù)調(diào)用后的下一條指令(函數(shù)調(diào)用語句的下一條可執(zhí)行語句)的地址,然后是函...
    zjfclimin閱讀 3,949評論 0 5
  • 原文地址:C語言函數(shù)調(diào)用棧(一)C語言函數(shù)調(diào)用棧(二) 0 引言 程序的執(zhí)行過程可看作連續(xù)的函數(shù)調(diào)用堵幽。當(dāng)一個函數(shù)執(zhí)...
    小豬啊嗚閱讀 4,597評論 1 19
  • 閱讀經(jīng)典——《深入理解計算機系統(tǒng)》04 函數(shù)調(diào)用時的棧結(jié)構(gòu)變化是一個很有趣的話題狗超,本文就來詳細(xì)剖析這個過程。 棧幀...
    金戈大王閱讀 23,193評論 14 36
  • 如何理解函數(shù)調(diào)用過程朴下?本文把一個簡單的C語言程序匯編成目標(biāo)代碼努咐,然后用objdump目標(biāo)文件反編譯成的匯編代碼,從...
    Ericgogo閱讀 3,205評論 0 1
  • 在開始函數(shù)調(diào)用約定之前我們需要先了解一下幾個相關(guān)的指令 1.1 push pushq立即數(shù)# q/l是后綴殴胧,表示操...
    聯(lián)旺閱讀 882評論 0 0