1. 程式人生 > >Memcached原始碼分析——記憶體管理

Memcached原始碼分析——記憶體管理

注:這篇內容極其混亂

推薦學習這篇部落格。部落格的地址:http://kenby.iteye.com/blog/1423989

基本元素item

item是Memcached中記錄儲存的基本單元,使用者向memcached寫入的key value鍵值對資訊都以item的形式存入Memcached中。

item基本結構

首先用一張圖來描述item的基本結構:

wps_clip_image-1384_thumb[3]_thumb

圖 1-1 item的基本機構

圖片來自部落格,畫的非常的清晰。從圖片中可以看到,item主要有兩個部分構成:item的元資料(屬性)部分+item的資料本身部分構成。這個就是Memcached中記錄的儲存單元。以下是其定義的原始碼:

typedef struct _stritem {
    struct _stritem *next;
    struct _stritem *prev;
    struct _stritem *h_next;    /* hash chain next*/
    rel_time_t      time;       /* least recent access */
    rel_time_t      exptime;    /* expire time */
    int             nbytes;     /* size of data */
    unsigned 
short refcount; uint8_t nsuffix; /* length of flags-and-length string */ uint8_t it_flags; /* ITEM_* above */ uint8_t slabs_clsid;/* which slab class we're in */ uint8_t nkey; /* key length, w/terminating null and padding */ union { uint64_t cas;
char end; } data[];//data 不佔用任何空間 } item;

使用typedef定義結構體_stritem為item。這個就是item的定義了。從item的定義中,可以看到,結構體本身並不包含任何關於資料key value的定義。那麼是否和圖中的紅色部分有違背呢?

堆上操作的巨集定義技巧

答案是在操作item的時候肯定會在堆上分配空間,開發人員使用了巨集定義的技巧,使得對記憶體的管理更加的靈活方便:

unsigned int size = sizeof(item) + settings.chunk_size;

上面是一個完整的chunk的大小的求解方法,可以看到除了sizeof以外,還額外負擔了使用者手動配置的chunk_size。可以看到一個item所佔的空間由兩部分構成,符合2.1.1中對item的定義。而chunk_size所指向的記憶體空間又是如何劃分的?

首先撇開記憶體空間的定義,結構體item中最關鍵的data[]空陣列的定義:

typedef _stritem{
  ....
  union{
      uint64_t cas;
      char end;
  }data[];
}item;

在_stritem結構體中定義了一個空的陣列data[],data[]不佔用任何的空間。當item後續有資料時,data的地址就是緊接著item元資料部分的成員的首地址。有了這個就不難理解巨集定義了。

cas是Memcached中為了支援多執行緒讀寫的一個標誌,類似compare and swap(實際上是check and set),是可選的標誌。當用戶啟動了cas特性後,則通過以下方式訪問cas位:

item->data->cas;

而當用戶沒有啟用cas的時候,則data就純當指標基地址來使用了。有了以上的知識,我們來看幾個巨集定義的使用:

#define ITEM_key(item) (((char*)&((item)->data)) \
         + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))

#define ITEM_suffix(item) ((char*) &((item)->data) + (item)->nkey + 1 \
         + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))

#define ITEM_data(item) ((char*) &((item)->data) + (item)->nkey + 1 \
         + (item)->nsuffix \
         + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))

使用四個巨集定義來給予data指標計算不同成員的在堆空間上面的偏移量。來看第一個:ITEM_key(item),首先將data的地址強制轉換成char*指標,從而以步進為位元組方式獲得地址。隨後判斷當前flag是否包含cas的部分,如果包含,則指標加上uint64_t的大小(8個位元組)越過cas佔用的地址;如果沒有包含,則加0,表明data的地址就是key的地址。

剩下求解suffix、data的地址採用類似相同的方式來完成。可以看到這個技巧的牛逼的地方。求解suffix、data地址的時候,可以看到有個+1的操作,這個原因是key的末尾有一個null終結符,佔用1個位元組的空間。因此需要加一操作。

為了求解整個item佔用空間大小(不是chunk的佔用空間),同樣定義了巨集來求解:

#define ITEM_ntotal(item) (sizeof(struct _stritem) + (item)->nkey + 1 \
         + (item)->nsuffix + (item)->nbytes \
         + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))

首先是_stritem本身的大小,隨後是key的大小,空位元組的大小,suffix的大小,value的大小,最後看當前是否使用了cas,如果使用了需要計算cas的大小。對每個分量求和便是整個item的大小。

從本小結的巨集定義中我們看到了C語言專案對於記憶體的精細操作,縱觀程式設計世界,可能也只有C能夠做出這麼精彩的記憶體操作。

item 元資料介紹

再來引入item的定義,這次直接截圖了,寫程式碼太佔用空間了。

wps_clip_image-2429_thumb[4]_thumb

圖 1-2 item的基本定義

這些資料代表了item的基本資訊,在後續的很多資料結構中都會用到他們,以下列舉主要的成員:

v LRU連結串列

next和prev指標用於構成連結串列,這個連結串列維護了當前已經分配的item空間。為了支援LRU特性,連結串列預設以訪問頻度排序,最新訪問的item將位於連結串列的表頭。

v hash表桶

h_next指標用於構成在hash連結串列中,桶內組成連結串列。hash表主要用於快速檢索item。

v 相關時間

item的最近訪問時間以及超時時間,在記憶體空間不足時,不得不將一些item換出記憶體,因此使用這個來完成。

v slabs_clsid

描述當前item位於哪個slabs類中。由於每個slab類的大小依次成指數遞增,因此某個item需要位於一個slab類中,而slabs_clsid就是當前slab類的編號。

Hash表實現

為了實現快速的檢索,Memcached內部實現了Hash表,就是為了純檢索。Memcached中的Hash表位於assoc.c以及assoc.h中實現的。我們來一睹hash表的定義:

static item** primary_hashtable = 0;

用一個二級item指標就實現了hashtable,其實hashtable的實現都在業務邏輯中了。

Hash表的實際構成結構如下:

wps_clip_image-2553_thumb[5]_thumb

圖 1-3 memcached hash表的實現

如上圖,Memcached採用了開鏈方法,對Hash表進行了實現。本節將對Hash表進行介紹。

結構原理解釋

從圖 1-3可以看出hash表的基本實現。整體來講就是一個指標陣列,陣列的每個元素存放著一個item。當對item進行儲存檢索時,將item的key求hash值,hash值的求法就是經典的取模法。存入之後就放入對應的編號位置。

如果遇上hash值相同衝突的情況,memcached則使用開鏈法,將相同hash值的item都連結在一起,使用item->h_next進行連結操作。

元素查詢

為了求解hash值,memcached使用巨集定義來求解給定的item應該存入哪個位置。

#define hashsize(n) ((ub4)1<<(n))
#define hashmask(n) (hashsize(n)-1)
hv = hash(key, nkey, 0);
it = primary_hashtable[hv & hashmask(hashpower)]

以上兩個巨集定義和兩條語句求解hash表位置的item。其中hv的求解依賴自定義的hash函式,這個函式就不分析了。當求解出hv後,和巨集2進行與操作就求出了hash表中桶的位置。而hashsize()就是將1左移n位,達到一個乘二的效果,而hashmask的目的是求出遮擋位:

以下為二進位制:

原本:100000

遮擋:011111

可以看出遮擋位將首位變為0,剩餘的都變為1。然後hv & hashmask的目的就是進行了一次求餘數操作。這種求餘操作避免了使用%這樣消耗比較高的操作,缺點是隻能應用於2的冪次的求餘數操作。但一般為了快速,hash表的桶的個數也是2的冪次數。

求解出桶的位置之後,查詢一個item就簡單了。從桶的第0個元素的位置開始依次沿著h_next進行就可以了。

求餘一般我都會直接使用%方法進行求解,開源專案能讓人提煉自己的程式設計功力,提升基礎。

hash表的擴張

hash表起始桶的個數為16,當存不下之後,hash表需要進行一次擴張操作。hash表的擴張需要一定時間,Memcached為了在表擴張時繼續服務,使用了雙hash表機制:

static item** primary_hashtable = 0;
static item** old_hashtable = 0;

