成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Linux后端程序員必備技能之函數(shù)棧

系統(tǒng) Linux
大家都知道函數(shù)調(diào)用是通過棧來實現(xiàn)的,而且知道在棧中存放著該函數(shù)的局部變量。但是對于棧的實現(xiàn)細(xì)節(jié)可能不一定清楚。本文將介紹一下在Linux平臺下函數(shù)棧是如何實現(xiàn)的。

大家都知道函數(shù)調(diào)用是通過棧來實現(xiàn)的,而且知道在棧中存放著該函數(shù)的局部變量。但是對于棧的實現(xiàn)細(xì)節(jié)可能不一定清楚。本文將介紹一下在Linux平臺下函數(shù)棧是如何實現(xiàn)的。有些同學(xué)可能覺得沒必要了解這么深入,其實非也。根據(jù)本號多年的經(jīng)驗,了解系統(tǒng)深層次的原理對分析疑難問題有很好的幫助。

函數(shù)棧

圖0 函數(shù)棧

就像熟悉抓包是解決網(wǎng)絡(luò)通信問題的高級武器一樣,熟悉函數(shù)調(diào)用棧則是分析程序內(nèi)存問題的高級武器。本文以Linux 64位操作系統(tǒng)下C語言開發(fā)為例,介紹應(yīng)用程序調(diào)用棧的實現(xiàn)原理,并通過一個實例和GDB工具具體分析一下某個程序的調(diào)用棧內(nèi)容。在介紹具體的調(diào)用棧之前,我們先介紹一些基礎(chǔ)知識,這些知識是理解后續(xù)函數(shù)調(diào)用棧的基礎(chǔ)。

X86 CPU的寄存器

CPU的寄存器是需要了解的基礎(chǔ)知識,這是因為在X64體系中函數(shù)的參數(shù)是通過寄存器傳遞的。如圖1是X86 CPU寄存器的列表及功能簡要說明。

Intel X86 CPU寄存器用途

圖1 Intel X86 CPU寄存器用途

我們知道Intel的CPU在設(shè)計的時候都是向前兼容的,也就是在新一代的CPU上可以運行老一代CPU上的編譯的程序。為了保證兼容性,新一代CPU保留了老一代寄存器的別名。以16位寄存器AX為例,AL表示低8位,AH表示高8位。而32位CPU問世之后,通過名為EAX的寄存器表示32位寄存器,AX仍然保留。以此類推,RAX表示一個64位寄存器。

圖2 不同的寄存器名稱

應(yīng)用程序的地址空間

操作系統(tǒng)通過虛擬內(nèi)存的方式為所有應(yīng)用程序提供了統(tǒng)一的內(nèi)存映射地址。如圖3所示,從上到下分別是用戶棧、共享庫內(nèi)存、運行時堆和代碼段。當(dāng)然這個是一個大概的分段,實際分段比這個可能稍微復(fù)雜一些,但整個格局沒有大變化。

應(yīng)用程序的地址空間

圖3 應(yīng)用程序的地址空間

從圖中可以看出用戶棧是從上往下生長的。也就是用戶棧會先占用高地址的空間,然后占用低地址空間。目前我們可以大體上有個了解即可,后面我們在詳細(xì)分析用戶棧的細(xì)節(jié)。

函數(shù)調(diào)用及匯編指令

為了理解函數(shù)調(diào)用棧的細(xì)節(jié),有必要了解一下匯編程序中函數(shù)調(diào)用的實現(xiàn)。函數(shù)的調(diào)用主要分為2部分,一個是調(diào)用,另外一個是返回。在匯編語言中函數(shù)調(diào)用是通過call指令完成的,返回則是通過ret指令。

匯編語言的call指令相當(dāng)于執(zhí)行了2步操作,分別是,1)將當(dāng)前的IP或CS和IP壓入棧中; 2)跳轉(zhuǎn),類似與jmp指令。同樣,ret指令也分2步,分別是,1)將棧中的地址彈出到IP寄存器;2)跳轉(zhuǎn)執(zhí)行后續(xù)指令。這個基本上就是函數(shù)調(diào)用的原理。

