/**
* 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;
}
|