平常主要使用primary_hashtable,當hash表擴張的時候,臨時使用old_hashtable,當hash表擴張完畢之後再切換到primary_hashtable。

Memcached使用assoc_maintenance_thread()這個函式對hash表進行管理,實質上是通過一個守護執行緒死迴圈處理:

while (do_run_maintenance_thread){
    ......
}

通過一個while死迴圈,一直檢視hash表的狀態,當hash表滿的時候對錶進行擴容。

old_hashtable = primary_hashtable;
primary_hashtable = calloc(hashsize(hashpower + 1), sizeof(void *));
if (primary_hashtable) {
    if (settings.verbose > 1)
            fprintf(stderr, "Hash table expansion starting\n");
    hashpower++;
    expanding = true;
    expand_bucket = 0;
    STATS_LOCK();
    stats.hash_power_level = hashpower;
    stats.hash_bytes += hashsize(hashpower) * sizeof(void *);
    stats.hash_is_expanding = 1;
    STATS_UNLOCK();

開始擴張前,將old_hashtable指向主表,隨即主表重新開始分配空間,可以看到新空間的大小是老空間的2倍。隨後再狀態位裡面設定一些標記,記錄hash表新使用的空間。最後將hash_is_expanding置為1,通知執行緒開始對hash表進行擴張操作。

執行緒do_run_maintenance_thread負責將老表中的所有資料依次拷貝到新表中。每個迴圈拷貝一個桶中的所有item,使用者可以設定每個迴圈拷貝多個桶,通過改變hash_bulk_move變數的值。但是這樣可能會導致堆cache鎖佔用的時間過長,影響Memcached對外提供的服務。hash表擴張拷貝的程式碼如下:

item *it, *next;
int bucket;

for (it = old_hashtable[expand_bucket]; NULL != it; it = next) {
    next = it->h_next;

    bucket = hash(ITEM_key(it), it->nkey, 0) & hashmask(hashpower);
    it->h_next = primary_hashtable[bucket];
    primary_hashtable[bucket] = it;
}

old_hashtable[expand_bucket] = NULL
expand_bucket++;

expand_bucket從0開始。拷貝時,將老表第0個桶中的所有元素依次取出,並重新計算在新表中的桶的位置,拷貝到新表中。如果新表當前桶中已經有了item了,那麼就放到桶中的第一個位置中。隨後將老表當前桶的位置置為空。最後對桶計數自增,進入下一個迴圈,繼續拷貝資料。

元素的刪除

memcached刪除元素並不是真的刪除,因為記憶體都是預先分配好的,hash表中存的東西相當於引用。指標變數退出函式後記憶體自動就釋放了。因此元素的刪除只是修改hash表的指標結構:

  item **before = _hashitem_before(key, nkey, hv);

    if (*before) {
        item *nxt;
        hash_items--;
    
        MEMCACHED_ASSOC_DELETE(key, nkey, hash_items);
        nxt = (*before)->h_next;
        (*before)->h_next = 0;   /* probably pointless, but whatever. */
        *before = nxt;  //*before代表item->next指標
        return;
    }

就是將before->h_next=item->h_next操作,經典的跨指標刪除法。函式_hashitem_before查詢當前key對應元素的前一個item,以方便刪除操作。

slab結構

Memcached將所有slab類組織在一起,構成了slab儲存結構。

wps_clip_image-3481_thumb[5]_thumb

圖 1-4 slab結構體系

圖1-4是整個slab儲存體系的結構圖,以slab為核心,將空閒槽、LRU佇列、slab列表、hash表完整的組合在一起。hash表已經在上一個小節中分析了,同時也不好在這張圖中進行表述。本小結介紹slab結構體系。

slab類定義

直接上結構體的定義:

wps_clip_image-3686_thumb[4]_thumb

圖1-5 slab類定義

定義一個結構體為slabclass_t型別。

size表明當前slab類中item的大小,slab id越高,則當前儲存的item的chunk的大小越大,最大是1MB。perslab儲存了當前slab類中擁有多少個item。計算方法很簡單size/perslab就可以計算出來。

slots儲存當前空閒的item。當item不用被回收時,會進入slot中去。slab在分配記憶體時優先從slots中進行分配。sl_curr看見變數定義的很牛逼,其實就是slots的數量。

slabs,一個slab類可能包含多個slab。系統分配記憶體時,當一個slab空間用完之後才會再分配下一個slab,這個slabs就是用於當前記錄當前slab類已經分配了多少個slab了。

slab_list,就是多個slab形成的連結串列,用這個指標進行描述。而下面list_size記錄前一個list的大小。記錄的原因是在分配新的slab list的時,通常分配的數量都是前一個list大小的二倍。(很多空間自動增長的資料結構的內部的經典做法)

killing,在slab進行rebalance中使用的機制,暫時沒有涉及這部分的程式碼,因此先跳過。

requested,已經請求的資料量大小,當前slab類已經分配出去的記憶體。

LRU佇列

每個slab類都有若干item,構成一個LRU列表。當對這個slab進行記憶體分配時,如果記憶體不足,就必須將某個item換出記憶體,騰出空間給其他申請記憶體的item使用。某個slab類的item LRU佇列並沒有直接和slabclass_t直接掛鉤,而是通過slab id進行關聯:

static item *heads[LARGEST_ID];
static item *tails[LARGEST_ID];

在item.c檔案中定義了兩個全域性的指標陣列,這個就是當前系統中所有slabclass的LRU佇列的頭、尾指標。如果想要使用指定的LRU佇列,使用head[id]以及tail[id]就可以對特定列表進行引用了。

v item插入

當系統新分配一個item時,需要將item放入LRU佇列中進行儲存。放入LRU佇列的對首中:

wps_clip_image-3846_thumb[3]_thumb

圖 1-6 item插入

將item插入連結串列的頭部,經典的頭部操作。連結串列插入頭部而不是尾部是有深刻原因的。當一個item插入連結串列時,表明這個item是最新,因此插入頭部。後續進行淘汰item的時候就是從連結串列尾部進行淘汰。Memcached將LRU連結串列中,越靠近頭部的節點,看成越新(更新、插入)的節點,LRU節點尾端的節點看成越老的節點。

v item刪除

刪除指定的item很容易,同樣是經典的指標操作:

wps_clip_image-3938_thumb[2]_thumb

圖 1-7 item unlink

可以看出將指標關係更新後,函式就退出了,並沒有真正刪除資料。沒有free的原因就是memcached自己進行記憶體的管理。釋放item留出的空間放入slab類的空閒槽slot中:

wps_clip_image-3997_thumb[4]_thumb

圖 1-8 item unlink後的操作:掛入slot中

函式do_slabs_free負責將釋放的item掛入slot中。同樣是掛入slots佇列的頭部。

LRU佇列更新

當對LRU佇列中item訪問時,需要更新item的狀態,因為空間不夠的時候換出的是一直沒有使用的item。更新操作:

wps_clip_image-4068_thumb[2]_thumb

圖 1-9 LRU佇列更新

更新關鍵操作就是if語句塊中的三行程式碼:1 把這個item拿出來,2 更新item的訪問時間,3 把這個item再放進去。1和3兩條語句將item從LRU佇列中放入佇列的首部,完成更新的目的。

在slab上分配item

之前的小結都是零碎的一些原始碼部分,這小節以slab的操作為軸,進行相關程式碼的分析工作。

主要呼叫函式do_item_alloc()完成。這個函式的程式碼量較多,業務邏輯較為複雜,這裡進行簡要的分析。

記憶體分配時優先從LRU佇列中超時的item進行分配:

wps_clip_image-4130_thumb[2]_thumb

wps_clip_image-31823_thumb[2]_thumb

圖 1-10 首先從LRU佇列中尋找

search=tails[id],找到slab類id為id的LRU尾指標,並賦值給Search。從LRU佇列末端開始選擇的原因是LRU佇列尾巴儲存著最有可能超時的item,如果佇列末端的都沒超時,則跳過LRU item的換出操作。

從尾端開始查詢,如果當前的hash桶被鎖住了,就跳過,查詢前一個item。如果沒有跳過,並且item也超時了,則將呼叫do_item_unlink_nolock將item從hash表和LRU佇列中移除。

如果LRU最後一個item沒有超時,則表明所有LRU佇列中的item均沒有超時,因此暫時不能從LRU佇列進行記憶體的分配,需要從slab系統進行記憶體的分配工作:

it = slabs_alloc(ntotal, id)

呼叫slabs_alloc()函式從slab系統中進行分配。slabs_alloc()函式操作同樣較為負責,為了不打斷do_item_alloc()的邏輯,先把這個函式放放,等do_item_alloc()函式分析完之後再討論。

如果從slab系統中進行記憶體分配同樣失敗了,則只能還是從LRU佇列中淘汰最舊未使用的item中了:

wps_clip_image-4160_thumb[2]_thumb

圖 1-11 LRU佇列換出item

如果slabs_alloc()失敗,則只能無奈從LRU佇列置換一個item出來。如果使用者設定了不允許進行item置換,則最終只能報錯:outofmemory了。否則更新evicted的item數量,並將這個item換出,更新當前slab類需要的記憶體資訊,將換出的item從slab系統中移除。

當移除之後,返回選定的item,對item做基本的初始化,並將item返回給函式上層呼叫者。

wps_clip_image-30254_thumb[2]_thumb

圖 1-12 分配item的初始化

從slab系統中分配一個新的slab

上小節在item分配的時候,首先檢視LRU佇列中是否有合適的item。如果沒有,則從slab類中進行記憶體的分配,呼叫函式:do_slabs_alloc()。

函式do_slabs_alloc()首先檢查當前slab類中的空閒slot連結串列是否還有可用的slot,如果有則拿一個slot出來:

/* return off our freelist */
        it = (item *)p->slots;
        p->slots = it->next;
        if (it->next) it->next->prev = 0;
        p->sl_curr--;
        ret = (void *)it;

 將slot連結串列中的第一個slot拿出來返回給呼叫者。如果當前slab類已經沒有後空閒的slot連結串列,則需要重新分配記憶體,即呼叫函式:do_slabs_newslab()進行:

if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) ||
        (grow_slab_list(id) == 0) ||//確保slab_list有足夠容量
        ((ptr = memory_allocate((size_t)len)) == 0)) {// memory_allocate 分配 1M 的記憶體空間, 
        //memory_allocate 就是移動指標

        MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id);
        return 0;
    }

