首页
Web开发
Windows程序
编程语言
数据库
移动开发
系统相关
微信
其他好文
会员
首页
>
系统相关
> 详细
Memcached源码分析之items.c
时间:
2016-09-07 19:19:46
阅读:
235
评论:
0
收藏:
0
[点我收藏+]
标签:
#include
"memcached.h"
#include
<sys/stat.h>
#include
<sys/socket.h>
#include
<sys/signal.h>
#include
<sys/resource.h>
#include
<fcntl.h>
#include
<netinet/in.h>
#include
<errno.h>
#include
<stdlib.h>
#include
<stdio.h>
#include
<string.h>
#include
<time.h>
#include
<assert.h>
#include
<unistd.h>
static
void
item_link_q
(
item
*
it
);
static
void
item_unlink_q
(
item
*
it
);
#define
LARGEST_ID POWER_LARGEST
typedef
struct
{
uint64_t
evicted
;
uint64_t
evicted_nonzero
;
rel_time_t
evicted_time
;
uint64_t
reclaimed
;
uint64_t
outofmemory
;
uint64_t
tailrepairs
;
uint64_t
expired_unfetched
;
uint64_t
evicted_unfetched
;
uint64_t
crawler_reclaimed
;
}
itemstats_t
;
static
item
*
heads
[
LARGEST_ID
];
//各个slabclass的LRU队列头指针数组
static
item
*
tails
[
LARGEST_ID
];
//各个slabclass的LRU队列尾指针数组
static
crawler crawlers
[
LARGEST_ID
];
//各个slabclass的item爬虫数组
static
itemstats_t
itemstats
[
LARGEST_ID
];
//各个slabclass的item统计数组
static
unsigned
int
sizes
[
LARGEST_ID
];
//各个slabclass的chunk大小数组
static
int
crawler_count
=
0
;
static
volatile
int
do_run_lru_crawler_thread
=
0
;
static
int
lru_crawler_initialized
=
0
;
static
pthread_mutex_t
lru_crawler_lock
=
PTHREAD_MUTEX_INITIALIZER
;
static
pthread_cond_t
lru_crawler_cond
=
PTHREAD_COND_INITIALIZER
;
//重置统计
void
item_stats_reset
(
void
)
{
mutex_lock
(&
cache_lock
);
memset
(
itemstats
,
0
,
sizeof
(
itemstats
));
mutex_unlock
(&
cache_lock
);
}
/* Get the next CAS id for a new item. */
uint64_t
get_cas_id
(
void
)
{
static
uint64_t
cas_id
=
0
;
return
++
cas_id
;
}
/* Enable this for reference-count debugging. */
#if 0
# define DEBUG_REFCNT(it,op) \
fprintf
(
stderr
,
"item %x refcnt(%c) %d %c%c%c\n"
,
\
it
,
op
,
it
->
refcount
,
\
(
it
->
it_flags
&
ITEM_LINKED
)
?
‘L‘
:
‘ ‘
,
\
(
it
->
it_flags
&
ITEM_SLABBED
)
?
‘S‘
:
‘ ‘
)
#else
# define DEBUG_REFCNT(it,op) while(0)
#endif
/**
算出item总大小
*/
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.. */
*
nsuffix
=
(
uint8_t
)
snprintf
(
suffix
,
40
,
" %d %d\r\n"
,
flags
,
nbytes
-
2
);
return
sizeof
(
item
)
+
nkey
+
*
nsuffix
+
nbytes
;
}
/**
item分配
把这个函数弄清楚,基本就把memcached内存管理机制大体弄清楚了。
*/
item
*
do_item_alloc
(
char
*
key
,
const
size_t
nkey
,
const
int
flags
,
const
rel_time_t
exptime
,
const
int
nbytes
,
const
uint32_t
cur_hv
)
{
uint8_t
nsuffix
;
item
*
it
=
NULL
;
char
suffix
[
40
];
size_t
ntotal
=
item_make_header
(
nkey
+
1
,
flags
,
nbytes
,
suffix
,
&
nsuffix
);
//item总大小
if
(
settings
.
use_cas
)
{
ntotal
+=
sizeof
(
uint64_t
);
//如果有用到cas 那么item大小还要加上unit64_t的size
}
unsigned
int
id
=
slabs_clsid
(
ntotal
);
//根据item大小,找到适合的slabclass
if
(
id
==
0
)
return
0
;
mutex_lock
(&
cache_lock
);
//cache锁
/* do a quick check if we have any expired items in the tail.. */
/* 准备分配新的item了,随便快速瞄一下lru链表末尾有没有过期item,有的话就用过期的空间 */
int
tries
=
5
;
int
tried_alloc
=
0
;
item
*
search
;
void
*
hold_lock
=
NULL
;
rel_time_t
oldest_live
=
settings
.
oldest_live
;
search
=
tails
[
id
];
//这个tails是一个全局变量,tails[xx]是id为xx的slabclass lru链表的尾部
/* We walk up *only* for locked items. Never searching for expired.
* Waste of CPU for almost all deployments */
//从LRU链表尾部(就是最久没使用过的item)开始往前找
for
(;
tries
>
0
&&
search
!=
NULL
;
tries
--,
search
=
search
->
prev
)
{
if
(
search
->
nbytes
==
0
&&
search
->
nkey
==
0
&&
search
->
it_flags
==
1
)
{
/* We are a crawler, ignore it. */
/*
这里注释意思是说我们现在是以爬虫的身份来爬出过期的空间,
像爬到这种很怪的item,就别管了,不是爬虫要做的事,不要就行了。
*/
tries
++;
continue
;
}
/**
你会看到很多地方有这个hv,简单说下,其实它是对item的一个hash,得到hv值,这个hv主要有两个
作用:
1)用于hash表保存item,通过hv计算出哈希表中的桶号
2)用于item lock表中锁住item,通过hv计算出应该用item lock表中哪个锁对当前item进行加锁
这两者都涉及到一个粒度问题,不可能保证每个不一样的key的hv不会相同,所有hash方法都可能
出现冲突。
所以hash表中用链表的方式处理冲突的item,而item lock表中会多个item共享一个锁,或者说
多个桶共享一个锁。
*/
uint32_t
hv
=
hash
(
ITEM_key
(
search
),
search
->
nkey
);
/**
尝试去锁住当前item。
*/
if
(
hv
==
cur_hv
||
(
hold_lock
=
item_trylock
(
hv
))
==
NULL
)
continue
;
/* Now see if the item is refcount locked */
if
(
refcount_incr
(&
search
->
refcount
)
!=
2
)
{
refcount_decr
(&
search
->
refcount
);
/* Old rare bug could cause a refcount leak. We haven‘t seen
* it in years, but we leave this code in to prevent failures
* just in case
没看懂这里的意思.....
*/
if
(
settings
.
tail_repair_time
&&
search
->
time
+
settings
.
tail_repair_time
<
current_time
)
{
itemstats
[
id
].
tailrepairs
++;
search
->
refcount
=
1
;
do_item_unlink_nolock
(
search
,
hv
);
}
if
(
hold_lock
)
item_trylock_unlock
(
hold_lock
);
continue
;
}
/* Expired or flushed */
//超时了...
if
((
search
->
exptime
!=
0
&&
search
->
exptime
<
current_time
)
||
(
search
->
time
<=
oldest_live
&&
oldest_live
<=
current_time
))
{
itemstats
[
id
].
reclaimed
++;
if
((
search
->
it_flags
&
ITEM_FETCHED
)
==
0
)
{
itemstats
[
id
].
expired_unfetched
++;
}
it
=
search
;
//拿下空间
slabs_adjust_mem_requested
(
it
->
slabs_clsid
,
ITEM_ntotal
(
it
),
ntotal
);
//更新统计数据
/**
什么是link,在这简单说下,就是把item加到哈希表和LRU链表的过程。详见items::do_item_link函数这里把item旧的link取消掉,当前函数do_item_alloc的工作只是拿空间,而往后可知道拿到item空间后会对这块item进行“link”工作,而这里这块item空间是旧的item超时然后拿来用的,所以先把它unlink掉
*/
do_item_unlink_nolock
(
it
,
hv
);
/* Initialize the item block: */
it
->
slabs_clsid
=
0
;
}
else
if
((
it
=
slabs_alloc
(
ntotal
,
id
))
==
NULL
)
{
/*如果没有找到超时的item,则
调用slabs_alloc分配空间,详见slabs_alloc
如果slabs_alloc分配空间失败,即返回NULL,则往下走,下面的代码是
把LRU列表最后一个给淘汰,即使item没有过期。
这里一般是可用内存已经满了,需要按LRU进行淘汰的时候。
//************mark: $1**************
*/
tried_alloc
=
1
;
//标记一下,表示有进入此分支,表示有尝试过调用slabs_alloc去分配新的空间。
//记下被淘汰item的信息,像我们使用memcached经常会查看的evicted_time就是在这里赋值啦!
if
(
settings
.
evict_to_free
==
0
)
{
itemstats
[
id
].
outofmemory
++;
}
else
{
itemstats
[
id
].
evicted
++;
itemstats
[
id
].
evicted_time
=
current_time
-
search
->
time
;
//被淘汰的item距离上次使用多长时间了
if
(
search
->
exptime
!=
0
)
itemstats
[
id
].
evicted_nonzero
++;
if
((
search
->
it_flags
&
ITEM_FETCHED
)
==
0
)
{
itemstats
[
id
].
evicted_unfetched
++;
}
it
=
search
;
slabs_adjust_mem_requested
(
it
->
slabs_clsid
,
ITEM_ntotal
(
it
),
ntotal
);
//更新统计数据
do_item_unlink_nolock
(
it
,
hv
);
//从哈希表和LRU链表中删掉
/* Initialize the item block: */
it
->
slabs_clsid
=
0
;
/*
如果当前slabclass有item被淘汰掉了,说明可用内存都满了,再也没有
slab可分配了,
而如果 slab_automove=2 (默认是1),这样会导致angry模式,
就是只要分配失败了,就马上进行slab重分配:把别的slabclass空间牺牲
掉一些,马上给现在的slabclass分配空间,而不会合理地根据淘汰统计
数据来分析要怎么重分配(slab_automove = 1则会)。
*/
if
(
settings
.
slab_automove
==
2
)
slabs_reassign
(-
1
,
id
);
}
}
refcount_decr
(&
search
->
refcount
);
/* If hash values were equal, we don‘t grab a second lock */
if
(
hold_lock
)
item_trylock_unlock
(
hold_lock
);
break
;
}
/**
如果上面的for循环里面没有找到空间,并且没有进入过else if ((it = slabs_alloc(ntotal, id)) == NULL)这个分支没有
尝试调slabs_alloc分配空间(有这种可能性),那么,下面这行代码就是再尝试分配。
你会觉得上面那个循环写得特纠结,逻辑不清,估计你也看醉了。其实整个分配原则是这样子:
1)先从LRU链表找下看看有没有恰好过期的空间,有的话就用这个空间。
2)如果没有过期的空间,就分配新的空间。
3)如果分配新的空间失败,那么往往是内存都用光了,则从LRU链表中把最旧的即使没过期的item淘汰掉,空间分给新的item用。
问题是:这个从“LRU链表找到的item”是一个不确定的东西,有可能这个item数据异常,有可能这个item由于与别的item共用锁的桶号
这个桶被锁住了,所以总之各种原因这个item此刻不一定可用,因此用了一个循环尝试找几次(上面是5)。
所以逻辑是:
1)我先找5次LRU看看有没有可用的过期的item,有就用它。(for循环5次)
2)5次没有找到可用的过期的item,那我分配新的。
3)分配新的不成功,那我再找5次看看有没有可用的虽然没过期的item,淘汰它,把空间给新的item用。(for循环5次)
那么这里有个问题,如果代码要写得逻辑清晰一点,我得写两个for循环,一个是为了第2)步前“找可用的过期的”item,
一个是第2)步不成功后“找可用的用来淘汰的”空间。而且有重复的逻辑“找到可用的”,所以memcached作者就合在一起了,
然后只能把第2)步也塞到for循环里面,确实挺尴尬的。。。估计memcached作者也写得很纠结。。。
所以就很有可能出现5次都没找到可用的空间,都没进入过elseif那个分支就被continue掉了,为了记下有没有进过elseif
分支就挫挫地用一个tried_alloc变量来做记号。。
*/
if
(!
tried_alloc
&&
(
tries
==
0
||
search
==
NULL
))
it
=
slabs_alloc
(
ntotal
,
id
);
if
(
it
==
NULL
)
{
itemstats
[
id
].
outofmemory
++;
mutex_unlock
(&
cache_lock
);
return
NULL
;
//没错!会有分配新空间不成功,而且尝试5次淘汰旧的item也没成功的时候,只能返回NULL。。
}
assert
(
it
->
slabs_clsid
==
0
);
assert
(
it
!=
heads
[
id
]);
//来到这里,说明item分配成功,下面主要是一些初始化工作。
/* Item initialization can happen outside of the lock; the item‘s already
* been removed from the slab LRU.
*/
it
->
refcount
=
1
;
/* the caller will have a reference */
mutex_unlock
(&
cache_lock
);
it
->
next
=
it
->
prev
=
it
->
h_next
=
0
;
it
->
slabs_clsid
=
id
;
DEBUG_REFCNT
(
it
,
‘*‘
);
it
->
it_flags
=
settings
.
use_cas
?
ITEM_CAS
:
0
;
it
->
nkey
=
nkey
;
it
->
nbytes
=
nbytes
;
memcpy
(
ITEM_key
(
it
),
key
,
nkey
);
it
->
exptime
=
exptime
;
memcpy
(
ITEM_suffix
(
it
),
suffix
,
(
size_t
)
nsuffix
);
it
->
nsuffix
=
nsuffix
;
return
it
;
}
/**
把这块item free掉,以供再利用,注意这里的free不是指把内存空间释放哦,
而是把这块item 变为“空闲”
*/
void
item_free
(
item
*
it
)
{
size_t
ntotal
=
ITEM_ntotal
(
it
);
unsigned
int
clsid
;
assert
((
it
->
it_flags
&
ITEM_LINKED
)
==
0
);
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
;
//在这把free掉的item 的slabs_clsid设为0
DEBUG_REFCNT
(
it
,
‘F‘
);
slabs_free
(
it
,
ntotal
,
clsid
);
}
/**
* 检查item大小
*/
bool
item_size_ok
(
const
size_t
nkey
,
const
int
flags
,
const
int
nbytes
)
{
char
prefix
[
40
];
uint8_t
nsuffix
;
size_t
ntotal
=
item_make_header
(
nkey
+
1
,
flags
,
nbytes
,
prefix
,
&
nsuffix
);
if
(
settings
.
use_cas
)
{
ntotal
+=
sizeof
(
uint64_t
);
}
return
slabs_clsid
(
ntotal
)
!=
0
;
}
/**
把item插入相应的slabclass lru链表中而已
*/
static
void
item_link_q
(
item
*
it
)
{
/* item is the new head */
item
**
head
,
**
tail
;
assert
(
it
->
slabs_clsid
<
LARGEST_ID
);
assert
((
it
->
it_flags
&
ITEM_SLABBED
)
==
0
);
head
=
&
heads
[
it
->
slabs_clsid
];
tail
=
&
tails
[
it
->
slabs_clsid
];
assert
(
it
!=
*
head
);
assert
((*
head
&&
*
tail
)
||
(*
head
==
0
&&
*
tail
==
0
));
it
->
prev
=
0
;
it
->
next
=
*
head
;
if
(
it
->
next
)
it
->
next
->
prev
=
it
;
*
head
=
it
;
if
(*
tail
==
0
)
*
tail
=
it
;
sizes
[
it
->
slabs_clsid
]++;
return
;
}
/**
把item从相应的slabclass lru链表中删掉而已,下面就是经典的删除链表逻辑代码了
*/
static
void
item_unlink_q
(
item
*
it
)
{
item
**
head
,
**
tail
;
assert
(
it
->
slabs_clsid
<
LARGEST_ID
);
head
=
&
heads
[
it
->
slabs_clsid
];
tail
=
&
tails
[
it
->
slabs_clsid
];
if
(*
head
==
it
)
{
assert
(
it
->
prev
==
0
);
*
head
=
it
->
next
;
}
if
(*
tail
==
it
)
{
assert
(
it
->
next
==
0
);
*
tail
=
it
->
prev
;
}
assert
(
it
->
next
!=
it
);
assert
(
it
->
prev
!=
it
);
if
(
it
->
next
)
it
->
next
->
prev
=
it
->
prev
;
if
(
it
->
prev
)
it
->
prev
->
next
=
it
->
next
;
sizes
[
it
->
slabs_clsid
]--;
return
;
}
/**
把item "link"起来,主要包括:
1)改变一些统计数据
2)把item加到哈希表
3)把item插入到相应的slabclass lru链表中
*/
int
do_item_link
(
item
*
it
,
const
uint32_t
hv
)
{
MEMCACHED_ITEM_LINK
(
ITEM_key
(
it
),
it
->
nkey
,
it
->
nbytes
);
assert
((
it
->
it_flags
&
(
ITEM_LINKED
|
ITEM_SLABBED
))
==
0
);
mutex_lock
(&
cache_lock
);
it
->
it_flags
|=
ITEM_LINKED
;
it
->
time
=
current_time
;
STATS_LOCK
();
stats
.
curr_bytes
+=
ITEM_ntotal
(
it
);
stats
.
curr_items
+=
1
;
stats
.
total_items
+=
1
;
STATS_UNLOCK
();
/* Allocate a new CAS ID on link. */
ITEM_set_cas
(
it
,
(
settings
.
use_cas
)
?
get_cas_id
()
:
0
);
assoc_insert
(
it
,
hv
);
//插入哈希表
item_link_q
(
it
);
//加入LRU链表
refcount_incr
(&
it
->
refcount
);
mutex_unlock
(&
cache_lock
);
return
1
;
}
/**
就是和do_item_link反过来的一些操作
*/
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
);
item_unlink_q
(
it
);
do_item_remove
(
it
);
}
mutex_unlock
(&
cache_lock
);
}
/* FIXME: Is it necessary to keep this copy/pasted code? */
void
do_item_unlink_nolock
(
item
*
it
,
const
uint32_t
hv
)
{
MEMCACHED_ITEM_UNLINK
(
ITEM_key
(
it
),
it
->
nkey
,
it
->
nbytes
);
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
);
item_unlink_q
(
it
);
do_item_remove
(
it
);
}
}
/**
指向item的指针不用的时候都会调用此函数
*/
void
do_item_remove
(
item
*
it
)
{
MEMCACHED_ITEM_REMOVE
(
ITEM_key
(
it
),
it
->
nkey
,
it
->
nbytes
);
assert
((
it
->
it_flags
&
ITEM_SLABBED
)
==
0
);
assert
(
it
->
refcount
>
0
);
if
(
refcount_decr
(&
it
->
refcount
)
==
0
)
{
//引用计数减1,当引用计数为0时,才真正把item free掉。
item_free
(
it
);
}
}
/**
主要作用是重置在最近使用链表中的位置,更新最近使用时间
*/
void
do_item_update
(
item
*
it
)
{
MEMCACHED_ITEM_UPDATE
(
ITEM_key
(
it
),
it
->
nkey
,
it
->
nbytes
);
if
(
it
->
time
<
current_time
-
ITEM_UPDATE_INTERVAL
)
{
assert
((
it
->
it_flags
&
ITEM_SLABBED
)
==
0
);
mutex_lock
(&
cache_lock
);
if
((
it
->
it_flags
&
ITEM_LINKED
)
!=
0
)
{
item_unlink_q
(
it
);
it
->
time
=
current_time
;
item_link_q
(
it
);
}
mutex_unlock
(&
cache_lock
);
}
}
int
do_item_replace
(
item
*
it
,
item
*
new_it
,
const
uint32_t
hv
)
{
MEMCACHED_ITEM_REPLACE
(
ITEM_key
(
it
),
it
->
nkey
,
it
->
nbytes
,
ITEM_key
(
new_it
),
new_it
->
nkey
,
new_it
->
nbytes
);
assert
((
it
->
it_flags
&
ITEM_SLABBED
)
==
0
);
do_item_unlink
(
it
,
hv
);
return
do_item_link
(
new_it
,
hv
);
}
void
item_stats_evictions
(
uint64_t
*
evicted
)
{
int
i
;
mutex_lock
(&
cache_lock
);
for
(
i
=
0
;
i
<
LARGEST_ID
;
i
++)
{
evicted
[
i
]
=
itemstats
[
i
].
evicted
;
}
mutex_unlock
(&
cache_lock
);
}
void
do_item_stats_totals
(
ADD_STAT add_stats
,
void
*
c
)
{
itemstats_t
totals
;
memset
(&
totals
,
0
,
sizeof
(
itemstats_t
));
int
i
;
for
(
i
=
0
;
i
<
LARGEST_ID
;
i
++)
{
totals
.
expired_unfetched
+=
itemstats
[
i
].
expired_unfetched
;
totals
.
evicted_unfetched
+=
itemstats
[
i
].
evicted_unfetched
;
totals
.
evicted
+=
itemstats
[
i
].
evicted
;
totals
.
reclaimed
+=
itemstats
[
i
].
reclaimed
;
totals
.
crawler_reclaimed
+=
itemstats
[
i
].
crawler_reclaimed
;
}
APPEND_STAT
(
"expired_unfetched"
,
"%llu"
,
(
unsigned
long
long
)
totals
.
expired_unfetched
);
APPEND_STAT
(
"evicted_unfetched"
,
"%llu"
,
(
unsigned
long
long
)
totals
.
evicted_unfetched
);
APPEND_STAT
(
"evictions"
,
"%llu"
,
(
unsigned
long
long
)
totals
.
evicted
);
APPEND_STAT
(
"reclaimed"
,
"%llu"
,
(
unsigned
long
long
)
totals
.
reclaimed
);
APPEND_STAT
(
"crawler_reclaimed"
,
"%llu"
,
(
unsigned
long
long
)
totals
.
crawler_reclaimed
);
}
void
do_item_stats
(
ADD_STAT add_stats
,
void
*
c
)
{
int
i
;
for
(
i
=
0
;
i
<
LARGEST_ID
;
i
++)
{
if
(
tails
[
i
]
!=
NULL
)
{
const
char
*
fmt
=
"items:%d:%s"
;
char
key_str
[
STAT_KEY_LEN
];
char
val_str
[
STAT_VAL_LEN
];
int
klen
=
0
,
vlen
=
0
;
if
(
tails
[
i
]
==
NULL
)
{
/* We removed all of the items in this slab class */
continue
;
}
APPEND_NUM_FMT_STAT
(
fmt
,
i
,
"number"
,
"%u"
,
sizes
[
i
]);
APPEND_NUM_FMT_STAT
(
fmt
,
i
,
"age"
,
"%u"
,
current_time
-
tails
[
i
]->
time
);
APPEND_NUM_FMT_STAT
(
fmt
,
i
,
"evicted"
,
"%llu"
,
(
unsigned
long
long
)
itemstats
[
i
].
evicted
);
APPEND_NUM_FMT_STAT
(
fmt
,
i
,
"evicted_nonzero"
,
"%llu"
,
(
unsigned
long
long
)
itemstats
[
i
].
evicted_nonzero
);
APPEND_NUM_FMT_STAT
(
fmt
,
i
,
"evicted_time"
,
"%u"
,
itemstats
[
i
].
evicted_time
);
APPEND_NUM_FMT_STAT
(
fmt
,
i
,
"outofmemory"
,
"%llu"
,
(
unsigned
long
long
)
itemstats
[
i
].
outofmemory
);
APPEND_NUM_FMT_STAT
(
fmt
,
i
,
"tailrepairs"
,
"%llu"
,
(
unsigned
long
long
)
itemstats
[
i
].
tailrepairs
);
APPEND_NUM_FMT_STAT
(
fmt
,
i
,
"reclaimed"
,
"%llu"
,
(
unsigned
long
long
)
itemstats
[
i
].
reclaimed
);
APPEND_NUM_FMT_STAT
(
fmt
,
i
,
"expired_unfetched"
,
"%llu"
,
(
unsigned
long
long
)
itemstats
[
i
].
expired_unfetched
);
APPEND_NUM_FMT_STAT
(
fmt
,
i
,
"evicted_unfetched"
,
"%llu"
,
(
unsigned
long
long
)
itemstats
[
i
].
evicted_unfetched
);
APPEND_NUM_FMT_STAT
(
fmt
,
i
,
"crawler_reclaimed"
,
"%llu"
,
(
unsigned
long
long
)
itemstats
[
i
].
crawler_reclaimed
);
}
}
/* getting here means both ascii and binary terminators fit */
add_stats
(
NULL
,
0
,
NULL
,
0
,
c
);
}
void
do_item_stats_sizes
(
ADD_STAT add_stats
,
void
*
c
)
{
/* max 1MB object, divided into 32 bytes size buckets */
const
int
num_buckets
=
32768
;
unsigned
int
*
histogram
=
calloc
(
num_buckets
,
sizeof
(
int
));
if
(
histogram
!=
NULL
)
{
int
i
;
/* build the histogram */
for
(
i
=
0
;
i
<
LARGEST_ID
;
i
++)
{
item
*
iter
=
heads
[
i
];
while
(
iter
)
{
int
ntotal
=
ITEM_ntotal
(
iter
);
int
bucket
=
ntotal
/
32
;
if
((
ntotal
%
32
)
!=
0
)
bucket
++;
if
(
bucket
<
num_buckets
)
histogram
[
bucket
]++;
iter
=
iter
->
next
;
}
}
/* write the buffer */
for
(
i
=
0
;
i
<
num_buckets
;
i
++)
{
if
(
histogram
[
i
]
!=
0
)
{
char
key
[
8
];
snprintf
(
key
,
sizeof
(
key
),
"%d"
,
i
*
32
);
APPEND_STAT
(
key
,
"%u"
,
histogram
[
i
]);
}
}
free
(
histogram
);
}
add_stats
(
NULL
,
0
,
NULL
,
0
,
c
);
}
//读取item数据
item
*
do_item_get
(
const
char
*
key
,
const
size_t
nkey
,
const
uint32_t
hv
)
{
//mutex_lock(&cache_lock);
item
*
it
=
assoc_find
(
key
,
nkey
,
hv
);
if
(
it
!=
NULL
)
{
refcount_incr
(&
it
->
refcount
);
if
(
slab_rebalance_signal
&&
((
void
*)
it
>=
slab_rebal
.
slab_start
&&
(
void
*)
it
<
slab_rebal
.
slab_end
))
{
do_item_unlink_nolock
(
it
,
hv
);
do_item_remove
(
it
);
it
=
NULL
;
}
}
//mutex_unlock(&cache_lock);
int
was_found
=
0
;
if
(
settings
.
verbose
>
2
)
{
int
ii
;
if
(
it
==
NULL
)
{
fprintf
(
stderr
,
"> NOT FOUND "
);
}
else
{
fprintf
(
stderr
,
"> FOUND KEY "
);
was_found
++;
}
for
(
ii
=
0
;
ii
<
nkey
;
++
ii
)
{
fprintf
(
stderr
,
"%c"
,
key
[
ii
]);
}
}
if
(
it
!=
NULL
)
{
if
(
settings
.
oldest_live
!=
0
&&
settings
.
oldest_live
<=
current_time
&&
it
->
time
<=
settings
.
oldest_live
)
{
do_item_unlink
(
it
,
hv
);
do_item_remove
(
it
);
it
=
NULL
;
if
(
was_found
)
{
fprintf
(
stderr
,
" -nuked by flush"
);
}
}
else
if
(
it
->
exptime
!=
0
&&
it
->
exptime
<=
current_time
)
{
do_item_unlink
(
it
,
hv
);
do_item_remove
(
it
);
it
=
NULL
;
if
(
was_found
)
{
fprintf
(
stderr
,
" -nuked by expire"
);
}
}
else
{
it
->
it_flags
|=
ITEM_FETCHED
;
DEBUG_REFCNT
(
it
,
‘+‘
);
}
}
if
(
settings
.
verbose
>
2
)
fprintf
(
stderr
,
"\n"
);
return
it
;
}
item
*
do_item_touch
(
const
char
*
key
,
size_t
nkey
,
uint32_t
exptime
,
const
uint32_t
hv
)
{
item
*
it
=
do_item_get
(
key
,
nkey
,
hv
);
if
(
it
!=
NULL
)
{
it
->
exptime
=
exptime
;
}
return
it
;
}
/* expires items that are more recent than the oldest_live setting. */
void
do_item_flush_expired
(
void
)
{
int
i
;
item
*
iter
,
*
next
;
if
(
settings
.
oldest_live
==
0
)
return
;
for
(
i
=
0
;
i
<
LARGEST_ID
;
i
++)
{
for
(
iter
=
heads
[
i
];
iter
!=
NULL
;
iter
=
next
)
{
/* iter->time of 0 are magic objects. */
if
(
iter
->
time
!=
0
&&
iter
->
time
>=
settings
.
oldest_live
)
{
next
=
iter
->
next
;
if
((
iter
->
it_flags
&
ITEM_SLABBED
)
==
0
)
{
do_item_unlink_nolock
(
iter
,
hash
(
ITEM_key
(
iter
),
iter
->
nkey
));
}
}
else
{
/* We‘ve hit the first old item. Continue to the next queue. */
break
;
}
}
}
}
static
void
crawler_link_q
(
item
*
it
)
{
/* item is the new tail */
item
**
head
,
**
tail
;
assert
(
it
->
slabs_clsid
<
LARGEST_ID
);
assert
(
it
->
it_flags
==
1
);
assert
(
it
->
nbytes
==
0
);
head
=
&
heads
[
it
->
slabs_clsid
];
tail
=
&
tails
[
it
->
slabs_clsid
];
assert
(*
tail
!=
0
);
assert
(
it
!=
*
tail
);
assert
((*
head
&&
*
tail
)
||
(*
head
==
0
&&
*
tail
==
0
));
it
->
prev
=
*
tail
;
it
->
next
=
0
;
if
(
it
->
prev
)
{
assert
(
it
->
prev
->
next
==
0
);
it
->
prev
->
next
=
it
;
}
*
tail
=
it
;
if
(*
head
==
0
)
*
head
=
it
;
return
;
}
static
void
crawler_unlink_q
(
item
*
it
)
{
item
**
head
,
**
tail
;
assert
(
it
->
slabs_clsid
<
LARGEST_ID
);
head
=
&
heads
[
it
->
slabs_clsid
];
tail
=
&
tails
[
it
->
slabs_clsid
];
if
(*
head
==
it
)
{
assert
(
it
->
prev
==
0
);
*
head
=
it
->
next
;
}
if
(*
tail
==
it
)
{
assert
(
it
->
next
==
0
);
*
tail
=
it
->
prev
;
}
assert
(
it
->
next
!=
it
);
assert
(
it
->
prev
!=
it
);
if
(
it
->
next
)
it
->
next
->
prev
=
it
->
prev
;
if
(
it
->
prev
)
it
->
prev
->
next
=
it
->
next
;
return
;
}
static
item
*
crawler_crawl_q
(
item
*
it
)
{
item
**
head
,
**
tail
;
assert
(
it
->
it_flags
==
1
);
assert
(
it
->
nbytes
==
0
);
assert
(
it
->
slabs_clsid
<
LARGEST_ID
);
head
=
&
heads
[
it
->
slabs_clsid
];
tail
=
&
tails
[
it
->
slabs_clsid
];
/* We‘ve hit the head, pop off */
if
(
it
->
prev
==
0
)
{
assert
(*
head
==
it
);
if
(
it
->
next
)
{
*
head
=
it
->
next
;
assert
(
it
->
next
->
prev
==
it
);
it
->
next
->
prev
=
0
;
}
return
NULL
;
/* Done */
}
assert
(
it
->
prev
!=
it
);
if
(
it
->
prev
)
{
if
(*
head
==
it
->
prev
)
{
*
head
=
it
;
}
if
(*
tail
==
it
)
{
*
tail
=
it
->
prev
;
}
assert
(
it
->
next
!=
it
);
if
(
it
->
next
)
{
assert
(
it
->
prev
->
next
==
it
);
it
->
prev
->
next
=
it
->
next
;
it
->
next
->
prev
=
it
->
prev
;
}
else
{
it
->
prev
->
next
=
0
;
}
it
->
next
=
it
->
prev
;
it
->
prev
=
it
->
next
->
prev
;
it
->
next
->
prev
=
it
;
if
(
it
->
prev
)
{
it
->
prev
->
next
=
it
;
}
}
assert
(
it
->
next
!=
it
);
assert
(
it
->
prev
!=
it
);
return
it
->
next
;
/* success */
}
/* I pulled this out to make the main thread clearer, but it reaches into the
* main thread‘s values too much. Should rethink again.
上面这句注释作者是说,他把用爬虫处理过期的item的工作放到另一个专门的线程里去做
是为了让主线程干净一点,但是这线程的工作涉及到太多主线程的东西了,得重新想想..
这个函数的作用是“评估”一下这个item是否应该free掉。其实主要就是看下有没有过期啦~
当然用户设置的settings.oldest_live参数也加入到考虑中
*/
static
void
item_crawler_evaluate
(
item
*
search
,
uint32_t
hv
,
int
i
)
{
rel_time_t
oldest_live
=
settings
.
oldest_live
;
if
((
search
->
exptime
!=
0
&&
search
->
exptime
<
current_time
)
||
(
search
->
time
<=
oldest_live
&&
oldest_live
<=
current_time
))
{
itemstats
[
i
].
crawler_reclaimed
++;
if
(
settings
.
verbose
>
1
)
{
int
ii
;
char
*
key
=
ITEM_key
(
search
);
fprintf
(
stderr
,
"LRU crawler found an expired item (flags: %d, slab: %d): "
,
search
->
it_flags
,
search
->
slabs_clsid
);
for
(
ii
=
0
;
ii
<
search
->
nkey
;
++
ii
)
{
fprintf
(
stderr
,
"%c"
,
key
[
ii
]);
}
fprintf
(
stderr
,
"\n"
);
}
if
((
search
->
it_flags
&
ITEM_FETCHED
)
==
0
)
{
itemstats
[
i
].
expired_unfetched
++;
}
do_item_unlink_nolock
(
search
,
hv
);
do_item_remove
(
search
);
assert
(
search
->
slabs_clsid
==
0
);
}
else
{
refcount_decr
(&
search
->
refcount
);
}
}
/**
item爬虫线程入口,负责从lru链表中把过期的item free掉
*/
static
void
*
item_crawler_thread
(
void
*
arg
)
{
int
i
;
pthread_mutex_lock
(&
lru_crawler_lock
);
if
(
settings
.
verbose
>
2
)
fprintf
(
stderr
,
"Starting LRU crawler background thread\n"
);
while
(
do_run_lru_crawler_thread
)
{
pthread_cond_wait
(&
lru_crawler_cond
,
&
lru_crawler_lock
);
while
(
crawler_count
)
{
item
*
search
=
NULL
;
void
*
hold_lock
=
NULL
;
for
(
i
=
0
;
i
<
LARGEST_ID
;
i
++)
{
if
(
crawlers
[
i
].
it_flags
!=
1
)
{
continue
;
}
pthread_mutex_lock
(&
cache_lock
);
search
=
crawler_crawl_q
((
item
*)&
crawlers
[
i
]);
if
(
search
==
NULL
||
(
crawlers
[
i
].
remaining
&&
--
crawlers
[
i
].
remaining
<
1
))
{
if
(
settings
.
verbose
>
2
)
fprintf
(
stderr
,
"Nothing left to crawl for %d\n"
,
i
);
crawlers
[
i
].
it_flags
=
0
;
crawler_count
--;
crawler_unlink_q
((
item
*)&
crawlers
[
i
]);
pthread_mutex_unlock
(&
cache_lock
);
continue
;
}
uint32_t
hv
=
hash
(
ITEM_key
(
search
),
search
->
nkey
);
/* Attempt to hash item lock the "search" item. If locked, no
* other callers can incr the refcount
*/
if
((
hold_lock
=
item_trylock
(
hv
))
==
NULL
)
{
pthread_mutex_unlock
(&
cache_lock
);
continue
;
}
/* Now see if the item is refcount locked */
if
(
refcount_incr
(&
search
->
refcount
)
!=
2
)
{
refcount_decr
(&
search
->
refcount
);
if
(
hold_lock
)
item_trylock_unlock
(
hold_lock
);
pthread_mutex_unlock
(&
cache_lock
);
continue
;
}
item_crawler_evaluate
(
search
,
hv
,
i
);
if
(
hold_lock
)
item_trylock_unlock
(
hold_lock
);
pthread_mutex_unlock
(&
cache_lock
);
if
(
settings
.
lru_crawler_sleep
)
usleep
(
settings
.
lru_crawler_sleep
);
}
}
if
(
settings
.
verbose
>
2
)
fprintf
(
stderr
,
"LRU crawler thread sleeping\n"
);
STATS_LOCK
();
stats
.
lru_crawler_running
=
false
;
STATS_UNLOCK
();
}
pthread_mutex_unlock
(&
lru_crawler_lock
);
if
(
settings
.
verbose
>
2
)
fprintf
(
stderr
,
"LRU crawler thread stopping\n"
);
return
NULL
;
}
static
pthread_t
item_crawler_tid
;
//停止item爬虫线程
int
stop_item_crawler_thread
(
void
)
{
int
ret
;
pthread_mutex_lock
(&
lru_crawler_lock
);
do_run_lru_crawler_thread
=
0
;
pthread_cond_signal
(&
lru_crawler_cond
);
pthread_mutex_unlock
(&
lru_crawler_lock
);
if
((
ret
=
pthread_join
(
item_crawler_tid
,
NULL
))
!=
0
)
{
fprintf
(
stderr
,
"Failed to stop LRU crawler thread: %s\n"
,
strerror
(
ret
));
return
-
1
;
}
settings
.
lru_crawler
=
false
;
return
0
;
}
/**
启动item 爬虫线程
*/
int
start_item_crawler_thread
(
void
)
{
int
ret
;
if
(
settings
.
lru_crawler
)
return
-
1
;
pthread_mutex_lock
(&
lru_crawler_lock
);
do_run_lru_crawler_thread
=
1
;
settings
.
lru_crawler
=
true
;
if
((
ret
=
pthread_create
(&
item_crawler_tid
,
NULL
,
item_crawler_thread
,
NULL
))
!=
0
)
{
fprintf
(
stderr
,
"Can‘t create LRU crawler thread: %s\n"
,
strerror
(
ret
));
pthread_mutex_unlock
(&
lru_crawler_lock
);
return
-
1
;
}
pthread_mutex_unlock
(&
lru_crawler_lock
);
return
0
;
}
enum
crawler_result_type lru_crawler_crawl
(
char
*
slabs
)
{
char
*
b
=
NULL
;
uint32_t
sid
=
0
;
uint8_t
tocrawl
[
POWER_LARGEST
];
if
(
pthread_mutex_trylock
(&
lru_crawler_lock
)
!=
0
)
{
return
CRAWLER_RUNNING
;
}
pthread_mutex_lock
(&
cache_lock
);
if
(
strcmp
(
slabs
,
"all"
)
==
0
)
{
for
(
sid
=
0
;
sid
<
LARGEST_ID
;
sid
++)
{
tocrawl
[
sid
]
=
1
;
}
}
else
{
for
(
char
*
p
=
strtok_r
(
slabs
,
","
,
&
b
);
p
!=
NULL
;
p
=
strtok_r
(
NULL
,
","
,
&
b
))
{
if
(!
safe_strtoul
(
p
,
&
sid
)
||
sid
<
POWER_SMALLEST
||
sid
>
POWER_LARGEST
)
{
pthread_mutex_unlock
(&
cache_lock
);
pthread_mutex_unlock
(&
lru_crawler_lock
);
return
CRAWLER_BADCLASS
;
}
tocrawl
[
sid
]
=
1
;
}
}
for
(
sid
=
0
;
sid
<
LARGEST_ID
;
sid
++)
{
if
(
tocrawl
[
sid
]
!=
0
&&
tails
[
sid
]
!=
NULL
)
{
if
(
settings
.
verbose
>
2
)
fprintf
(
stderr
,
"Kicking LRU crawler off for slab %d\n"
,
sid
);
crawlers
[
sid
].
nbytes
=
0
;
crawlers
[
sid
].
nkey
=
0
;
crawlers
[
sid
].
it_flags
=
1
;
/* For a crawler, this means enabled. */
crawlers
[
sid
].
next
=
0
;
crawlers
[
sid
].
prev
=
0
;
crawlers
[
sid
].
time
=
0
;
crawlers
[
sid
].
remaining
=
settings
.
lru_crawler_tocrawl
;
crawlers
[
sid
].
slabs_clsid
=
sid
;
crawler_link_q
((
item
*)&
crawlers
[
sid
]);
crawler_count
++;
}
}
pthread_mutex_unlock
(&
cache_lock
);
pthread_cond_signal
(&
lru_crawler_cond
);
STATS_LOCK
();
stats
.
lru_crawler_running
=
true
;
STATS_UNLOCK
();
pthread_mutex_unlock
(&
lru_crawler_lock
);
return
CRAWLER_OK
;
}
//初始化lru item爬虫线程
int
init_lru_crawler
(
void
)
{
if
(
lru_crawler_initialized
==
0
)
{
if
(
pthread_cond_init
(&
lru_crawler_cond
,
NULL
)
!=
0
)
{
fprintf
(
stderr
,
"Can‘t initialize lru crawler condition\n"
);
return
-
1
;
}
pthread_mutex_init
(&
lru_crawler_lock
,
NULL
);
lru_crawler_initialized
=
1
;
}
return
0
;
}
Memcached源码分析之items.c
标签:
原文地址:http://www.cnblogs.com/guolanzhu/p/5850256.html
踩
(
0
)
赞
(
0
)
举报
评论
一句话评论(
0
)
登录后才能评论!
分享档案
更多>
2021年07月29日 (22)
2021年07月28日 (40)
2021年07月27日 (32)
2021年07月26日 (79)
2021年07月23日 (29)
2021年07月22日 (30)
2021年07月21日 (42)
2021年07月20日 (16)
2021年07月19日 (90)
2021年07月16日 (35)
周排行
更多
gitlab 在linux安装环境下存储地址
2021-07-29
当 Mac 未检测到外部显示器时如何修复它
2021-07-29
Ubuntu18.04安装qemu遇到问题-qemu : Depends: qemu-system (>= 1:2.11+dfsg-1ubuntu7)
2021-07-28
[Linux]Shell编程【待续】
2021-07-28
Linux系统资源查看
2021-07-27
Archlinux爬坑指南
2021-07-27
[Linux]Linux发展历程
2021-07-27
非桌面系统 (ubuntu)安装google-chrome
2021-07-27
在Ubuntu18.04系统中源码安装 gcc7.3.0
2021-07-23
Linux快捷键杂记
2021-07-22
友情链接
兰亭集智
国之画
百度统计
站长统计
阿里云
chrome插件
新版天听网
关于我们
-
联系我们
-
留言反馈
© 2014
mamicode.com
版权所有 联系我们:gaon5@hotmail.com
迷上了代码!