弱弱的求个memcached的官方最新英文文档,编译不出伤不起。

qq120848369 2011-08-10 08:04:44
如题,谁能帮帮忙,我的虚拟机上不了网,发现xml2rfc在make时需要联网的,很费解。。

谁能给个已经做成PDF或者TXT的原版文档,谢谢。。
...全文
148 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
cd2108006026 2012-02-09
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 coding_hello 的回复:]

求楼上的看2,3天,开发一套效率高的吧~~ 让我们恭维恭维!!
[/Quote]

不好意思呵呵 月新还不到两万,水平太烂
qq120848369 2011-08-15
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 babilife 的回复:]

楼主很强大,我只想说
[/Quote]

= =,被导师让看Linux3.0内存模块源码,看不懂所以只好先看memcached,有兴趣才有动力。
qq120848369 2011-08-15
  • 打赏
  • 举报
回复
再说个亮点:

assoc.c:
static void *assoc_maintenance_thread(void *arg) {

while (do_run_maintenance_thread) {
int ii = 0;

/* Lock the cache, and bulk move multiple buckets to the new
* hash table. */
pthread_mutex_lock(&cache_lock);

for (ii = 0; ii < hash_bulk_move && expanding; ++ii) {
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++;
if (expand_bucket == hashsize(hashpower - 1)) {
expanding = false;
free(old_hashtable);
if (settings.verbose > 1)
fprintf(stderr, "Hash table expansion done\n");
}
}

if (!expanding) {
/* We are done expanding.. just wait for next invocation */
pthread_cond_wait(&maintenance_cond, &cache_lock);
}

pthread_mutex_unlock(&cache_lock);
}
return NULL;
}


注意加锁部分,阅读assoc.c会发现函数都在没有锁的前提下操作共享变量,很郁闷.

其实加锁部分都出现在thread.c中,同样是用cache_lock锁住的,也就是说assoc.c中的所有函数除了上边这个
交换线程函数以外,调用时都是被锁住的,都是安全的。

亮就亮在交换线程的锁粒度上,它每次上锁后,只交换一个哈希桶,也许初看代码有点看不懂,但注意到:

hash_bulk_move 实际为1,也就是while循环内加锁,只执行一个桶的交换工作便释放锁,在下一次while中再次

加锁。hash_bulk_move是可以人为控制的,作用在于并发时,哈希表expand线程影响用户操作哈希表的时间比

例。

这里hash_bulk_move为1,也就是交换一个桶,就给用户占有锁操作哈希表的机会,细节很重要啊~


至善者善之敌 2011-08-15
  • 打赏
  • 举报
回复
楼主很强大,我只想说
qq120848369 2011-08-15
  • 打赏
  • 举报
回复
已经读完了...框架清晰了..

一个LRU,一个开放给用户的item get用于查询与懒惰删除,一个item alloc/free用于分配item 。

整个程序都是围绕LRU和slab的,特殊一点就是每个slabclass桶对应一个LRU链表,这样就方便的满足了不同size的item在对应的LRU中refcount为0的过期结点的正常回收(slab free)与内存紧张时对refcount为0结点的强制回收的要求。

主要就是assoc某块这个哈希表很恶心,读的时候发现怎么一个锁都没有,很费解。

后来脉络明确之后发现在memcached.c中,有一系列的item_xxxx函数,对do_item_xxx加锁封装了。

而关于哈希表的使用都发生在do_item_xxx中,比如do_item_get之类的,里边就会调用assoc_find之类的哈希

表函数。 所以锁的粒度是比较粗的,相当于锁住item_xxxx函数的同时完全锁住哈希函数体,而不是专门在

assoc.c中锁住临界值。

还有一个重要的体会就是item的refcount和LRU以及slab的重要关系。

LRU是维护了最近使用的item的链表,我没有详细跟踪item是何时插入到了LRU中以及何时update到队列头部,

但LRU的淘汰机制很明了。


首先,当一个item的refcount非0时,你不能将它slab free,因为有人在用它。 refcount是如何成为非0的

呢? 是由item_alloc首次获取一个item时赋值refcount=1,或者item_get增加某个item的refcount++。

item_get将会伴随着LRU的懒惰删除机制,因为memcached程序是一个缓存程序,说白了就是数据存进哈希中,