分配時,首先呼叫grow_slab_list()確保當前slab類有足夠的空間。函式grow_slab_list()首先判斷已經分配的slab個數是否已經趕上slab list的大小,如果趕上了說明slab list已經分配完了,呼叫realloc()將slab list的大小擴大一倍。

隨後呼叫memory_allocate()分配1MB的空間出來,該函式將返回分配空間的指標mem_current,並將mem_current向前移動1MB大小,為下一次分配記憶體進行準備。又是一個手動管理記憶體的例項。

最後呼叫函式split_slab_page_into_freelist()將分配的記憶體初始化為slots,加入slot連結串列中。函式split_slab_page_into_freelist()將空間切分:

 slabclass_t *p = &slabclass[id];
    int x;
    //將一大塊兒記憶體分割成item,掛載到空閒slot中
    for (x = 0; x < p->perslab; x++) {
        do_slabs_free(ptr, 0, id);
        ptr += p->size;
  }

依次將指標進行步進移動,劃分為item,並將這個item進行fdo_slabs_free操作。而do_slabs_free():

it = (item *)ptr;
    it->it_flags |= ITEM_SLABBED;
    it->prev = 0;
    it->next = p->slots;
    if (it->next) it->next->prev = it;
    p->slots = it;
    p->sl_curr++;
  p->requested -= size;

