Memcached源码分析之assoc.c
#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 <assert.h>#include <pthread.h>static pthread_cond_t maintenance_cond = PTHREAD_COND_INITIALIZER;typedef unsigned long int ub4; /* unsigned 4-byte quantities */typedef unsigned char ub1; /* unsigned 1-byte quantities */unsigned int hashpower = HASHPOWER_DEFAULT;#define hashsize(n) ((ub4)1<<(n)) //2^n 次方 默认n是16(上面hashpower)#define hashmask(n) (hashsize(n)-1) //0x1111 1111 1111 1111static item** primary_hashtable = 0; //主hash表,注意理解,这个表只是存指针,没有存itemstatic item** old_hashtable = 0; //旧的hash表,在扩展hash表的时候用到static unsigned int hash_items = 0; //hash表总数static bool expanding = false; //是不是正在扩展hash表中(通过线程assoc_maintenance_thread)static bool started_expanding = false; /* 在扩展时,是以桶为粒度进行的,这是告诉我们扩张到哪个桶了。3 从0 到 hashsize(hashpower - 1) - 1 */static unsigned int expand_bucket = 0;void assoc_init(const int hashtable_init) { if (hashtable_init) { hashpower = hashtable_init; } primary_hashtable = calloc(hashsize(hashpower), sizeof(void *)); if (! primary_hashtable) { fprintf(stderr, "Failed to init hashtable.\n"); exit(EXIT_FAILURE); } STATS_LOCK(); stats.hash_power_level = hashpower; stats.hash_bytes = hashsize(hashpower) * sizeof(void *); STATS_UNLOCK();}/**通过key查找item*/item *assoc_find(const char *key, const size_t nkey, const uint32_t hv) { item *it; unsigned int oldbucket; //hv & hashmask(hashpower)得到的是桶在hash表中的下标 if (expanding && (oldbucket = (hv & hashmask(hashpower - 1))) >= expand_bucket) { it = old_hashtable[oldbucket]; } else { it = primary_hashtable[hv & hashmask(hashpower)]; //找出item所在的桶链表的首item } item *ret = NULL; int depth = 0; //遍历相同桶的链表,直到指定的key名为止。 while (it) { //这里为什么要先判断长度?&&的执行过程是先判断左边,如果不为true,那右边的条件也不用判断了, //所以个人认为是为了判断memcmp(key, ITEM_key(it), nkey)的调用 if ((nkey == it->nkey) && (memcmp(key, ITEM_key(it), nkey) == 0)) { ret = it; break; } it = it->h_next; ++depth; } MEMCACHED_ASSOC_FIND(key, nkey, depth); return ret;}/**这里的查找过程和上面的assoc_find 基本一致,不同的地方在于:这里返回的是指向 “指向当前item的指针”,并且引用当前item的上一个item的h_next,所以这里返回的就是当前item在桶链表中的前一个item的h_next,这也是为什么命名叫_hashitem_before的原因~*/static item** _hashitem_before (const char *key, const size_t nkey, const uint32_t hv) { item **pos; unsigned int oldbucket; if (expanding && (oldbucket = (hv & hashmask(hashpower - 1))) >= expand_bucket) { pos = &old_hashtable[oldbucket]; } else { pos = &primary_hashtable[hv & hashmask(hashpower)]; } while (*pos && ((nkey != (*pos)->nkey) || memcmp(key, ITEM_key(*pos), nkey))) { pos = &(*pos)->h_next; } return pos;}/**扩展哈希表*/static void assoc_expand(void) { 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(); } else { primary_hashtable = old_hashtable; /* Bad news, but we can keep running. */ }}/**主要是唤醒哈希表维护线程,执行哈希表扩展工作。*/static void assoc_start_expand(void) { if (started_expanding) return; started_expanding = true; /** 发送一个信号给正在处于阻塞等待状态的哈希表维护线程。见assoc_maintenance_thread */ pthread_cond_signal(&maintenance_cond);}/* Note: this isn't an assoc_update. The key must not already exist to call this *//**把item插入到hash表*/int assoc_insert(item *it, const uint32_t hv) { unsigned int oldbucket;// assert(assoc_find(ITEM_key(it), it->nkey) == 0); /* shouldn't have duplicately named things defined */ //hv & hashmask(hashpower)得到的是桶在hash表中的下标 if (expanding && (oldbucket = (hv & hashmask(hashpower - 1))) >= expand_bucket) { it->h_next = old_hashtable[oldbucket]; old_hashtable[oldbucket] = it; } else { //哈希表难免会冲突,这里用链表保存相同桶下标的item //这里是把新的item放到桶的链表头 it->h_next = primary_hashtable[hv & hashmask(hashpower)]; primary_hashtable[hv & hashmask(hashpower)] = it; } hash_items++; if (! expanding && hash_items > (hashsize(hashpower) * 3) / 2) { //当哈希表中的item数大于哈希表桶数的1.5倍时,开始扩展哈希表 assoc_start_expand(); } MEMCACHED_ASSOC_INSERT(ITEM_key(it), it->nkey, hash_items); return 1;}/**从哈希表中删除某个item*/void assoc_delete(const char *key, const size_t nkey, const uint32_t hv) { /** 调用_hashitem_before取到指向 指向当前item的上一个item的h_next指针 */ item **before = _hashitem_before(key, nkey, hv); //下面利用before指针,把当前item的h_next指向0,把上一个item的h_next指向原来before的h_next达到删除作用 if (*before) { item *nxt; hash_items--; /* The DTrace probe cannot be triggered as the last instruction * due to possible tail-optimization by the compiler */ MEMCACHED_ASSOC_DELETE(key, nkey, hash_items); nxt = (*before)->h_next; (*before)->h_next = 0; /* probably pointless, but whatever. */ *before = nxt; return; } /* Note: we never actually get here. the callers don't delete things they can't find. */ assert(*before != 0);}static volatile int do_run_maintenance_thread = 1;#define DEFAULT_HASH_BULK_MOVE 1int hash_bulk_move = DEFAULT_HASH_BULK_MOVE;/**哈希表维护线程工作时执行的函数*/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. */ item_lock_global(); 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) & 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); STATS_LOCK(); stats.hash_bytes -= hashsize(hashpower - 1) * sizeof(void *); stats.hash_is_expanding = 0; STATS_UNLOCK(); if (settings.verbose > 1) fprintf(stderr, "Hash table expansion done\n"); } } mutex_unlock(&cache_lock); item_unlock_global(); if (!expanding) { /* finished expanding. tell all threads to use fine-grained locks */ switch_item_lock_type(ITEM_LOCK_GRANULAR); slabs_rebalancer_resume(); /* We are done expanding.. just wait for next invocation */ mutex_lock(&cache_lock); started_expanding = false; pthread_cond_wait(&maintenance_cond, &cache_lock); //等待条件变量,当条件到达时唤醒线程往下执行 /* Before doing anything, tell threads to use a global lock */ mutex_unlock(&cache_lock); slabs_rebalancer_pause(); switch_item_lock_type(ITEM_LOCK_GLOBAL); mutex_lock(&cache_lock); assoc_expand(); mutex_unlock(&cache_lock); } } return NULL;}static pthread_t maintenance_tid;/**启动哈希表维护线程*/int start_assoc_maintenance_thread() { int ret; char *env = getenv("MEMCACHED_HASH_BULK_MOVE"); if (env != NULL) { hash_bulk_move = atoi(env); if (hash_bulk_move == 0) { hash_bulk_move = DEFAULT_HASH_BULK_MOVE; } } if ((ret = pthread_create(&maintenance_tid, NULL, assoc_maintenance_thread, NULL)) != 0) { //assoc_maintenance_thread为线程执行入口 fprintf(stderr, "Can't create thread: %s\n", strerror(ret)); return -1; } return 0;}/**停止哈希表维护线程,在memcached服务退出时执行,见memcached.c中main函数,event_base_loop之后*/void stop_assoc_maintenance_thread() { mutex_lock(&cache_lock); /** 发送信号assoc_maintenance_thread进入while循环相应的上下文, 而设置do_run_maintenance_thread = 0让线程在下次while(do_run_maintenance_thread)语句 中退出循环,线程退出。 */ do_run_maintenance_thread = 0; pthread_cond_signal(&maintenance_cond); mutex_unlock(&cache_lock); pthread_join(maintenance_tid, NULL); //等待线程退出 }Memcached源码分析之assoc.c的更多相关文章
- Memcached源码分析
作者:Calix,转载请注明出处:http://calixwu.com 最近研究了一下memcached的源码,在这里系统总结了一下笔记和理解,写了几 篇源码分析和大家分享,整个系列分为“结构篇”和“ ...
- Memcached源码分析之请求处理(状态机)
作者:Calix 一)上文 在上一篇线程模型的分析中,我们知道,worker线程和主线程都调用了同一个函数,conn_new进行事件监听,并返回conn结构体对象.最终有事件到达时,调用同一个函数ev ...
- Memcached源码分析之线程模型
作者:Calix 一)模型分析 memcached到底是如何处理我们的网络连接的? memcached通过epoll(使用libevent,下面具体再讲)实现异步的服务器,但仍然使用多线程,主要有两种 ...
- Memcached源码分析之从SET命令开始说起
作者:Calix 如果直接把memcached的源码从main函数开始说,恐怕会有点头大,所以这里以一句经典的“SET”命令简单地开个头,算是回忆一下memcached的作用,后面的结构篇中关于命令解 ...
- Memcached源码分析之内存管理
先再说明一下,我本次分析的memcached版本是1.4.20,有些旧的版本关于内存管理的机制和数据结构与1.4.20有一定的差异(本文中会提到). 一)模型分析在开始解剖memcached关于内存管 ...
- memcached源码分析-----item过期失效处理以及LRU爬虫
memcached源码分析-----item过期失效处理以及LRU爬虫,memcached-----item 转载请注明出处:http://blog.csdn.net/luotuo44/article ...
- Memcached源码分析——内存管理
注:这篇内容极其混乱 推荐学习这篇博客.博客的地址:http://kenby.iteye.com/blog/1423989 基本元素item item是Memcached中记录存储的基本单元,用户向m ...
- Memcached源码分析——process_command函数解析
以下为个人笔记 /** * process_command 在memcached中是用来处理用户发送的命令的, * 包括get set,add,delete,replace,stats,flush_a ...
- memcached源码分析一-slab
Slab作为一种内存管理方案,其作用主要有以下2点: a) 避免频繁的内存分配释放造成的内存碎片 b) 减少内存分配操作产生的性能开销 Linux内核数据结构中也有slab的设计,Linux提供了一套 ...
随机推荐
- 视频录制SurfaceView
package com.bw.videorecorder; import java.io.File;import java.io.IOException; import android.media.M ...
- Mesos架构简介
1. 前言 同其他大部分分布式系统一样,Apache Mesos为了简化设计,也是采用了master/slave结构,为了解决master单点故障,将master做得尽可能地轻量级,其上面所有的元数据 ...
- 使用nginx简单实现负载均衡
只是简单使用nginx玩玩而已,知道能这么用,但是在实际项目中并没有实践过,在项目不大的时候用不到,但是对于理解负载均衡来说还是可以的. 利用虚拟机安装了三个centOS系统,然后顺便装了环境. 这里 ...
- PAT1004
A family hierarchy is usually presented by a pedigree tree. 一个家族的层次结构经常用一个血缘树来呈现. Your job is to cou ...
- eclipse快捷键(转载)
Ctrl+1 快速修复(最经典的快捷键,就不用多说了)Ctrl+D: 删除当前行 Ctrl+Alt+↓ 复制当前行到下一行(复制增加)Ctrl+Alt+↑ 复制当前行到上一行(复制增加)Alt+↓ 当 ...
- js插入节点appendChild和insertBefore
首先 从定义来理解 这两个方法: appendChild() 方法:可向节点的子节点列表的末尾添加新的子节点.语法:appendChild(newchild) insertBefore() 方法:可在 ...
- 详细版在虚拟机安装和使用hadoop分布式集群
集群模式: 一台master 192.168.85.2 一台slave 192.168.85.3 jdk jdk1.8.0_74(版本不重要,看喜欢) hadoop版本 2.7.2(版本不重要,2. ...
- Android----ListView入门知识--各种Adapter配合使用
引自:http://www.cnblogs.com/playing/archive/2011/03/21/1990555.html 在android开发中ListView是比较常用的组件,它以列表的形 ...
- 转:总结Selenium WebDriver中一些鼠标和键盘事件的使用
在使用 Selenium WebDriver 做自动化测试的时候,会经常模拟鼠标和键盘的一些行为.比如使用鼠标单击.双击.右击.拖拽等动作:或者键盘输入.快捷键使用.组合键使用等模拟键盘的操作.在 W ...
- web 开发:CSS3 常用属性——速查手册!
web 开发:CSS3 常用属性——速查手册! CSS3 简介:http://www.runoob.com/css3/css3-intro.html 1.目录 http://caniuse.com/ ...