给它一个生命期,在生命期内你随便用这个数据(item),过了生命期的之后,如果你仍旧在引用这个item,

那也没关系,系统不会slab_free它(因为refcount!=0),它此刻仍旧在哈希表里,也仍旧在LRU里(如果

refcount==0,那么从LRU里回收(item_free-->>slab_free)这个item是多么美好的事情,意味着我们的slab又

有内存可用了)。 但如果此时你试图再次item_get这个item,正如上一行所说,你可以在哈希中找到它,但是

它已经超出生命期,理应从LRU中淘汰并且归还到slab池中。 关键又来了,从LRU与哈希中淘汰并不意味着这个

item就应该删除,LRU只是一个内存回收辅助机制,它会记录你用过的item,并且在item生命期过后帮助你slab

free以提供更多可用的内存。 我一开始很费解,LRU有什么用,依靠item_remove让用户自己来释放item,一直

到item的refcount为0之后由item_free-->slab_free不就释放掉了,检测LRU有什么用啊。

但是,加入LRU后,就体现出了生命期这个概念,memcached作为缓冲层,它不是持久对象的,没有在使用中的

对象如果超出生命期需要被淘汰,如果没有超出生命期而没有人引用,那也不应该淘汰它,因为可能还会有人

用。 这就是LRU存在的意义了,正因为LRU的存在,item的分配机制已经超出了slab的分配与回收,加入了LRU

链表的检测以配合SLAB的回收工作。

这个特性主要体现在item_alloc中,它首先检测对应size的LRU,对于refcount==0并且过期的结点进行item_unlink,因为refcount为0并且过期,那么如果此时用户调用Item_get试图获取此item的行为将会引起
懒惰删除,item_get同样会引起item_unlink从而导致item_free->slab_free的调用链,从而释放掉这个item的内存。 这与item_alloc中的检测行为与处理是一致的。

更重要的一点是,当item_get配合item_remove,这两个函数被用户使用时,只要生命期未到,那么即便refcount为0,也不会从LRU中删除(因为LRU维护的就是生命期内的item)。 如果在refcount为0,生命期
未到的前提下,从此没有其他人使用了。 那么这个item的内存将有谁回收? 答:由item_get或者item_alloc在
该item超出生命期之后回收(前提已经说了是refcount==0),这里的回收很明确是item_free-->slab_free的调用过程。

与上一段类似的一个情形如下:如果用户多次调用Item_get,使refcount非0,并且一直没有item_remove,那么在生命期超出之后,会发生什么? 答: 最起码要从LRU中删除它,因为它已经过期了! 那么这个删除动作由谁引起,对这个item又会影响什么? 答:删除动作由item_get引起(懒惰删除),它查询哈希发现该item存在,发现它超出生命期不应该再被用户获得,于是item_unlink从LRU中删除它,但并不调用Item_free-->slab_free,因为它的refcount!=0,仍旧被人使用中。 从LRU中删除是理所应当的,因为LRU保存生命期内的结点,应该想尽一切办法从中去掉那些已经过期的,于是就有了懒惰删除这种机制来顺带的帮一下忙。
现在LRU中去掉了该item的记录,但并没有归还其内存,直到用户调用item_remove一直到refcount为0,item_remove->item_free->slab_free的调用链便会发生,内存还是得到了归还。

总结:内存的实际归还(区分LRU的unlink操作,这完全是两码事)大致分为2种:

1,由item_remove在item已不在LRU并且refcount减为0的情况(item不在LRU的情况:由item_get懒惰item_unlink引起,绝对不会由item_alloc引发,因为item_alloc引发unlink的前提是refcount为0)下由item_free释放内存。

2,在refcount为0并且过期的前提下,由item_alloc第一次LRU扫描unlink->item_free->slab_free或者由item_get在item过期时懒惰Unlink->item->free 或者 由item_alloc第二次LRU扫描时对refcount==0且未过期的或者永久不过期的结点强制unlink。



说的比较乱。。。

1,item_alloc用于分配一个item,并初始化refcount=1,它首先检测LRU中refcount为0的,并且过期的结点,找到的话则检测refcount,如果refcount为0,则令refcount=1, 接着调用item_unlink从LRU中删去该item(伴随着hash的删除,注意由于refcount=1,所以item_free不会被item_unlink调用,这就免去了item_free->slab_free,再到slab_alloc的复杂调用),可以直接从LRU中归还item给用户使用。