修改當前item的前後指標,將item移入slots連結串列中去。

在slab上刪除一個item

有了前面小節的基礎,當需要手動刪除item時則比較簡單了,呼叫函式do_item_unlink()完成。

void do_item_unlink(item *it, const uint32_t hv) {
    MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
    mutex_lock(&cache_lock);
    if ((it->it_flags & ITEM_LINKED) != 0) {
        it->it_flags &= ~ITEM_LINKED;
        STATS_LOCK();
        stats.curr_bytes -= ITEM_ntotal(it);
        stats.curr_items -= 1;
        STATS_UNLOCK();
        assoc_delete(ITEM_key(it), it->nkey, hv);//hash表中刪除
        item_unlink_q(it);//LRU佇列中刪除
        do_item_remove(it);//把item放入slot中
    }
    mutex_unlock(&cache_lock);
}

首先設定標誌位,更新狀態資訊。隨後呼叫assoc_delete()將item指標關係從hash表中刪除。再呼叫item_unlink_q()將item指標關係從LRU佇列中刪除。最後呼叫do_item_remove()將item放入空閒slots佇列中。

可以看出,刪除item的時候並不真正的釋放記憶體,而是巧妙的將空閒的item放入slots中,以備將來使用。優秀的記憶體管理操作使得Memcached的效能很高。

