一切像霧像雨又像風
作者:CppExplore 網址:http://www.shnenglu.com/CppExplore/服務器設計人員在一段時間的摸索后,都會發現:服務器性能的關鍵在于內存。從收包到解析,到消息內存的申請,到session結構內存的申請都要小心處理,盡量減少內存數據copy,減少內存動態申請,減少內存檢索。為達到這個目的,不同的地方有不同的方法,比如常見的包解析,使用緩沖區偏移以及長度來標識包內字段信息;內存使用量固定的系統,系統啟動就申請好所有需要的內存,初始化好,等待使用的時候直接使用;基于license控制的系統,根據license的數量,一次性申請固定數量內存等......。本文不再總結這些特性方案,重點說下常見的通用的內存池緩存技術。 內存池可有效降低動態申請內存的次數,減少與內核態的交互,提升系統性能,減少內存碎片,增加內存空間使用率,避免內存泄漏的可能性,這么多的優點,沒有理由不在系統中使用該技術。為了給內存池技術尋找基石,先從低層的內存管理看起。硬件層略掉不談,可回顧《操作系統》。一、linux內存管理策略 linux低層采用三層結構,實際使用中可以方便映射到兩層或者三層結構,以適用不同的硬件結構。最下層的申請內存函數get_free_page。之上有三種類型的內存分配函數(1)kmalloc類型。內核進程使用,基于slab技術,用于管理小于內存頁的內存申請。思想出發點和應用層面的內存緩沖池同出一轍。但它針對內核結構,特別處理,應用場景固定,不考慮釋放。不再深入探討。(2)vmalloc類型。內核進程使用。用于申請不連續內存。(3)brk/mmap類型。用戶進程使用。malloc/free實現的基礎。有關詳細內容,推薦http://www.kerneltravel.net/journal/v/mem.htm。http://www.kerneltravel.net上有不少內核相關知識。二、malloc系統的內存管理策略 malloc系統有自己的內存池管理策略,malloc的時候,檢測池中是否有足夠內存,有則直接分配,無則從內存中調用brk/mmap函數分配,一般小于等于128k(可設置)的內存,使用brk函數,此時堆向上(有人有的硬件或系統向下)增長,大于128k的內存使用mmap函數申請,此時堆的位置任意,無固定增長方向。free的時候,檢測標記是否是mmap申請,是則調用unmmap歸還給操作系統,非則檢測堆頂是否有大于128k的空間,有則通過brk歸還給操作系統,無則標記未使用,仍在glibc的管理下。glibc為申請的內存存儲多余的結構用于管理,因此即使是malloc(0),也會申請出內存(一般16字節,依賴于malloc的實現方式),在應用程序層面,malloc(0)申請出的內存大小是0,因為malloc返回的時候在實際的內存地址上加了16個字節偏移,而c99標準則規定malloc(0)的返回行為未定義。除了內存塊頭域,malloc系統還有紅黑樹結構保存內存塊信息,不同的實現又有不同的分配策略。頻繁直接調用malloc,會增加內存碎片,增加和內核態交互的可能性,降低系統性能。linux下的glibc多為Doug Lea實現,有興趣的可以去baidu、google。三、應用層面的內存池管理 跳過malloc,直接基于brk/mmap實現內存池,原理上是可行的,但實際中這種實現要追逐內核函數的升級,增加了維護成本,另增加了移植性的困難,據說squid的內存池是基于brk的,本人尚未閱讀squid源碼(了解磁盤緩存的最佳代碼,以后再詳細閱讀),不敢妄言。本文后面的討論的內存池都是基于malloc(或者new)實現。我們可以將內存池的實現分兩個類別來討論。1、不定長內存池。典型的實現有apr_pool、obstack。優點是不需要為不同的數據類型創建不同的內存池,缺點是造成分配出的內存不能回收到池中。這是由于這種方案以session為粒度,以業務處理的層次性為設計基礎。(1)apr_pool。apr全稱Apache portable Run-time libraries,Apache可移植運行庫。可以從http://www.apache.org/網站上下載到。apache以高性能、穩定性著稱,它所有模塊的內存申請都由內存池模塊apr_pool實現。有關apr_pool結構、實現的原理,http://blog.csdn.net/tingya/(apache源碼分析類別中的apache內存池實現內幕系列)已經有了詳細的講解,結合自己下載的源碼,已經足夠了。本人并不推薦去看這個blog和去看詳細的代碼數據結構以及邏輯。明白apr_pool實現的原理,知道如何使用就足夠了。深入細節只能是浪費腦細胞,當然完全憑個人興趣愛好了。 這里舉例說下簡單的使用:
apr_pool中主要有3個對象,allocator、pool、block。pool從allocator申請內存,pool銷毀的時候把內存歸還allocator,allocator銷毀的時候把內存歸還給系統,allocator有一個owner成員,是一個pool對象,allocator的owner銷毀的時候,allocator被銷毀。在apr_pool中并無block這個單詞出現,這里大家可以把從pool從申請的內存稱為block,使用apr_palloc申請block,block只能被申請,沒有釋放函數,只能等pool銷毀的時候才能把內存歸還給allocator,用于allocator以后的pool再次申請。 我給的例子中并沒有出現創建allocator的函數,而是使用的默認全局allocator。apr_pool提供了一系列函數操作allocator,可以自己調用這些函數:
Powered by: C++博客 Copyright © cppexplore