除了上边的第一次LRU扫描,后边还有内存紧张时第二次LRU扫描的强制unlink,不过前提仍旧是refcount=0,生命期内的item此刻也能得到释放(早点释放无所谓,因为memcached本来就是个缓存系统,用户get不到自然就灰溜溜的去DB里找数据了,无所谓~)。 不过这次释放没有refcount=1的赋值,所以item_unlink实际是最终调用了slab_free归还了内存。 紧接着尝试了slab_alloc来获取内存,从代码你可以清晰的看到。

2,item_get用于获取一个尚在cache中的item,也许它已经过期了,那么在过期后首次get仍旧可以在哈希与LRU中得到该item,但是之后的过期检测会执行item_unlink, 从LRU与哈希中移除它,意味着该item不应再被使用,即将消逝于memcached中。 但是否真的可以slab_free掉取决于refcount,如果之前调用过item_get,那么必然refcount会累加而非0,那么item_unlink就不会slab_free它了,实际的free动作留到了item_remove中。
如果refcount为0,那么item_unlink就会调用到free,实际归还了内存。 这是懒惰删除的机制,是围绕LRU与item的生命期做文章的:如果过期,请离开LRU,并且最好离开cache,至于能不能离开cache,那得看看还有没有人在使用它!

3,item_remove,看过上边一段之后,我们就知道remove干嘛了。 它减少item的refcount,并且在减为0时,根据item是否仍旧在LRU中决定是否实际free掉该内存。 因为如果item仍旧在LRU中,可以从理论上说明该item尚未过期,即便现在refcount为0没有人使用该item,我们也不应该free它,应该保留在LRU中,也许由item_get查询使用该item。 如果我们remove时,refcount为0,如果此时它不在LRU中,说明已经过期并且没人使用,那么free内存,这就是上边一段所提及的情形了。

利用上边3个函数,每个函数都谨慎对待了item是否可以free这个点,以及LRU维护生命期内item这个概念。
过期就要离开LRU,离开cache,但能不能离开cache还要看有没有人在用这个item。 unlink可以让item离开LRU,意味着它已经实际过期(与理论时间过期有差别),但unlink是否可以free由refcount决定。

这几个因素相互作用,实现了LRU。

至于网络部分是很简单的,也就是一个work thread epoll 若干socket,并且epoll一个管道用于获取新conn到来的通知。 每个thread对应一个新conn队列,一个通知管道,虽然用的是libevent,但是不会影响你对网络部分的框架把握,典型的round-robin你就将会看到,管道通知与new connection加入epoll事件你也会看到,一个thread对应一个thread结构体你也会看到,这一块是很简单的。 读一下thread.c文件即可。

assoc.c是哈希部分,其哈希函数对应文件hash.c,这是96年的算法,不必看,因为这只是一个哈希函数,貌似是个二进制哈希函数,很强力~

再就是看一下cache.c,这是一个对象内存分配器和构造器,和C++很像。

damon是守护进程生成函数,全国统一模样,也不必担心。

items.c就是Item_xxx函数了,LRU的实现就在其中,当然离不开slab,所以slab.c必须先看。

slab.c是内存池,它的设计逻辑挺诡异的,也是想了一会才看明白的。

slabclass_t就是一个桶,一个内存池是由很多桶做成的:
static slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES];

每个桶里:
typedef struct {
unsigned int size; /* sizes of items */
unsigned int perslab; /* how many items per slab */

void **slots; /* list of item ptrs */
unsigned int sl_total; /* size of previous array */
unsigned int sl_curr; /* first free slot */

void *end_page_ptr; /* pointer to next free item at end of page, or 0 */
unsigned int end_page_free; /* number of items remaining at end of last alloced page */

unsigned int slabs; /* how many slabs were allocated for this class */

void **slab_list; /* array of slab pointers */
unsigned int list_size; /* size of prev array */

unsigned int killing; /* index+1 of dying slab, or zero if none */
size_t requested; /* The number of requested bytes */
} slabclass_t;


end_page_ptr就是malloc来的一大段内存,它的使用前提是slots这个数组里没有被回收来的内存块,那么只好动用end_page_ptr来取一段内存给用户了。

