欧美性猛交xxxx免费看_牛牛在线视频国产免费_天堂草原电视剧在线观看免费_国产粉嫩高清在线观看_国产欧美日本亚洲精品一5区

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

函數(shù)調(diào)用時(shí)底層會(huì)發(fā)生什么

jf_78858299 ? 來源:碼農(nóng)的荒島求生 ? 作者:碼農(nóng)的荒島求生 ? 2023-02-17 14:47 ? 次閱讀

有讀者問題函數(shù)調(diào)用是如何實(shí)現(xiàn)的,今天就來聊聊這個(gè)比較簡(jiǎn)單的問題。

大家都應(yīng)該打包過東西吧,搬家之類的,通常都是找?guī)讉€(gè)箱子一股腦裝進(jìn)去,為了不讓箱子占地方,你通常會(huì)把它們摞好,就像這樣:

最先被打包好的箱子被摞在最下方,剛打包好的箱子總是放在最上方,這就形成了一種first in last out的結(jié)構(gòu),也就是我們所說的棧,stack,上面的這些箱子就形成了棧。

如果你懂得用箱子打包東西,你就能明白函數(shù)調(diào)用是怎么一回事。

原來,在程序運(yùn)行時(shí)每個(gè)被調(diào)用的函數(shù)都有自己的一個(gè)箱子,假設(shè)這段代碼是這樣寫的:

void D() {}
void C() {
D();
}
void B() {
C();
}
void A() {
B();
}

函數(shù)A調(diào)用函數(shù)B、B調(diào)用C、C調(diào)用D,那么當(dāng)函數(shù)D在運(yùn)行時(shí)內(nèi)存中就會(huì)有四個(gè)箱子,每個(gè)函數(shù)一個(gè):

圖片

每個(gè)函數(shù)占據(jù)的這個(gè)箱子——也就是這塊內(nèi)存,就被稱為棧幀,stack frame,只不過由于引力的作用,我們摞箱子時(shí)是從下往上增長(zhǎng),而出于內(nèi)存布局的需要,函數(shù)調(diào)用時(shí)的棧是從高地址向低地址增長(zhǎng)。

這些箱子中都裝有什么呢?你在函數(shù)中定義的局部變量就裝在這里,關(guān)于棧幀內(nèi)容更詳細(xì)的講解你可以參考這里《函數(shù)調(diào)用是在內(nèi)存中是什么樣子》,這些不是本文的重點(diǎn),這里更關(guān)心的是這些棧幀是怎樣增長(zhǎng)以及減少的。

仔細(xì)觀察上面這張圖,每個(gè)箱子最重要的信息有兩個(gè), 你至少需要知道箱子的底部以及箱子的頂部在哪里 !

圖片

在計(jì)算機(jī)中,每個(gè)函數(shù)棧幀的“底部”和“頂部”的信息——也就是內(nèi)存地址,分別存放在兩個(gè)寄存器中:BasePointer(BP)寄存器以及StackPointer(SP)寄存器,即我們熟悉的rbp以及rsp,32位下為ebp以及esp,注意本文以x86_64為例。

圖片

只要確定了rbp和rsp你就能得到一塊棧區(qū),在這塊棧區(qū)上就可以進(jìn)行函數(shù)調(diào)用:

圖片

讀到這里肯定有的同學(xué)可能會(huì)問,CPU中的寄存器不是有限的嗎?從這里的講解看每個(gè)棧幀都需要維護(hù)一個(gè)“棧頂”與“棧底”的信息,每個(gè)核心中的rbp以及rsp寄存器就一個(gè),我們?cè)撛鯓哟_保函數(shù)運(yùn)行時(shí)相應(yīng)棧幀使用的rbp以及rsp是正確的呢?

方法非常簡(jiǎn)單,調(diào)用函數(shù)時(shí)會(huì)創(chuàng)建新的棧幀,此時(shí)需要將原有rbp寄存器中的值保存在新的棧幀上,就像這樣:

圖片

上圖就是函數(shù)調(diào)用時(shí)第一件要完成的事情,把rbp的值push到棧上,rsp下移,然后呢?然后也很簡(jiǎn)單,只需要把rsp指向的地址也賦值給rbp即可,這樣就開啟了一個(gè)新的棧幀:

圖片

完成上述操作的有兩條機(jī)器指令(gcc編譯器):

push   %rbp
mov %rsp,%rbp

如果你去看編譯器為每個(gè)函數(shù)生成的機(jī)器指令,那么開頭幾乎都是這兩條指令,現(xiàn)在你應(yīng)該明白這兩條指令的作用了吧。

這兩條指令就把上一個(gè)棧幀的rbp的保存到了新的棧幀,由于此時(shí)rsp已經(jīng)指向了新的棧幀棧頂,由于此時(shí)棧為空,因此棧頂和棧底的地址是一樣的,可以直接把rsp賦給rbp,這樣一個(gè)全新的棧幀就創(chuàng)建出來了。

