nginx的內存池實現?

《nginx開發從入門到精通》的內存池章節太監了。。。有沒有系統地介紹nginx內存池實現機制的資料?


Nginx內存池的實現

眾所周知,內存泄露是c語言程序容易引起的一大問題,當程序複雜度提高,代碼量加大,內存控制的細節增多,就容易導致內存的泄露,這是個簡單而又複雜問題

Nginx作為一個相對複雜的軟體,由於對性能的極致追求,導致對內存的大量細節控制,內存泄露成為必然的要解決的問題,於是Nginx引入內存池來解決這一問題

簡單點說」內存池」就是用來管理內存的統一申請與釋放的機制,這得益於http服務的階段性,導致內存的階段性申請與釋放不會發生太多問題。

pool的機制概要:

1.進程預向系統申請一大塊內存,稱之為「內存池」(內存池的創建),統一申請。

2.當再次需要內存時,向內存池申請內存,而非向系統申請,統一管理。

3.當一階段結束,內存不再需要時,釋放內存池資源(內存池的釋放),統一釋放。

內存池在內存中的具體結構:

單個內存池初始結構,向系統申請的整個內存作為一個內存池,內存池的開始是記錄內存池的信息結構ngx_pool_t

申請並連接新的內存池節點

nginx用一個ngx_pool_t結構來記錄一個內存池

結構聲明

last 指向可用內存的開始地址

end 指向內存池的結束地址

next指向下一個內存池,當次內存池的內存用完時,便初始一個新的內存池,構成一個內存池鏈,第一個內存池有完整的ngx_pool_t結構,為節約空間和統一管理,此後的內存池只有ngx_pool_data_t來記錄當前內存池的狀態。

failed記錄內存池內存申請失敗的次數,申請失敗可能是剩餘空間小於申請空間

max記錄可向內存池申請內存的最大值,超過這一值則直接調用malloc向系統申請而不是向內存池申請,但向系統申請的內存依然要掛載在內存池的large欄位上,方便統一釋放,向系統申請的內存也可以提前釋放。

current指向申請開始的內存池節點,由於內存池是一個鏈式結構,每次申請內存池為避免每次都要遍歷鏈表,顧用current結構指向下次申請開始分配內存的內存池,舉例來說,若第一個內存池的內存用完時,那麼current指向下一個內存池,以免下次申請內存池又要測試第一個內存池是否有足夠可用空間。

current指向哪個內存池有一定的演算法,通過failed欄位的值來決定是否current指向下一個內存池。舉例來說,第一個內存池剩下100kb,此時向內存池申請200kb內存,那麼第一個內存池不夠,就是說失敗了,failed++,然後nginx就會再創建一個內存池節點(之所以稱之為節點是因為內存池是鏈在一起的),由新申請的內存池節點分配200kb內存。第二次申請150kb內存,current指向第一個內存池,還是不夠,failed++,於是向下一個內存池節點申請150kb,當failed達到一個值時(5或6),current不再指向第一個內存池,而指向下一個內存池,以免每次都要在第一個內存池判斷內存是否夠用。由此可看出Nginx對性能的極致追求。

large用來指向直接向系統申請的大塊內存鏈表,當同意釋放內存池時,遍歷large鏈表,釋放這些大塊的內存。

內存池的創建,內存申請,銷毀流程:

1.創建內存池:ngx_create_pool()進程先向系統申請一頁內存,然後初始化ngx_pool_t結構,初始化後的結 構如上文圖所示

2.向內存池申請內存:

void *ngx_palloc(ngx_pool_t*pool,size_t size)向內存池申請size大小內存,若size大於max,則改用向系統申請。

void *ngx_pnalloc(ngx_pool_t*pool,size_t size)向內存申請size大小的內存,不用對齊

void*ngx_pmemalign(ngx_pool_t *pool,size_tsize)無論大小多大都向系統申請。

void *ngx_pcalloc(ngx_pool_t*pool,size_t size)申請並清零

3.釋放內存池,先釋放連接的大塊內存,和其他資源,然後釋放內存池空間


推薦閱讀:

TAG:Nginx | Web伺服器 |