前段時間業(yè)務(wù)反映某類服務(wù)器上更新了 bash 之后,ssh 連上去偶發(fā)登陸失敗,客戶端吐出錯誤信息如下所示:
大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)
圖 - 0

該版本 bash 為部門這邊所定制,但是實現(xiàn)上與原生版并沒有不同,那么這些錯誤從哪里來?

是 bash 的鍋嗎

從上面的錯誤信息可以猜測,異常是 bash 在啟動過程中分配內(nèi)存失敗所導(dǎo)致,看起來像是某些情況下該進程錯誤地進行了大量內(nèi)存分配,最后導(dǎo)致內(nèi)存不足,要確認這個事情比較簡單,動態(tài)內(nèi)存分配到系統(tǒng)調(diào)用這一層上主要就兩種方式: brk() 和 mmap(), 所以只要統(tǒng)計一下這兩者的調(diào)用就可以大概估算出是否有大內(nèi)存分配了。

bash 是由 sshd 啟動的,于是 strace 跟蹤了一下 sshd 進程,結(jié)果發(fā)現(xiàn)異常發(fā)生時,bash 分配的內(nèi)存非常地少,少到有時甚至只有幾十字節(jié)也會失敗,幾乎可以斷定 bash 在內(nèi)存使用上沒有異常,但在這期間發(fā)現(xiàn)一個詭異的現(xiàn)象,Bash 一直只用 brk 在分配小內(nèi)存,brk() 失敗后就直接退出了,一般程序使用的 libc 中的 malloc (或其它類似的 malloc) 會結(jié)合 brk 和 mmap 一起使用【0】,不至于 brk 一失敗就分配不到內(nèi)存,順手查看了下 bash 的源碼,發(fā)現(xiàn)它確實基于 brk 做了自己的內(nèi)存管理,并沒有使用 malloc 或 mmap。

但那并不是重點,重點是即使是只使用 brk,也不至于只能分配幾十字節(jié)的內(nèi)存。

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

進程的內(nèi)存布局在結(jié)構(gòu)上是有規(guī)律的,具體來說對于 linux 系統(tǒng)上的進程,其內(nèi)存空間一般可以粗略地分為以下幾大段【1】,從高內(nèi)存到低內(nèi)存排列:
1、內(nèi)核態(tài)內(nèi)存空間,其大小一般比較固定(可以編譯時調(diào)整),但 32 位系統(tǒng)和 64 位系統(tǒng)的值不一樣。
2、用戶態(tài)的堆棧,大小不固定,可以用 ulimit -s 進行調(diào)整,默認一般為 8M,從高地址向低地址增長。
3、mmap 區(qū)域,進程茫茫內(nèi)存空間里的主要部分,既可以從高地址到低地址延伸(所謂 flexible layout),也可以從低到高延伸(所謂 legacy layout),看進程具體情況【2】【3】。
4、brk 區(qū)域,緊鄰數(shù)據(jù)段(甚至貼著),從低位向高位伸展,但它的大小主要取決于 mmap 如何增長,一般來說,即使是 32 位的進程以傳統(tǒng)方式延伸,也有差不多 1 GB 的空間(準確地說是 TASK_SIZE/3 - 代碼段數(shù)據(jù)段,參看 arch/x86/include/asm/processor.h 里宏 TASK_UNMAPPED_BASE 的定義)【4】
5、數(shù)據(jù)段,主要是進程里初始化和未初始化的全局數(shù)據(jù)總和,當(dāng)然還有編譯器生成一些輔助數(shù)據(jù)結(jié)構(gòu)等等),大小取決于具體進程,其位置緊貼著代碼段。
6、代碼段,主要是進程的指令,包括用戶代碼和編譯器生成的輔助代碼,其大小取決于具體程序,但起始位置根據(jù) 32 位還是 64 位一般固定(-fPIC, -fPIE等除外【5】)。

以上各段(除了代碼段數(shù)據(jù)段)其起始位置根據(jù)系統(tǒng)是否起用 randomize_va_space 一般稍有變化,各段之間因此可能有隨機大小的間隔,千言萬語不如一幅圖:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)
圖 - 1

所以現(xiàn)在的問題歸結(jié)為:為什么目標(biāo)進程的 brk 的區(qū)域突然那么小了,先檢查一下 bash 的內(nèi)存布局:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)
圖 - 2

這個進程的內(nèi)存布局和一般理解上有很大出入,從上往下是低內(nèi)存到高內(nèi)存:

延伸閱讀

學(xué)習(xí)是年輕人改變自己的最好方式-Java培訓(xùn),做最負責(zé)任的教育,學(xué)習(xí)改變命運,軟件學(xué)習(xí),再就業(yè),大學(xué)生如何就業(yè),幫大學(xué)生找到好工作,lphotoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)學(xué)習(xí)是年輕人改變自己的最好方式