如果我們?cè)诒徽{(diào)函數(shù)內(nèi)部創(chuàng)建一些局部變量:

void funcB() {
int a = 1;
int b = 2;
int c = 3;
...
}

那么此時(shí)棧會(huì)進(jìn)一步擴(kuò)大,并把局部變量存放在該函數(shù)的棧幀中:

圖片

現(xiàn)在我們的??梢噪S著函數(shù)調(diào)用而增長(zhǎng),可以看到,棧幀和你搬家時(shí)用的紙箱子還是不太一樣的,函數(shù)棧幀不會(huì)一開始就大小固定好,而是隨著指令的執(zhí)行動(dòng)態(tài)增加,也就是如果你往棧上push一些數(shù)據(jù),棧幀就會(huì)相應(yīng)的增大一點(diǎn)。

那么函數(shù)調(diào)用完成時(shí)該怎么辦呢?這也非常簡(jiǎn)單,只需要一條機(jī)器指令:

leave

我們?cè)谏弦黄獥^(qū)分配內(nèi)存快還是堆區(qū)分配內(nèi)存快中講解了一部分,leave指令的作用是將?;焚x值給rsp,這樣棧指針指向上一個(gè)棧幀的棧頂,然后pop出rbp,這樣rbp就指向上一個(gè)棧幀的棧底:

圖片

看到了吧,執(zhí)行完leave指令后rbp以及rsp就指向了上一個(gè)棧幀,這就相當(dāng)于棧幀的彈出,這樣stack 1占用的內(nèi)存就無(wú)效了,沒有任何用處了,顯然這就是我們常說的內(nèi)存回收,因此簡(jiǎn)單的一條leave指令即可把棧區(qū)中的內(nèi)存回收掉。

圖片

而在x86平臺(tái),leave指令后往往跟上一條ret指令:

leave
ret

我們已經(jīng)了解了leave指令的作用,這條指令讓rbp以及rsp指向上一個(gè)棧幀,然后呢?顯然CPU應(yīng)該從funcA調(diào)用函數(shù)funcB之后的一行代碼處繼續(xù)運(yùn)行,那么這行代碼的地址在哪里呢?顯然就在funcA棧幀的棧頂:

圖片