小結

本章對slab記憶體管理進行了介紹。可以看出,Memcached在對記憶體管理時,以slab類為核心,通過靈活改變操控hash表、LRU佇列、slab空閒slots三類資料結構,改變item的具體行為以及位置,達成記憶體的分配與管理工作。設計非常合理與巧妙。

相關推薦

Memcached原始碼分析——記憶體管理

注:這篇內容極其混亂 推薦學習這篇部落格。部落格的地址:http://kenby.iteye.com/blog/1423989 基本元素item item是Memcached中記錄儲存的基本單元,使用者向memcached寫入的key value鍵值對資訊都以item的形式存入Memcached中。

Linux核心原始碼分析--記憶體管理(一、分頁機制)

        Linux系統中分為幾大模組:程序排程、記憶體管理、程序通訊、檔案系統、網路模組;各個模組之間都有一定的聯絡,就像蜘蛛網一樣,所以這也是為什麼Linux核心那麼難理解,因為不知道從哪裡開始著手去學習。很多人會跟著系統上電啟動 BIOS-->bootse

Linux核心原始碼分析--記憶體管理(二、函式實現技巧)

        仔細的分析了一下各個記憶體管理函式的實現,發現裡面涉及到了幾個技巧,如果知道了這幾個技巧,那麼閱讀記憶體管理原始碼將會事半功倍(主要是這幾個技巧在幾個函式中都出現過),當然也會選擇性的分析幾個比較重要的函式實現; 函式實現技巧         1、向上取整

memcached原始碼分析-----slab記憶體分配器

        溫馨提示:本文用到了一些可以在啟動memcached設定的全域性變數。關於這些全域性變數的含義可以參考《memcached啟動引數詳解》。對於這些全域性變數,處理方式就像《如何閱讀memcached原始碼》所說的那樣直接取其預設值。 slab記憶體池

nginx原始碼分析記憶體池結構ngx_pool_t及記憶體管理

