有本事就出來,沒本事就當(dāng)鱉!
如果讓我回答關(guān)于進程棧,線程棧的問題,只要問題不籠統(tǒng),只要問題明確,我會一五一十地回答,正確率上九成,然而,可悲的是,問題往往他媽的都不是 很明確,因此,游戲到此結(jié)束??!艸。但是如果給我一個問的機會,我會問下面一個問題,記住,使出你拉屎的勁來回答(該問題足夠糙!不必太當(dāng)回事,重要的東 西在下面-):
UNIX/Linux
的stack在大多數(shù)平臺是向下擴展的(注意,我已經(jīng)告訴他事實了,我并沒有問...是如何擴展的,這是可以背誦下來并朗讀出來的),在一個執(zhí)行流調(diào)用了
一個函數(shù)A,而該函數(shù)A在stack上分配了一個大數(shù)組導(dǎo)致了stack擴展(注意,這又是一段陳述,我還沒有給出問題),然后A返回
了,UNIX/Linux理應(yīng)回收調(diào)A里面大數(shù)組分配stack空間-因為它再也沒有用了,但是它并沒有這么做(這里可能是一個陷阱,真的是UNIX
/Linux理應(yīng)這么做卻沒有做,還是說我只是在逗你玩...不確定,但陳述就是如此)。(注意,我的問題來了),請問,UNIX/Linux為什么這么
做????。?!
凡是回答操作系統(tǒng)這么規(guī)定的之類的答案,一律零分!況且,你能證明我說的一定對嗎?萬一我是逗你玩呢?操作系統(tǒng)能規(guī)定一個錯的東西嗎?或者我可以繼續(xù)問為
什么這么規(guī)定,直到像我初中的歷史老師被我問到朝我眼角猛打一拳那樣,如果我能找回點賤人所謂的快感,那么來打吧!問題就是這樣,不管這個問題是一個偽命
題還是你有自己的想法,能說5分鐘的,我覺得也夠可以了。該題目的答題要求如下:
時間限制:5分鐘。
作答方式:全口述,不能畫圖,不能打手勢...語言含糊不清的,表達能力不好的,算錯誤。
答題建議:如果你對OS虛擬內(nèi)存管理以及Linux的VMA實現(xiàn)細節(jié)沒有相當(dāng)深入的理解,請不要猜測答案。請直接回答“不知道”,然后看完此文。
.................................
5分鐘過去。我要公布一點我的想法了。
首先,這個問題在本身看來,有問題。因為雖然Linux理應(yīng)這么做,但它:
第一,它不一定能做到;
第二,它根本沒有必要做。
那么論據(jù)是什么?憑什么這樣說?
沒必要這樣做。執(zhí)行流還會調(diào)用別的函數(shù)或者再次調(diào)用A,頻繁回收棧損耗性能;
很難或者不能做到。stack操作是處理器控制的,和OS內(nèi)核地址空間管理機制之間沒有同步機制,一個函數(shù)調(diào)用結(jié)束后,CPU自動處理stack寄存器的收縮,彈出棧幀,然而它無法通知OS內(nèi)存管理系統(tǒng)去更新進程地址空間的映射關(guān)系。
stack 會一直擴展到碰到異常的地址B,B可能是一個readonly的地址或者是一個保護空洞,在向下擴展stack情況下,如果地址B偏上,會導(dǎo)致stack 空間變小,如果偏下,一旦函數(shù)局部變量幾乎占滿了stack底到B的空間,mmap雖然也能unmap掉這段區(qū)域然后remap,然而這會使數(shù)據(jù)混亂,造 成嚴(yán)重問題。
mmap或者brk期間,比較stack頂部與esp寄存器,若小于則回收(等于是正常的,大于是不可能的)。
Linux 沒有判斷什么esp寄存器,Linux的原則很簡單,只要一個地址處在一個vma范圍內(nèi)或者處在stack可擴展的范圍內(nèi),且擁有權(quán)限的,它就是可以訪 問,內(nèi)核是不管這個VMA是屬于stack還是heap或者別的什么,具體由應(yīng)用程序自己控制,也就是說,你完全可以寫一段代碼,把地址空間中所有可以寫 的區(qū)域全部清零,這完全有可能,緩沖區(qū)溢出可能是一種蓄意的破壞,然而程序員偶然的錯誤也會造成破壞,雖然他們大多數(shù)都不知道錯誤是如何發(fā)生的。我不想用 文字長篇大論Linux是如何管理VMA的,你知道這個應(yīng)該是一個前提,你必須知道這個。我用一段代碼以及兩個圖示來展示Linux系統(tǒng)內(nèi)核是如何管理 stack附近的地址空間映射的,并且在第二張圖中給出,如果你非要蓄意破壞,會造成什么問題。也就是說,一旦發(fā)生莫名奇妙的錯誤,你必須能從細節(jié)上理解 這個錯誤是如何發(fā)生的。
下面一幅圖展示一直到出事之前,該進程的stack附近的地址空間映射區(qū)域是怎么演化的:
下面一幅圖展示出事的過程以及這個事故的原因:
如 果你覺得圖是我自己畫出來的,那么肯定有一個疑問,我是基于什么畫出來的,事實上,我并不是通過看代碼畫出來的,我是通過不斷查看procfs的maps 文件實時了解該進程地址空間的細節(jié),將其轉(zhuǎn)換成了上面的圖示,為了讓我有機會到另一個終端去查maps文件,我在代碼中增加了getchar調(diào)用,每次查 看完maps文件,我會拍一下鍵盤的回車鍵。我的測試如下:
root@abcd:~# ./a.out
7846
init state
before unmap fixmap around stack
after unmap fixmap around stack
after extend stack[first]
after second fixmap around stack at the same address
after unmap fixmap around stack incompletely
段錯誤 (core dumped)
root@abcd:~# cat /proc/`ps -e|grep a.out|awk '{print $1}'`/maps |tail -n 6|head -n 4
2b2c01483000-2b2c01487000 r--p 00157000 fe:00 387296 /lib/libc-2.11.2.so
2b2c01487000-2b2c01488000 rw-p 0015b000 fe:00 387296 /lib/libc-2.11.2.so
2b2c01488000-2b2c0148f000 rw-p 00000000 00:00 0
7fff6236a000-7fff6237f000 rw-p 00000000 00:00 0 [stack]
root@abcd:~# cat /proc/`ps -e|grep a.out|awk '{print $1}'`/maps |tail -n 6|head -n 4
2b2c01487000-2b2c01488000 rw-p 0015b000 fe:00 387296 /lib/libc-2.11.2.so
2b2c01488000-2b2c0148f000 rw-p 00000000 00:00 0
7fff62359000-7fff6235c000 rw-p 00000000 00:00 0
7fff6236a000-7fff6237f000 rw-p 00000000 00:00 0 [stack]
root@abcd:~# cat /proc/`ps -e|grep a.out|awk '{print $1}'`/maps |tail -n 6|head -n 4
2b2c01483000-2b2c01487000 r--p 00157000 fe:00 387296 /lib/libc-2.11.2.so
2b2c01487000-2b2c01488000 rw-p 0015b000 fe:00 387296 /lib/libc-2.11.2.so
2b2c01488000-2b2c0148f000 rw-p 00000000 00:00 0
7fff6236a000-7fff6237f000 rw-p 00000000 00:00 0 [stack]
root@abcd:~# cat /proc/`ps -e|grep a.out|awk '{print $1}'`/maps |tail -n 6|head -n 4
2b2c01483000-2b2c01487000 r--p 00157000 fe:00 387296 /lib/libc-2.11.2.so
2b2c01487000-2b2c01488000 rw-p 0015b000 fe:00 387296 /lib/libc-2.11.2.so
2b2c01488000-2b2c0148f000 rw-p 00000000 00:00 0
7fff5e0bb000-7fff6237f000 rw-p 00000000 00:00 0 [stack]
root@abcd:~# cat /proc/`ps -e|grep a.out|awk '{print $1}'`/maps |tail -n 6|head -n 4
2b2c01488000-2b2c0148f000 rw-p 00000000 00:00 0
7fff5e0bb000-7fff62359000 rw-p 00000000 00:00 0
7fff62359000-7fff6235c000 rw-p 00000000 00:00 0
7fff6235d000-7fff6237f000 rw-p 00000000 00:00 0 [stack]
root@abcd:~# cat /proc/`ps -e|grep a.out|awk '{print $1}'`/maps |tail -n 6|head -n 4
2b2c01488000-2b2c0148f000 rw-p 00000000 00:00 0
7fff5e0bb000-7fff62359000 rw-p 00000000 00:00 0
7fff6235a000-7fff6235c000 rw-p 00000000 00:00 0
7fff6235d000-7fff6237f000 rw-p 00000000 00:00 0 [stack]
我怕上面的文字信息太亂,格式在不同瀏覽器會有問題,我還特意截了一張圖:
你 可以用這種方式徹底限制一個進程的stack的大小,越界了不是報錯,而是segfault。然后你可以signal捕獲這個segfault,在里面把 那個未完全unmap的fixmap vma以及那個可憐且孤獨的殘缺的stack vma給徹底unmap掉。不過這確實沒什么好玩的。有什么用呢?它的作用就是讓你更加深入理解Linux對虛擬地址空間的管理方式。
本文不涉及線程棧,但是倒也不難,線程棧一般在heap區(qū)或者中間的大塊mmap區(qū)動態(tài)分配,mmap的時候給它一個MAP_GROWSDOWN標(biāo)志就可以了。關(guān)于它的管理方式,沒啥差別。核心問題在于,缺頁異常處理程序是怎么識別到一個缺頁是一個vma內(nèi)部的缺頁(結(jié)果就是調(diào)頁),還是vma外部的缺頁。在后一種情況下,缺頁處理邏輯還要進一步識別是stack的缺頁(結(jié)果就是extend stack然后調(diào)頁),還是非stack缺頁(結(jié)果就是segfault...)。
Linux的stack除非遇到本文所述的這種方式的擠兌收縮,它是永遠擴展的。如果你想閱讀Linux的內(nèi)核代碼,那么也需要理解下面的事實:
1.find_vma函數(shù)能找到vma只有一個限制,即輸入地址只要小于查找vma的end即可,并非很多人想象的那樣輸入地址必須處在查找vma的start和end之間;
2.find_vma函數(shù)之所以實現(xiàn)得如此incompletely,是因為為了簡化缺頁中斷的處理,同時也是為了提供一種更加統(tǒng)一的方式同時處理upgrows和downgrows的vma。
雖
然這個問題問得有點亂,但是如果能找上述回答連續(xù)扯5分鐘的,應(yīng)該是真行!不過我不知道怎樣的語言表達能力可以不用圖解和代碼把上面的每一個細節(jié)說清
楚...總之,我覺得我的這個題目是一個好題目??梢越ㄗh給看到此文的人,把它做面試題吧。凡是發(fā)現(xiàn)不了題目問題的以及說不出所以然的,一律不要!這真是
一道好測試題啊,它是如此之好,以至于我還想再出幾道比它更好的。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
分享題目:請使勁回答一個關(guān)于UNIX/Linux自動擴展stack的問-創(chuàng)新互聯(lián)
文章來源:http://jinyejixie.com/article32/psesc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計、網(wǎng)站策劃、云服務(wù)器、關(guān)鍵詞優(yōu)化、軟件開發(fā)、微信小程序
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容