|
/** * Generates the variable-sized part of the header for an object. * * key - The key * nkey - The length of the key * flags - key flags * nbytes - value长度+2('\r\n') * suffix - 存储flag和value_len * nsuffix - The length of the suffix is stored here. * * Returns the total size of the header. */
//由这里可以推出item的存储格式:item struct + key+1 + suffix +value + '\r\n' static size_t item_make_header(const uint8_t nkey, const int flags, const int nbytes, char *suffix, uint8_t *nsuffix) { /* suffix is defined at 40 chars elsewhere.. */ //nbytes-2=value length *nsuffix = (uint8_t) snprintf(suffix, 40, " %d %d\r\n", flags, nbytes - 2); return sizeof(item) + nkey + *nsuffix + nbytes; }
//分配一个item空间 item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes) { uint8_t nsuffix; item *it = NULL; char suffix[40]; size_t ntotal = item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix); if (settings.use_cas) { ntotal += sizeof(uint64_t); } unsigned int id = slabs_clsid(ntotal); //寻找合适的slabclass if (id == 0) return 0; /* do a quick check if we have any expired items in the tail.. */ int tries = 50; item *search; //首先从LRU链尾开始,寻找一个过期的item, 最多尝试50次 for (search = tails[id];tries > 0 && search != NULL; tries--, search=search->prev) { if (search->refcount == 0 && (search->exptime != 0 && search->exptime < current_time)) { it = search; /* I don't want to actually free the object, just steal * the item to avoid to grab the slab mutex twice ;-) */ STATS_LOCK(); stats.reclaimed++; STATS_UNLOCK(); itemstats[id].reclaimed++; it->refcount = 1; do_item_unlink(it); //将寻找到的item移除 /* Initialize the item block: */ it->slabs_clsid = 0; it->refcount = 0; break; } }
if (it == NULL && (it = slabs_alloc(ntotal, id)) == NULL) { /* ** Could not find an expired item at the tail, and memory allocation ** failed. Try to evict some items! */ tries = 50; //没有找到过期item并分配chunk失败,则进行LRU淘汰一个item /* If requested to not push old items out of cache when memory runs out, * we're out of luck at this point... */ if (settings.evict_to_free == 0) { itemstats[id].outofmemory++; return NULL; } /* * try to get one off the right LRU * don't necessariuly unlink the tail because it may be locked: refcount>0 * search up from tail an item with refcount==0 and unlink it; give up after 50 * tries */ if (tails[id] == 0) { itemstats[id].outofmemory++; return NULL; } for (search = tails[id]; tries > 0 && search != NULL; tries--, search=search->prev) { if (search->refcount == 0) { if (search->exptime == 0 || search->exptime > current_time) { itemstats[id].evicted++; itemstats[id].evicted_time = current_time - search->time; if (search->exptime != 0) itemstats[id].evicted_nonzero++; STATS_LOCK(); stats.evictions++; STATS_UNLOCK(); } else { itemstats[id].reclaimed++; STATS_LOCK(); stats.reclaimed++; STATS_UNLOCK(); } do_item_unlink(search); break; } } it = slabs_alloc(ntotal, id); //LRU淘汰没有找到,再尝试一次内存分配。。。 if (it == 0) { itemstats[id].outofmemory++; /* Last ditch effort(最后一搏). There is a very rare bug which causes * refcount leaks. We've fixed most of them, but it still happens, * and it may happen in the future. * We can reasonably assume no item can stay locked for more than * three hours, so if we find one in the tail which is that old, * free it anyway. */ tries = 50; //分配失败,再尝试一次LRU淘汰 for (search = tails[id]; tries > 0 && search != NULL; tries--, search=search->prev) { if (search->refcount != 0 && search->time + TAIL_REPAIR_TIME < current_time) { itemstats[id].tailrepairs++; search->refcount = 0; do_item_unlink(search); break; } } it = slabs_alloc(ntotal, id); //最后一次chunk分配 if (it == 0) { return NULL; } } }
assert(it->slabs_clsid == 0); //标志已经找到item时 slabs_clsid=0 it->slabs_clsid = id;
assert(it != heads[it->slabs_clsid]);
it->next = it->prev = it->h_next = 0; it->refcount = 1; /* the caller will have a reference */ DEBUG_REFCNT(it, '*'); it->it_flags = settings.use_cas ? ITEM_CAS : 0; it->nkey = nkey; it->nbytes = nbytes; memcpy(ITEM_key(it), key, nkey); //存放key string it->exptime = exptime; memcpy(ITEM_suffix(it), suffix, (size_t)nsuffix); //存放suffix it->nsuffix = nsuffix; return it; //注意:这里并没有存放value string, 要等到调用函数来memcpy }
//释放item
void item_free(item *it) { size_t ntotal = ITEM_ntotal(it); unsigned int clsid; assert((it->it_flags & ITEM_LINKED) == 0); //非LINKED状态才能free assert(it != heads[it->slabs_clsid]); assert(it != tails[it->slabs_clsid]); assert(it->refcount == 0);
/* so slab size changer can tell later if item is already free or not */ clsid = it->slabs_clsid; it->slabs_clsid = 0; it->it_flags |= ITEM_SLABBED; //标志归还内存到slab,唯一 一次使用 ITEM_SLABBED DEBUG_REFCNT(it, 'F'); slabs_free(it, ntotal, clsid); //是否slab memory }
/** * Returns true if an item will fit in the cache (its size does not exceed * the maximum for a cache entry.) */ bool item_size_ok(const size_t nkey, const int flags, const int nbytes) { char prefix[40]; uint8_t nsuffix;
return slabs_clsid(item_make_header(nkey + 1, flags, nbytes, prefix, &nsuffix)) != 0; }
|