slots就是回收来的内存块了,初始化是空的,只有用户归还内存时才会放到slots里,当然分配时也是优先从slots里取。

void **slab_list是你曾经memory_alloc过的大块内存,它们的首地址都被存进这个数组里做个记录,实际有没有用途取决于是否开启了预先分配内存机制。

每个slabclass里的每个slab大小是有限制的(对应于slab_list中每一个元素),也就是不能超过settings.item_size_max,从slabs_init里你可以清晰的看到,最后一个slabclass里特意指定
一个chunk的size为settings.item_size_max,一个slab只能装的下一个chunk.

而其他桶中,每个chunk的size由factor逐渐累加并且8字节对齐,每个slab最多装settings.item_size_max/size个chunk,这个数取整后就是perslab,也就是每个slab可以分配的chunk个数。
那么perslab*size也就是实际需要分配的slab的size,这个数近似于settings.item_size_max,但由于取整的原因,就有了偏差。

slab的设计整体是这样的:有一个全局大内存池,你可以选择使用预先分配策略,那么它会被开辟。

每个slabclass又是一个小内存池管理器,它内部有一个end_page_ptr用于管理小内存池,小内存池是从

memory_alloc从全局大内存池割过来的(预先分配机制打开),或者是直接malloc(预先分配没打开)。

每个slabclass就是一个桶,不同的桶里装有不一样size的chunk,分配内存首先根据size定位到桶,然后检测

slots是否有被归还的chunk,没有的话则从end_page_ptr割一块给用户,如果连end_page_ptr都用完了(end_page_ptr的大小是perslab*size,前面已经说过了),那么就do_slabs_newslab调用memory_alloc分配个新的slab,放进该桶的slab_list里记录起来,并且赋给end_page_ptr,那么就今后又可以使用end_page_ptr割内存给用户了。

这里就应该明白为什么要记录slab_list了,因为如果没有打开预先分配机制,memeroy_alloc每次调用不是从全局内存池拿内存而且malloc内存,所以记录起来,今后就可以释放啊~


这是周末阅读memcached的心得体会,如果对某些童鞋有帮助倍感荣幸。
toadzw 2011-08-11
  • 打赏
  • 举报
回复
ding .......................
野男孩 2011-08-11
  • 打赏
  • 举报
回复
求楼上的看2,3天,开发一套效率高的吧~~ 让我们恭维恭维!!
cd2108006026 2011-08-11
  • 打赏
  • 举报
回复
很不喜欢这个代码。它还依赖了LIBEVENT。。。够雷人的

楼主可以花两三天看懂这个代码,然后根据自己的需求来改动重写。

BTW,原始的MEMCACHED代码效率确实有点不恭维
Proteas 2011-08-11
  • 打赏
  • 举报
回复
缺什么,就让信息中心给你下载。
一叶之舟 2011-08-11
  • 打赏
  • 举报
回复
memcached.org上你所需要资料都有,最好自己去下一个
qq120848369 2011-08-11
  • 打赏
  • 举报
回复
是XML什么的..公司开发机LINUX无法联外网,木法make xml啊。
Jxiaoshen 2011-08-11
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 jixingzhong 的回复:]
这里有没有你要的东东?

http://memcached.org/
[/Quote]
顶五星~~
pathuang68 2011-08-11
  • 打赏
  • 举报
回复
memcached是个很有意思的东东。

文档memcached上面有,但不是楼主要的PDF货txt...

帮顶了。

碰到这样的情况,俺就用两台机器,一台机器看文档,一台干活。
至善者善之敌 2011-08-11
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 zhoutanliang 的回复:]
http://memcached.org/
难道这没有嘛
[/Quote]

+++1
一叶之舟 2011-08-11
  • 打赏
  • 举报
回复
到memcached.org去下载吧
AlanBruce 2011-08-10
  • 打赏
  • 举报
回复
http://memcached.org/
难道这没有嘛
AlanBruce 2011-08-10
  • 打赏
  • 举报
回复
Mark!这么多的分!
peng_weida 2011-08-10
  • 打赏
  • 举报
回复
我是来打酱油的
c_losed 2011-08-10
  • 打赏
  • 举报
回复
下来瞅了下 不会玩
luciferisnotsatan 2011-08-10
  • 打赏
  • 举报
回复
帮顶。
加载更多回复(1)

64,649

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