除了在代碼間的跳動外,函數(shù)的調(diào)用往往還需要傳遞一個參數(shù),而處理完成后還可能有返回值。這些數(shù)據(jù)的傳遞都是通過寄存器進行的。在函數(shù)調(diào)用之前通過上文介紹的寄存器存儲參數(shù),函數(shù)返回之前通過RAX寄存器(32位系統(tǒng)為EAX)存儲返回結(jié)果。

另外一個比較重要的知識點是函數(shù)調(diào)用過程中與堆棧相關(guān)的寄存器RSP和RBP,兩個寄存器主要實現(xiàn)對棧位置的記錄,具體作用如下:

  • RSP:棧指針寄存器(reextended stack pointer),其內(nèi)存放著一個指針,該指針永遠(yuǎn)指向系統(tǒng)棧最上面一個棧幀的棧頂。
  • RBP:基址指針寄存器(reextended base pointer),其內(nèi)存放著一個指針,該指針永遠(yuǎn)指向系統(tǒng)棧最上面一個棧幀的底部。

寄存器的名稱跟體系結(jié)構(gòu)是相關(guān)的,本文是64位系統(tǒng),因此寄存器是RSP和RBP。如果是32位系統(tǒng)則寄存器的名稱為ESP和EBP。

應(yīng)用程序調(diào)用棧

我們先從整體上來看一下函數(shù)調(diào)用棧的主要內(nèi)容,如圖4所示。在函數(shù)棧中主要包括函數(shù)參數(shù)表、局部變量表、棧的基址和函數(shù)返回地址。這里棧的基址是上一個棧幀的基址,因為在本函數(shù)中需要使用該基址訪問棧中的內(nèi)容,因此需要首先將上一個棧幀中的基址壓棧。

函數(shù)調(diào)用棧概覽

圖4 函數(shù)調(diào)用棧概覽

為了便于理解,我們以一個具體的程序作為示例。本程序非常簡單,主要是模擬了多個函數(shù)的函數(shù)調(diào)用關(guān)系和參數(shù)傳遞。另外,在函數(shù)func_2中定義了2個形參,以模擬多參數(shù)傳遞的過程。

函數(shù)棧匯編分析

圖5 函數(shù)棧匯編分析

在本示例中,main函數(shù)調(diào)用func_1函數(shù)。我們從main函數(shù)開始分析,可以先看一下右側(cè)的C語言代碼。首先是函數(shù)參數(shù)的準(zhǔn)備過程。在main函數(shù)調(diào)用func_1時依次傳入的參數(shù)為1、2、3和4+g,其中***一個參數(shù)是需要計算的。按照紅色方框的虛線,我們可以看到對應(yīng)的匯編程序,在匯編程序中首先處理***一個參數(shù),然后是倒數(shù)第二個,以此類推(函數(shù)參數(shù)的處理順序在日常開發(fā)中是需要注意的內(nèi)容重點)。同時,我們看到存儲參數(shù)的寄存器名稱與前文是一致。

當(dāng)準(zhǔn)備完參數(shù)之后,就是調(diào)用func_1函數(shù),這個在匯編語言中就是call func_1這一行。雖然只是一行匯編指令,但其實內(nèi)部做了一些事情,這個我們在前文介紹call指令的時候有所介紹,大家可以參考一下前文。

之后就進入func_1函數(shù)的處理邏輯。最一開始是pushq %rbp匯編程序,這句指令的作用是將RBP壓入函數(shù)棧中。這句壓棧及后面的更新RBP的值(moveq %rsp, %rbp)是構(gòu)建本函數(shù)的棧幀頭,后續(xù)對本棧幀的內(nèi)容的訪問都是通過幀頭(RBP)進行的。接下來是對參數(shù)壓棧的過程和局部變量初始化的過程,具體分布參考圖5中的綠色方框和紅色方框。

完成函數(shù)內(nèi)的運算后,***將運算結(jié)果放入寄存器EAX中,然后調(diào)用指令leave和ret。這里面需要說明的是leave指令,該指令相當(dāng)于下面兩條匯編指令。可以對比一下函數(shù)入口的匯編指令,其實兩者是對稱的。leave指令將本幀的?;焚x值給棧指針(圖6中步驟2),然后將其中的內(nèi)容彈出到RBP中(圖6中步驟3)。其實就是RBP指向上一個幀(調(diào)用者)的棧幀,也即是一個復(fù)原的過程。

  1. movl %ebp %esp 
  2. popl %ebp 