本部落格(http://blog.csdn.net/livelylittlefish)貼出作者(阿波)相關研究、學習內容所做的筆記,歡迎廣大朋友指正!Content0. 序1. 記憶體池結構1.1 ngx_pool_t結構1.2 其他相關結構1.3 ngx_pool_t的邏輯

Memcached原始碼分析記憶體管理篇之item結構圖及slab結構圖

.Memcached原始碼分析之記憶體管理篇 部落格分類: linuxc  . 使用命令 set(key, value) 向 memcached 插入一條資料, memcached 內部是如何組織資料呢 一 把資料組裝成 item memcached 接受到客戶端的資料後

原始碼記憶體管理--得記憶體者得天下

程序和記憶體管理堪稱核心的任督二脈,是最重要的兩部分,這兩部弄清楚了,主體架構也就確立,其它都是支脈。而這兩者中,又數記憶體管理最難,所以,得記憶體者得天下。   (一) 1.buddy(夥伴)機制。 以頁為單位的大記憶體。   2.slab機制。 管

63.ImageLoader原始碼分析-記憶體快取演算法

一. 前言 圖片記憶體快取可以提高圖片顯示速度,但是有些問題,比如佔用記憶體,如果不加以控制,甚至可能會OOM 所以,需要提供各種各樣的演算法來控制記憶體的使用,以適應不同的使用場景,目前,ImageLoader提供了若干記憶體管理演算法。 預設記憶體快取是關閉的,需要手動開啟 二. 繼承關係圖

Redis原始碼剖析--記憶體管理zmalloc

功能函式總覽 在zmalloc.h中,定義了Redis記憶體分配的主要功能函式,這些函式基本上實現了Redis記憶體申請,釋放和統計等功能,其函式宣告如下: void *zmalloc(size_t size); /

Memcached原始碼分析-網路模型(1)

1 網路模型 Memcached採用了,單程序多執行緒的工作方式,同時採用了libevent事件驅動進行網路請求的處理。 2 工作原理 2.1 libevent介紹 2.2 網路請求流程 2.2.1 流程圖 2.2.2 主執行緒工作流程分析 主執行緒工作流

Memcached原始碼分析-命令解析(3)

#1 流程圖 2 流程說明 1 當進入到conn_read狀態後,會呼叫try_read_network(),將socket資料讀取到conn的rbuf中。 例如:char *rbuf = ‘set key 0 0 4\r\nget name\r\n’。 2

Memcached原始碼分析-HashTable(4)

1 原理圖 2 說明: Memcached在啟動的時候,會預設初始化一個HashTable,這個table的預設長度為65536。 我們將這個HashTable中的每一個元素稱為桶,每個桶就是一個i

Memcached原始碼分析- LRU原理以及get/set命令(6)

1 原理圖 2 LRU原理 每一個slabclass都會對應一個heads指標指向item連結串列的頭部,tails指向items連結串列的尾部。組成一個LRU連結串列,連結串列是按照訪問時間順序排列

Libevent原始碼分析-----記憶體分配

    Libevent的記憶體分配函式還是比較簡單的,並沒有定義記憶體池之類的東西。如同前一篇部落格那樣,給予Libevent庫的使用者充分的設定權(定製),即可以設定使用者(Libevent庫的使用者)自己的記憶體分配函式。至於怎麼分配,主動權在於使用者。但在設定(定製)

fresco原始碼分析-記憶體回收

           我覺得記憶體管理是三方相簿最重要的點, 而且該知識點能夠應用到專案裡, 所以著重看了一下fresco是如何回收記憶體的。      fresco記憶體釋放分為2種方式: 1、按照LruCach的方式釋放引用計數為0物件, fresco內部邏輯實現; 2、

Memcached原始碼分析——連線狀態變化分析(drive_machine)

這篇文章主要介紹Memcached中,基於libevent構造的主執行緒和worker執行緒所處理連線的狀態互相轉換的過程(不涉資料的存取等操作),也就是drive_machine的主要業務邏輯了。狀態轉換過程沒有涉及所有狀態,同時,由於自己能力問題,一些狀態轉換還可能有錯,還請各位前輩指正。轉換條件限定:T

python原始碼分析----記憶體分配(2)

早就應該寫部分的內容了。。。。最近比較負能量。。。傷不起啊。。 上一篇說到了,在python的記憶體分配中兩個非常重要的方法:PyObject_Malloc和PyObject_Free 在具體的來這兩個方法之前,先要看看別的一些東西 //這裡用usedpool構成了一個雙

Redis原始碼分析-記憶體分配

本文轉載自Day Day Up部落格,文章對Redis的記憶體分配封裝庫進行了分析,描述了Redis在記憶體分配和使用統計方面的各種細節和技巧。 Redis中到處都會進行記憶體分配操作。為了遮蔽不同平臺之間的差異,以及統計記憶體佔用量等,Redis對記憶體分配函式進

Memcached原始碼分析之基於Libevent的網路模型(1)

文章列表: 《Memcached原始碼分析 - Memcached原始碼分析之總結篇(8)》 關於Memcached: memcached是一款非常普及的伺服器端快取軟體,memcached主要是基於Libevent庫進行開發的。 如果你還不瞭解libev

memcached 原始碼分析——半同步、半非同步模式

memcached 是目前應用非常廣泛的快取伺服器,採用的是半同步、半非同步模式。 半同步、半非同步 半同步/半非同步模型的基礎設施:主執行緒建立多個子執行緒(這些子執行緒也稱為worker執行緒),每一個執行緒都維持自己的事件迴圈,即每個執行緒都有自己