當(dāng)CPU執(zhí)行call指令時(shí)會(huì)把該函數(shù)的返回地址push到棧中,而ret指令的作用正是將棧頂彈出(pop)到rip寄存器,rip寄存器告訴CPU接下來該從哪里執(zhí)行機(jī)器指令,這個(gè)返回地址是funcA調(diào)用funcB時(shí)push到棧上的,這樣當(dāng)從函數(shù)funcB()返回后我們就知道該從哪里繼續(xù)執(zhí)行機(jī)器指令了,這就是ret指令的作用,當(dāng)然這里也是函數(shù)調(diào)用實(shí)現(xiàn)的基本原理。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 程序
    +關(guān)注

    關(guān)注

    117

    文章

    3798

    瀏覽量

    81457
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4346

    瀏覽量

    63013
  • 函數(shù)調(diào)用
    +關(guān)注

    關(guān)注

    0

    文章

    19

    瀏覽量

    2607
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    C語(yǔ)言使用函數(shù)調(diào)用的知識(shí)點(diǎn)

    C語(yǔ)言使用函數(shù)調(diào)用,我們?cè)偈煜げ贿^了,但是函數(shù)調(diào)用在內(nèi)存中究竟發(fā)生了什么真的清楚嗎?只有搞清楚內(nèi)存里的內(nèi)幕,才算完全搞懂
    發(fā)表于 09-07 11:47 ?882次閱讀

    C函數(shù)調(diào)用機(jī)制與棧幀原理詳解

    當(dāng)一個(gè)C函數(shù)調(diào)用時(shí)函數(shù)的參數(shù)如何傳遞、堆棧指針如何變化、棧幀是如何被建立以及如何被消除的,一直缺乏系統(tǒng)性的理解,因此決定花時(shí)間學(xué)習(xí)下函數(shù)調(diào)用時(shí)
    發(fā)表于 06-08 10:49 ?1483次閱讀
    C<b class='flag-5'>函數(shù)</b><b class='flag-5'>調(diào)用</b>機(jī)制與棧幀原理詳解

    如何查看及更改函數(shù)/函數(shù)塊的調(diào)用環(huán)境

    是循環(huán)執(zhí)行,當(dāng)一個(gè)功能塊被多個(gè)外部函數(shù)/函數(shù)調(diào)用時(shí),我們應(yīng)如何查看某一次調(diào)用時(shí)的內(nèi)部變量呢?這涉及到函數(shù)塊的
    的頭像 發(fā)表于 11-17 09:08 ?1047次閱讀
    如何查看及更改<b class='flag-5'>函數(shù)</b>/<b class='flag-5'>函數(shù)</b>塊的<b class='flag-5'>調(diào)用</b>環(huán)境

    如果使用FCALL調(diào)用函數(shù)而使用RET返回的話, 就會(huì)發(fā)生CSA泄露怎么解決?

    FCALL調(diào)用函數(shù)不會(huì)自動(dòng)存儲(chǔ)Upper Context, 需要使用FRET進(jìn)行返回, 如果使用FCALL調(diào)用函數(shù)而使用RET返回的話, 就會(huì)發(fā)生
    發(fā)表于 01-26 07:57

    應(yīng)用程序調(diào)用底層驅(qū)動(dòng)

    本片主要講述了嵌入式linux操作系統(tǒng)的上層應(yīng)用程序是如何調(diào)用底層驅(qū)動(dòng)程序的。
    發(fā)表于 03-14 15:00 ?0次下載

    03 底層庫(kù)函數(shù)

    03 底層庫(kù)函數(shù)
    發(fā)表于 10-11 09:29 ?7次下載
    03 <b class='flag-5'>底層</b>庫(kù)<b class='flag-5'>函數(shù)</b>

    內(nèi)聯(lián)函數(shù)和外聯(lián)函數(shù)有什么區(qū)別

    內(nèi)聯(lián)函數(shù)是指用inline關(guān)鍵字修飾的函數(shù)。在類內(nèi)定義的函數(shù)被默認(rèn)成內(nèi)聯(lián)函數(shù)。內(nèi)聯(lián)函數(shù)從源代碼層看,有
    發(fā)表于 12-15 11:52 ?5930次閱讀
    內(nèi)聯(lián)<b class='flag-5'>函數(shù)</b>和外聯(lián)<b class='flag-5'>函數(shù)</b>有什么區(qū)別

    詳解python普通函數(shù)創(chuàng)建與調(diào)用

    函數(shù)是一種僅在調(diào)用時(shí)運(yùn)行的代碼塊。您可以將數(shù)據(jù)(稱為參數(shù))傳遞到函數(shù)中,然后由函數(shù)可以把數(shù)據(jù)作為結(jié)果返回。
    的頭像 發(fā)表于 03-01 16:32 ?1911次閱讀

    C語(yǔ)言使用函數(shù)調(diào)用在內(nèi)存中究竟發(fā)生了什么?

    C語(yǔ)言使用函數(shù)調(diào)用,我們?cè)偈煜げ贿^了,但是函數(shù)調(diào)用在內(nèi)存中究竟發(fā)生了什么真的清楚嗎?只有搞清楚內(nèi)存里的內(nèi)幕,才算完全搞懂
    的頭像 發(fā)表于 01-13 14:09 ?1253次閱讀

    C語(yǔ)言函數(shù)調(diào)用的形式及過程

    C語(yǔ)言函數(shù)調(diào)用時(shí)的數(shù)據(jù)傳遞 在調(diào)用有參函數(shù)時(shí),主調(diào)函數(shù)和被調(diào)函數(shù)之間有數(shù)據(jù)傳遞關(guān)系。
    的頭像 發(fā)表于 03-10 14:28 ?1910次閱讀

    什么是函數(shù)調(diào)用?

    函數(shù)調(diào)用,就是使用我們已經(jīng)定義好的函數(shù),或者C語(yǔ)言自帶的庫(kù)函數(shù)。
    的頭像 發(fā)表于 04-04 17:21 ?6001次閱讀

    SCL中調(diào)用函數(shù)的示例

    在此,可插入函數(shù) (FC) 調(diào)用函數(shù)塊 (FB) 調(diào)用。函數(shù)塊可作為單實(shí)例、多重實(shí)例或參數(shù)實(shí)例進(jìn)行調(diào)用
    的頭像 發(fā)表于 06-06 10:18 ?2329次閱讀

    網(wǎng)絡(luò)系統(tǒng)調(diào)用網(wǎng)絡(luò)套接字入口函數(shù)

    調(diào)用的應(yīng)用層接口函數(shù),第二個(gè)參數(shù)是一個(gè)指針,指向具體被調(diào)用函數(shù)(如accept函數(shù))所需要的參數(shù)。 這些在用戶系統(tǒng)
    的頭像 發(fā)表于 07-24 11:02 ?511次閱讀

    ES32F36xx芯片發(fā)生HardFault異常時(shí)的函數(shù)調(diào)用關(guān)系及問題定位

    ES32F36xx芯片發(fā)生HardFault異常時(shí)的函數(shù)調(diào)用關(guān)系及問題定位
    的頭像 發(fā)表于 11-06 17:13 ?864次閱讀
    ES32F36xx芯片<b class='flag-5'>發(fā)生</b>HardFault異常時(shí)的<b class='flag-5'>函數(shù)</b><b class='flag-5'>調(diào)用</b>關(guān)系及問題定位

    python定義函數(shù)調(diào)用函數(shù)的順序

    定義函數(shù)調(diào)用函數(shù)的順序 函數(shù)被定義后,本身是不會(huì)自動(dòng)執(zhí)行的,只有在被調(diào)用后,函數(shù)才會(huì)被執(zhí)行,得
    的頭像 發(fā)表于 10-04 17:17 ?1537次閱讀