圖6 函數(shù)返回示意圖

這樣,函數(shù)返回后寄存器RBP和RSP從被調(diào)用者的棧幀切換到了調(diào)用者的棧幀。

通過GDB分析函數(shù)調(diào)用棧

上面是通過反匯編的方式分析函數(shù)的調(diào)用棧和棧幀情況。我們還可以通過gdb動態(tài)的分析函數(shù)棧和棧幀的使用情況。我們依然通過main函數(shù)調(diào)用func_1函數(shù)為例來分析。我們這里在函數(shù)func_1的入口處設(shè)置一個單點,然后運行程序,程序停止在斷點處。如圖7是我們逐步執(zhí)行是函數(shù)棧的變化過程,具體細(xì)節(jié)我們這里就不再贅述,大家可以實際操作一下。

圖7 函數(shù)棧變化過程

本文的目的是讓大家對函數(shù)調(diào)用棧有個整體的了解,這樣對以后程序的疑難雜癥就有更多的解決思路。因為在實際生產(chǎn)環(huán)境中與棧相關(guān)的問題也是比較多的,比如局部變量太多導(dǎo)致的棧溢出,或者踩內(nèi)存問題引起的棧破壞等等。因此,了解了函數(shù)棧的原理,在遇到所謂的莫名其妙問題的時候就會有新的思路。往往很多問題不是問題本身莫名其妙,而是我們的知識儲備不夠,自己感覺莫名其妙而已。

責(zé)任編輯:趙寧寧 來源: itworld123
相關(guān)推薦

2022-05-19 15:08:43

技術(shù)函數(shù)調(diào)用棧Linux

2019-09-25 11:39:07

程序員編程技術(shù)

2020-10-10 11:01:40

后端程序員技術(shù)

2020-10-09 14:44:57

程序員開發(fā)技術(shù)

2015-07-01 09:10:20

2021-04-13 09:07:12

程序員工具軟件開發(fā)

2018-08-01 16:12:54

程序員基本技能

2020-04-20 19:00:30

程序員分布式事務(wù)架構(gòu)

2020-09-04 14:25:31

程序員網(wǎng)絡(luò)安全

2015-03-13 09:45:27

2009-06-25 09:33:43

Java API程序員

2013-06-13 14:29:26

架構(gòu)師程序員

2022-10-24 09:00:47

畫圖工具程序員XMind

2014-08-15 14:25:48

Android程序員資源

2014-08-20 10:28:29

Android

2011-06-11 20:59:12

程序員

2019-01-30 14:14:16

LinuxUNIX操作系統(tǒng)

2024-10-07 11:11:26

2019-12-13 15:00:16

索引MySQL數(shù)據(jù)庫

2023-10-04 00:50:00

Java開發(fā)
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 国产精品视频一区二区三区, | 国产日韩精品视频 | 国产一区二区三区精品久久久 | 中文字幕二区三区 | 九九综合九九 | 特级a欧美做爰片毛片 | 看a级黄色毛片 | 免费观看毛片 | 羞羞在线观看视频 | 91精品国产综合久久久动漫日韩 | 精品99在线 | 日韩在线视频一区二区三区 | 午夜成人免费视频 | 91热爆在线观看 | 国产精品日韩一区 | 香蕉一区二区 | 欧美国产日韩精品 | 久久这里有精品 | 欧美日韩在线播放 | 国产一区不卡在线观看 | 久色一区| 欧美日韩国产精品一区 | 亚洲一区二区免费 | 国产精品一区二区三区久久 | 精品国产一区二区三区性色 | 麻豆精品国产91久久久久久 | 综合色在线 | 日韩在线观看一区 | 草久在线视频 | 久久精品国产久精国产 | 成人免费大片黄在线播放 | 国产一区二区三区四区三区四 | a级毛片毛片免费观看久潮喷 | 91精品91久久久 | 欧美日韩精品中文字幕 | 天天拍天天插 | 国产一区二区三区四区在线观看 | 夜夜骑首页 | 一区二区免费 | 青青草原精品99久久精品66 | 日韩福利视频 |