Memcached源码分析之items.c

  1. #include "memcached.h"
  2. #include <sys/stat.h>
  3. #include <sys/socket.h>
  4. #include <sys/signal.h>
  5. #include <sys/resource.h>
  6. #include <fcntl.h>
  7. #include <netinet/in.h>
  8. #include <errno.h>
  9. #include <stdlib.h>
  10. #include <stdio.h>
  11. #include <string.h>
  12. #include <time.h>
  13. #include <assert.h>
  14. #include <unistd.h>
  15. static void item_link_q(item *it);
  16. static void item_unlink_q(item *it);
  17. #define LARGEST_ID POWER_LARGEST
  18. typedef struct {
  19. uint64_t evicted;
  20. uint64_t evicted_nonzero;
  21. rel_time_t evicted_time;
  22. uint64_t reclaimed;
  23. uint64_t outofmemory;
  24. uint64_t tailrepairs;
  25. uint64_t expired_unfetched;
  26. uint64_t evicted_unfetched;
  27. uint64_t crawler_reclaimed;
  28. } itemstats_t;
  29. static item *heads[LARGEST_ID]; //各个slabclass的LRU队列头指针数组
  30. static item *tails[LARGEST_ID]; //各个slabclass的LRU队列尾指针数组
  31. static crawler crawlers[LARGEST_ID]; //各个slabclass的item爬虫数组
  32. static itemstats_t itemstats[LARGEST_ID]; //各个slabclass的item统计数组
  33. static unsigned int sizes[LARGEST_ID]; //各个slabclass的chunk大小数组
  34. static int crawler_count = 0;
  35. static volatile int do_run_lru_crawler_thread = 0;
  36. static int lru_crawler_initialized = 0;
  37. static pthread_mutex_t lru_crawler_lock = PTHREAD_MUTEX_INITIALIZER;
  38. static pthread_cond_t lru_crawler_cond = PTHREAD_COND_INITIALIZER;
  39. //重置统计
  40. void item_stats_reset(void) {
  41. mutex_lock(&cache_lock);
  42. memset(itemstats, 0, sizeof(itemstats));
  43. mutex_unlock(&cache_lock);
  44. }
  45. /* Get the next CAS id for a new item. */
  46. uint64_t get_cas_id(void) {
  47. static uint64_t cas_id = 0;
  48. return ++cas_id;
  49. }
  50. /* Enable this for reference-count debugging. */
  51. #if 0
  52. # define DEBUG_REFCNT(it,op) \
  53. fprintf(stderr, "item %x refcnt(%c) %d %c%c%c\n", \
  54. it, op, it->refcount, \
  55. (it->it_flags & ITEM_LINKED) ? ‘L‘ : ‘ ‘, \
  56. (it->it_flags & ITEM_SLABBED) ? ‘S‘ : ‘ ‘)
  57. #else
  58. # define DEBUG_REFCNT(it,op) while(0)
  59. #endif
  60. /**
  61. 算出item总大小
  62. */
  63. static size_t item_make_header(const uint8_t nkey, const int flags, const int nbytes,
  64. char *suffix, uint8_t *nsuffix) {
  65. /* suffix is defined at 40 chars elsewhere.. */
  66. *nsuffix = (uint8_t) snprintf(suffix, 40, " %d %d\r\n", flags, nbytes - 2);
  67. return sizeof(item) + nkey + *nsuffix + nbytes;
  68. }
  69. /**
  70. item分配
  71. 把这个函数弄清楚,基本就把memcached内存管理机制大体弄清楚了。
  72. */
  73. item *do_item_alloc(char *key, const size_t nkey, const int flags,
  74. const rel_time_t exptime, const int nbytes,
  75. const uint32_t cur_hv) {
  76. uint8_t nsuffix;
  77. item *it = NULL;
  78. char suffix[40];
  79. size_t ntotal = item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix); //item总大小
  80. if (settings.use_cas) {
  81. ntotal += sizeof(uint64_t); //如果有用到cas 那么item大小还要加上unit64_t的size
  82. }
  83. unsigned int id = slabs_clsid(ntotal); //根据item大小,找到适合的slabclass
  84. if (id == 0)
  85. return 0;
  86. mutex_lock(&cache_lock); //cache锁
  87. /* do a quick check if we have any expired items in the tail.. */
  88. /* 准备分配新的item了,随便快速瞄一下lru链表末尾有没有过期item,有的话就用过期的空间 */
  89. int tries = 5;
  90. int tried_alloc = 0;
  91. item *search;
  92. void *hold_lock = NULL;
  93. rel_time_t oldest_live = settings.oldest_live;
  94. search = tails[id]; //这个tails是一个全局变量,tails[xx]是id为xx的slabclass lru链表的尾部
  95. /* We walk up *only* for locked items. Never searching for expired.
  96. * Waste of CPU for almost all deployments */
  97. //从LRU链表尾部(就是最久没使用过的item)开始往前找
  98. for (; tries > 0 && search != NULL; tries--, search=search->prev) {
  99. if (search->nbytes == 0 && search->nkey == 0 && search->it_flags == 1) {
  100. /* We are a crawler, ignore it. */
  101. /*
  102. 这里注释意思是说我们现在是以爬虫的身份来爬出过期的空间,
  103. 像爬到这种很怪的item,就别管了,不是爬虫要做的事,不要就行了。
  104. */
  105. tries++;
  106. continue;
  107. }
  108. /**
  109. 你会看到很多地方有这个hv,简单说下,其实它是对item的一个hash,得到hv值,这个hv主要有两个
  110. 作用:
  111. 1)用于hash表保存item,通过hv计算出哈希表中的桶号
  112. 2)用于item lock表中锁住item,通过hv计算出应该用item lock表中哪个锁对当前item进行加锁
  113. 这两者都涉及到一个粒度问题,不可能保证每个不一样的key的hv不会相同,所有hash方法都可能
  114. 出现冲突。
  115. 所以hash表中用链表的方式处理冲突的item,而item lock表中会多个item共享一个锁,或者说
  116. 多个桶共享一个锁。
  117. */
  118. uint32_t hv = hash(ITEM_key(search), search->nkey);
  119. /**
  120. 尝试去锁住当前item。
  121. */
  122. if (hv == cur_hv || (hold_lock = item_trylock(hv)) == NULL)
  123. continue;
  124. /* Now see if the item is refcount locked */
  125. if (refcount_incr(&search->refcount) != 2) {
  126. refcount_decr(&search->refcount);
  127. /* Old rare bug could cause a refcount leak. We haven‘t seen
  128. * it in years, but we leave this code in to prevent failures
  129. * just in case
  130. 没看懂这里的意思.....
  131. */
  132. if (settings.tail_repair_time &&
  133. search->time + settings.tail_repair_time < current_time) {
  134. itemstats[id].tailrepairs++;
  135. search->refcount = 1;
  136. do_item_unlink_nolock(search, hv);
  137. }
  138. if (hold_lock)
  139. item_trylock_unlock(hold_lock);
  140. continue;
  141. }
  142. /* Expired or flushed */
  143. //超时了...
  144. if ((search->exptime != 0 && search->exptime < current_time)
  145. || (search->time <= oldest_live && oldest_live <= current_time)) {
  146. itemstats[id].reclaimed++;
  147. if ((search->it_flags & ITEM_FETCHED) == 0) {
  148. itemstats[id].expired_unfetched++;
  149. }
  150. it = search; //拿下空间
  151. slabs_adjust_mem_requested(it->slabs_clsid, ITEM_ntotal(it), ntotal); //更新统计数据
  152. /**
  153. 什么是link,在这简单说下,就是把item加到哈希表和LRU链表的过程。详见items::do_item_link函数这里把item旧的link取消掉,当前函数do_item_alloc的工作只是拿空间,而往后可知道拿到item空间后会对这块item进行“link”工作,而这里这块item空间是旧的item超时然后拿来用的,所以先把它unlink掉
  154. */
  155. do_item_unlink_nolock(it, hv);
  156. /* Initialize the item block: */
  157. it->slabs_clsid = 0;
  158. } else if ((it = slabs_alloc(ntotal, id)) == NULL) {/*如果没有找到超时的item,则
  159. 调用slabs_alloc分配空间,详见slabs_alloc
  160. 如果slabs_alloc分配空间失败,即返回NULL,则往下走,下面的代码是
  161. 把LRU列表最后一个给淘汰,即使item没有过期。
  162. 这里一般是可用内存已经满了,需要按LRU进行淘汰的时候。
  163. //************mark: $1**************
  164. */
  165. tried_alloc = 1; //标记一下,表示有进入此分支,表示有尝试过调用slabs_alloc去分配新的空间。
  166. //记下被淘汰item的信息,像我们使用memcached经常会查看的evicted_time就是在这里赋值啦!
  167. if (settings.evict_to_free == 0) {
  168. itemstats[id].outofmemory++;
  169. } else {
  170. itemstats[id].evicted++;
  171. itemstats[id].evicted_time = current_time - search->time; //被淘汰的item距离上次使用多长时间了
  172. if (search->exptime != 0)
  173. itemstats[id].evicted_nonzero++;
  174. if ((search->it_flags & ITEM_FETCHED) == 0) {
  175. itemstats[id].evicted_unfetched++;
  176. }
  177. it = search;
  178. slabs_adjust_mem_requested(it->slabs_clsid, ITEM_ntotal(it), ntotal);//更新统计数据
  179. do_item_unlink_nolock(it, hv); //从哈希表和LRU链表中删掉
  180. /* Initialize the item block: */
  181. it->slabs_clsid = 0;
  182. /*
  183. 如果当前slabclass有item被淘汰掉了,说明可用内存都满了,再也没有
  184. slab可分配了,
  185. 而如果 slab_automove=2 (默认是1),这样会导致angry模式,
  186. 就是只要分配失败了,就马上进行slab重分配:把别的slabclass空间牺牲
  187. 掉一些,马上给现在的slabclass分配空间,而不会合理地根据淘汰统计
  188. 数据来分析要怎么重分配(slab_automove = 1则会)。
  189. */
  190. if (settings.slab_automove == 2)
  191. slabs_reassign(-1, id);
  192. }
  193. }
  194. refcount_decr(&search->refcount);
  195. /* If hash values were equal, we don‘t grab a second lock */
  196. if (hold_lock)
  197. item_trylock_unlock(hold_lock);
  198. break;
  199. }
  200. /**
  201. 如果上面的for循环里面没有找到空间,并且没有进入过else if ((it = slabs_alloc(ntotal, id)) == NULL)这个分支没有
  202. 尝试调slabs_alloc分配空间(有这种可能性),那么,下面这行代码就是再尝试分配。
  203. 你会觉得上面那个循环写得特纠结,逻辑不清,估计你也看醉了。其实整个分配原则是这样子:
  204. 1)先从LRU链表找下看看有没有恰好过期的空间,有的话就用这个空间。
  205. 2)如果没有过期的空间,就分配新的空间。
  206. 3)如果分配新的空间失败,那么往往是内存都用光了,则从LRU链表中把最旧的即使没过期的item淘汰掉,空间分给新的item用。
  207. 问题是:这个从“LRU链表找到的item”是一个不确定的东西,有可能这个item数据异常,有可能这个item由于与别的item共用锁的桶号
  208. 这个桶被锁住了,所以总之各种原因这个item此刻不一定可用,因此用了一个循环尝试找几次(上面是5)。
  209. 所以逻辑是:
  210. 1)我先找5次LRU看看有没有可用的过期的item,有就用它。(for循环5次)
  211. 2)5次没有找到可用的过期的item,那我分配新的。
  212. 3)分配新的不成功,那我再找5次看看有没有可用的虽然没过期的item,淘汰它,把空间给新的item用。(for循环5次)
  213. 那么这里有个问题,如果代码要写得逻辑清晰一点,我得写两个for循环,一个是为了第2)步前“找可用的过期的”item,
  214. 一个是第2)步不成功后“找可用的用来淘汰的”空间。而且有重复的逻辑“找到可用的”,所以memcached作者就合在一起了,
  215. 然后只能把第2)步也塞到for循环里面,确实挺尴尬的。。。估计memcached作者也写得很纠结。。。
  216. 所以就很有可能出现5次都没找到可用的空间,都没进入过elseif那个分支就被continue掉了,为了记下有没有进过elseif
  217. 分支就挫挫地用一个tried_alloc变量来做记号。。
  218. */
  219. if (!tried_alloc && (tries == 0 || search == NULL))
  220. it = slabs_alloc(ntotal, id);
  221. if (it == NULL) {
  222. itemstats[id].outofmemory++;
  223. mutex_unlock(&cache_lock);
  224. return NULL; //没错!会有分配新空间不成功,而且尝试5次淘汰旧的item也没成功的时候,只能返回NULL。。
  225. }
  226. assert(it->slabs_clsid == 0);
  227. assert(it != heads[id]);
  228. //来到这里,说明item分配成功,下面主要是一些初始化工作。
  229. /* Item initialization can happen outside of the lock; the item‘s already
  230. * been removed from the slab LRU.
  231. */
  232. it->refcount = 1; /* the caller will have a reference */
  233. mutex_unlock(&cache_lock);
  234. it->next = it->prev = it->h_next = 0;
  235. it->slabs_clsid = id;
  236. DEBUG_REFCNT(it, ‘*‘);
  237. it->it_flags = settings.use_cas ? ITEM_CAS : 0;
  238. it->nkey = nkey;
  239. it->nbytes = nbytes;
  240. memcpy(ITEM_key(it), key, nkey);
  241. it->exptime = exptime;
  242. memcpy(ITEM_suffix(it), suffix, (size_t)nsuffix);
  243. it->nsuffix = nsuffix;
  244. return it;
  245. }
  246. /**
  247. 把这块item free掉,以供再利用,注意这里的free不是指把内存空间释放哦,
  248. 而是把这块item 变为“空闲”
  249. */
  250. void item_free(item *it) {
  251. size_t ntotal = ITEM_ntotal(it);
  252. unsigned int clsid;
  253. assert((it->it_flags & ITEM_LINKED) == 0);
  254. assert(it != heads[it->slabs_clsid]);
  255. assert(it != tails[it->slabs_clsid]);
  256. assert(it->refcount == 0);
  257. /* so slab size changer can tell later if item is already free or not */
  258. clsid = it->slabs_clsid;
  259. it->slabs_clsid = 0; //在这把free掉的item 的slabs_clsid设为0
  260. DEBUG_REFCNT(it, ‘F‘);
  261. slabs_free(it, ntotal, clsid);
  262. }
  263. /**
  264. * 检查item大小
  265. */
  266. bool item_size_ok(const size_t nkey, const int flags, const int nbytes) {
  267. char prefix[40];
  268. uint8_t nsuffix;
  269. size_t ntotal = item_make_header(nkey + 1, flags, nbytes,
  270. prefix, &nsuffix);
  271. if (settings.use_cas) {
  272. ntotal += sizeof(uint64_t);
  273. }
  274. return slabs_clsid(ntotal) != 0;
  275. }
  276. /**
  277. 把item插入相应的slabclass lru链表中而已
  278. */
  279. static void item_link_q(item *it) { /* item is the new head */
  280. item **head, **tail;
  281. assert(it->slabs_clsid < LARGEST_ID);
  282. assert((it->it_flags & ITEM_SLABBED) == 0);
  283. head = &heads[it->slabs_clsid];
  284. tail = &tails[it->slabs_clsid];
  285. assert(it != *head);
  286. assert((*head && *tail) || (*head == 0 && *tail == 0));
  287. it->prev = 0;
  288. it->next = *head;
  289. if (it->next) it->next->prev = it;
  290. *head = it;
  291. if (*tail == 0) *tail = it;
  292. sizes[it->slabs_clsid]++;
  293. return;
  294. }
  295. /**
  296. 把item从相应的slabclass lru链表中删掉而已,下面就是经典的删除链表逻辑代码了
  297. */
  298. static void item_unlink_q(item *it) {
  299. item **head, **tail;
  300. assert(it->slabs_clsid < LARGEST_ID);
  301. head = &heads[it->slabs_clsid];
  302. tail = &tails[it->slabs_clsid];
  303. if (*head == it) {
  304. assert(it->prev == 0);
  305. *head = it->next;
  306. }
  307. if (*tail == it) {
  308. assert(it->next == 0);
  309. *tail = it->prev;
  310. }
  311. assert(it->next != it);
  312. assert(it->prev != it);
  313. if (it->next) it->next->prev = it->prev;
  314. if (it->prev) it->prev->next = it->next;
  315. sizes[it->slabs_clsid]--;
  316. return;
  317. }
  318. /**
  319. 把item "link"起来,主要包括:
  320. 1)改变一些统计数据
  321. 2)把item加到哈希表
  322. 3)把item插入到相应的slabclass lru链表中
  323. */
  324. int do_item_link(item *it, const uint32_t hv) {
  325. MEMCACHED_ITEM_LINK(ITEM_key(it), it->nkey, it->nbytes);
  326. assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);
  327. mutex_lock(&cache_lock);
  328. it->it_flags |= ITEM_LINKED;
  329. it->time = current_time;
  330. STATS_LOCK();
  331. stats.curr_bytes += ITEM_ntotal(it);
  332. stats.curr_items += 1;
  333. stats.total_items += 1;
  334. STATS_UNLOCK();
  335. /* Allocate a new CAS ID on link. */
  336. ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);
  337. assoc_insert(it, hv); //插入哈希表
  338. item_link_q(it); //加入LRU链表
  339. refcount_incr(&it->refcount);
  340. mutex_unlock(&cache_lock);
  341. return 1;
  342. }
  343. /**
  344. 就是和do_item_link反过来的一些操作
  345. */
  346. void do_item_unlink(item *it, const uint32_t hv) {
  347. MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
  348. mutex_lock(&cache_lock);
  349. if ((it->it_flags & ITEM_LINKED) != 0) {
  350. it->it_flags &= ~ITEM_LINKED;
  351. STATS_LOCK();
  352. stats.curr_bytes -= ITEM_ntotal(it);
  353. stats.curr_items -= 1;
  354. STATS_UNLOCK();
  355. assoc_delete(ITEM_key(it), it->nkey, hv);
  356. item_unlink_q(it);
  357. do_item_remove(it);
  358. }
  359. mutex_unlock(&cache_lock);
  360. }
  361. /* FIXME: Is it necessary to keep this copy/pasted code? */
  362. void do_item_unlink_nolock(item *it, const uint32_t hv) {
  363. MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
  364. if ((it->it_flags & ITEM_LINKED) != 0) {
  365. it->it_flags &= ~ITEM_LINKED;
  366. STATS_LOCK();
  367. stats.curr_bytes -= ITEM_ntotal(it);
  368. stats.curr_items -= 1;
  369. STATS_UNLOCK();
  370. assoc_delete(ITEM_key(it), it->nkey, hv);
  371. item_unlink_q(it);
  372. do_item_remove(it);
  373. }
  374. }
  375. /**
  376. 指向item的指针不用的时候都会调用此函数
  377. */
  378. void do_item_remove(item *it) {
  379. MEMCACHED_ITEM_REMOVE(ITEM_key(it), it->nkey, it->nbytes);
  380. assert((it->it_flags & ITEM_SLABBED) == 0);
  381. assert(it->refcount > 0);
  382. if (refcount_decr(&it->refcount) == 0) { //引用计数减1,当引用计数为0时,才真正把item free掉。
  383. item_free(it);
  384. }
  385. }
  386. /**
  387. 主要作用是重置在最近使用链表中的位置,更新最近使用时间
  388. */
  389. void do_item_update(item *it) {
  390. MEMCACHED_ITEM_UPDATE(ITEM_key(it), it->nkey, it->nbytes);
  391. if (it->time < current_time - ITEM_UPDATE_INTERVAL) {
  392. assert((it->it_flags & ITEM_SLABBED) == 0);
  393. mutex_lock(&cache_lock);
  394. if ((it->it_flags & ITEM_LINKED) != 0) {
  395. item_unlink_q(it);
  396. it->time = current_time;
  397. item_link_q(it);
  398. }
  399. mutex_unlock(&cache_lock);
  400. }
  401. }
  402. int do_item_replace(item *it, item *new_it, const uint32_t hv) {
  403. MEMCACHED_ITEM_REPLACE(ITEM_key(it), it->nkey, it->nbytes,
  404. ITEM_key(new_it), new_it->nkey, new_it->nbytes);
  405. assert((it->it_flags & ITEM_SLABBED) == 0);
  406. do_item_unlink(it, hv);
  407. return do_item_link(new_it, hv);
  408. }
  409. void item_stats_evictions(uint64_t *evicted) {
  410. int i;
  411. mutex_lock(&cache_lock);
  412. for (i = 0; i < LARGEST_ID; i++) {
  413. evicted[i] = itemstats[i].evicted;
  414. }
  415. mutex_unlock(&cache_lock);
  416. }
  417. void do_item_stats_totals(ADD_STAT add_stats, void *c) {
  418. itemstats_t totals;
  419. memset(&totals, 0, sizeof(itemstats_t));
  420. int i;
  421. for (i = 0; i < LARGEST_ID; i++) {
  422. totals.expired_unfetched += itemstats[i].expired_unfetched;
  423. totals.evicted_unfetched += itemstats[i].evicted_unfetched;
  424. totals.evicted += itemstats[i].evicted;
  425. totals.reclaimed += itemstats[i].reclaimed;
  426. totals.crawler_reclaimed += itemstats[i].crawler_reclaimed;
  427. }
  428. APPEND_STAT("expired_unfetched", "%llu",
  429. (unsigned long long)totals.expired_unfetched);
  430. APPEND_STAT("evicted_unfetched", "%llu",
  431. (unsigned long long)totals.evicted_unfetched);
  432. APPEND_STAT("evictions", "%llu",
  433. (unsigned long long)totals.evicted);
  434. APPEND_STAT("reclaimed", "%llu",
  435. (unsigned long long)totals.reclaimed);
  436. APPEND_STAT("crawler_reclaimed", "%llu",
  437. (unsigned long long)totals.crawler_reclaimed);
  438. }
  439. void do_item_stats(ADD_STAT add_stats, void *c) {
  440. int i;
  441. for (i = 0; i < LARGEST_ID; i++) {
  442. if (tails[i] != NULL) {
  443. const char *fmt = "items:%d:%s";
  444. char key_str[STAT_KEY_LEN];
  445. char val_str[STAT_VAL_LEN];
  446. int klen = 0, vlen = 0;
  447. if (tails[i] == NULL) {
  448. /* We removed all of the items in this slab class */
  449. continue;
  450. }
  451. APPEND_NUM_FMT_STAT(fmt, i, "number", "%u", sizes[i]);
  452. APPEND_NUM_FMT_STAT(fmt, i, "age", "%u", current_time - tails[i]->time);
  453. APPEND_NUM_FMT_STAT(fmt, i, "evicted",
  454. "%llu", (unsigned long long)itemstats[i].evicted);
  455. APPEND_NUM_FMT_STAT(fmt, i, "evicted_nonzero",
  456. "%llu", (unsigned long long)itemstats[i].evicted_nonzero);
  457. APPEND_NUM_FMT_STAT(fmt, i, "evicted_time",
  458. "%u", itemstats[i].evicted_time);
  459. APPEND_NUM_FMT_STAT(fmt, i, "outofmemory",
  460. "%llu", (unsigned long long)itemstats[i].outofmemory);
  461. APPEND_NUM_FMT_STAT(fmt, i, "tailrepairs",
  462. "%llu", (unsigned long long)itemstats[i].tailrepairs);
  463. APPEND_NUM_FMT_STAT(fmt, i, "reclaimed",
  464. "%llu", (unsigned long long)itemstats[i].reclaimed);
  465. APPEND_NUM_FMT_STAT(fmt, i, "expired_unfetched",
  466. "%llu", (unsigned long long)itemstats[i].expired_unfetched);
  467. APPEND_NUM_FMT_STAT(fmt, i, "evicted_unfetched",
  468. "%llu", (unsigned long long)itemstats[i].evicted_unfetched);
  469. APPEND_NUM_FMT_STAT(fmt, i, "crawler_reclaimed",
  470. "%llu", (unsigned long long)itemstats[i].crawler_reclaimed);
  471. }
  472. }
  473. /* getting here means both ascii and binary terminators fit */
  474. add_stats(NULL, 0, NULL, 0, c);
  475. }
  476. void do_item_stats_sizes(ADD_STAT add_stats, void *c) {
  477. /* max 1MB object, divided into 32 bytes size buckets */
  478. const int num_buckets = 32768;
  479. unsigned int *histogram = calloc(num_buckets, sizeof(int));
  480. if (histogram != NULL) {
  481. int i;
  482. /* build the histogram */
  483. for (i = 0; i < LARGEST_ID; i++) {
  484. item *iter = heads[i];
  485. while (iter) {
  486. int ntotal = ITEM_ntotal(iter);
  487. int bucket = ntotal / 32;
  488. if ((ntotal % 32) != 0) bucket++;
  489. if (bucket < num_buckets) histogram[bucket]++;
  490. iter = iter->next;
  491. }
  492. }
  493. /* write the buffer */
  494. for (i = 0; i < num_buckets; i++) {
  495. if (histogram[i] != 0) {
  496. char key[8];
  497. snprintf(key, sizeof(key), "%d", i * 32);
  498. APPEND_STAT(key, "%u", histogram[i]);
  499. }
  500. }
  501. free(histogram);
  502. }
  503. add_stats(NULL, 0, NULL, 0, c);
  504. }
  505. //读取item数据
  506. item *do_item_get(const char *key, const size_t nkey, const uint32_t hv) {
  507. //mutex_lock(&cache_lock);
  508. item *it = assoc_find(key, nkey, hv);
  509. if (it != NULL) {
  510. refcount_incr(&it->refcount);
  511. if (slab_rebalance_signal &&
  512. ((void *)it >= slab_rebal.slab_start && (void *)it < slab_rebal.slab_end)) {
  513. do_item_unlink_nolock(it, hv);
  514. do_item_remove(it);
  515. it = NULL;
  516. }
  517. }
  518. //mutex_unlock(&cache_lock);
  519. int was_found = 0;
  520. if (settings.verbose > 2) {
  521. int ii;
  522. if (it == NULL) {
  523. fprintf(stderr, "> NOT FOUND ");
  524. } else {
  525. fprintf(stderr, "> FOUND KEY ");
  526. was_found++;
  527. }
  528. for (ii = 0; ii < nkey; ++ii) {
  529. fprintf(stderr, "%c", key[ii]);
  530. }
  531. }
  532. if (it != NULL) {
  533. if (settings.oldest_live != 0 && settings.oldest_live <= current_time &&
  534. it->time <= settings.oldest_live) {
  535. do_item_unlink(it, hv);
  536. do_item_remove(it);
  537. it = NULL;
  538. if (was_found) {
  539. fprintf(stderr, " -nuked by flush");
  540. }
  541. } else if (it->exptime != 0 && it->exptime <= current_time) {
  542. do_item_unlink(it, hv);
  543. do_item_remove(it);
  544. it = NULL;
  545. if (was_found) {
  546. fprintf(stderr, " -nuked by expire");
  547. }
  548. } else {
  549. it->it_flags |= ITEM_FETCHED;
  550. DEBUG_REFCNT(it, ‘+‘);
  551. }
  552. }
  553. if (settings.verbose > 2)
  554. fprintf(stderr, "\n");
  555. return it;
  556. }
  557. item *do_item_touch(const char *key, size_t nkey, uint32_t exptime,
  558. const uint32_t hv) {
  559. item *it = do_item_get(key, nkey, hv);
  560. if (it != NULL) {
  561. it->exptime = exptime;
  562. }
  563. return it;
  564. }
  565. /* expires items that are more recent than the oldest_live setting. */
  566. void do_item_flush_expired(void) {
  567. int i;
  568. item *iter, *next;
  569. if (settings.oldest_live == 0)
  570. return;
  571. for (i = 0; i < LARGEST_ID; i++) {
  572. for (iter = heads[i]; iter != NULL; iter = next) {
  573. /* iter->time of 0 are magic objects. */
  574. if (iter->time != 0 && iter->time >= settings.oldest_live) {
  575. next = iter->next;
  576. if ((iter->it_flags & ITEM_SLABBED) == 0) {
  577. do_item_unlink_nolock(iter, hash(ITEM_key(iter), iter->nkey));
  578. }
  579. } else {
  580. /* We‘ve hit the first old item. Continue to the next queue. */
  581. break;
  582. }
  583. }
  584. }
  585. }
  586. static void crawler_link_q(item *it) { /* item is the new tail */
  587. item **head, **tail;
  588. assert(it->slabs_clsid < LARGEST_ID);
  589. assert(it->it_flags == 1);
  590. assert(it->nbytes == 0);
  591. head = &heads[it->slabs_clsid];
  592. tail = &tails[it->slabs_clsid];
  593. assert(*tail != 0);
  594. assert(it != *tail);
  595. assert((*head && *tail) || (*head == 0 && *tail == 0));
  596. it->prev = *tail;
  597. it->next = 0;
  598. if (it->prev) {
  599. assert(it->prev->next == 0);
  600. it->prev->next = it;
  601. }
  602. *tail = it;
  603. if (*head == 0) *head = it;
  604. return;
  605. }
  606. static void crawler_unlink_q(item *it) {
  607. item **head, **tail;
  608. assert(it->slabs_clsid < LARGEST_ID);
  609. head = &heads[it->slabs_clsid];
  610. tail = &tails[it->slabs_clsid];
  611. if (*head == it) {
  612. assert(it->prev == 0);
  613. *head = it->next;
  614. }
  615. if (*tail == it) {
  616. assert(it->next == 0);
  617. *tail = it->prev;
  618. }
  619. assert(it->next != it);
  620. assert(it->prev != it);
  621. if (it->next) it->next->prev = it->prev;
  622. if (it->prev) it->prev->next = it->next;
  623. return;
  624. }
  625. static item *crawler_crawl_q(item *it) {
  626. item **head, **tail;
  627. assert(it->it_flags == 1);
  628. assert(it->nbytes == 0);
  629. assert(it->slabs_clsid < LARGEST_ID);
  630. head = &heads[it->slabs_clsid];
  631. tail = &tails[it->slabs_clsid];
  632. /* We‘ve hit the head, pop off */
  633. if (it->prev == 0) {
  634. assert(*head == it);
  635. if (it->next) {
  636. *head = it->next;
  637. assert(it->next->prev == it);
  638. it->next->prev = 0;
  639. }
  640. return NULL; /* Done */
  641. }
  642. assert(it->prev != it);
  643. if (it->prev) {
  644. if (*head == it->prev) {
  645. *head = it;
  646. }
  647. if (*tail == it) {
  648. *tail = it->prev;
  649. }
  650. assert(it->next != it);
  651. if (it->next) {
  652. assert(it->prev->next == it);
  653. it->prev->next = it->next;
  654. it->next->prev = it->prev;
  655. } else {
  656. it->prev->next = 0;
  657. }
  658. it->next = it->prev;
  659. it->prev = it->next->prev;
  660. it->next->prev = it;
  661. if (it->prev) {
  662. it->prev->next = it;
  663. }
  664. }
  665. assert(it->next != it);
  666. assert(it->prev != it);
  667. return it->next; /* success */
  668. }
  669. /* I pulled this out to make the main thread clearer, but it reaches into the
  670. * main thread‘s values too much. Should rethink again.
  671. 上面这句注释作者是说,他把用爬虫处理过期的item的工作放到另一个专门的线程里去做
  672. 是为了让主线程干净一点,但是这线程的工作涉及到太多主线程的东西了,得重新想想..
  673. 这个函数的作用是“评估”一下这个item是否应该free掉。其实主要就是看下有没有过期啦~
  674. 当然用户设置的settings.oldest_live参数也加入到考虑中
  675. */
  676. static void item_crawler_evaluate(item *search, uint32_t hv, int i) {
  677. rel_time_t oldest_live = settings.oldest_live;
  678. if ((search->exptime != 0 && search->exptime < current_time)
  679. || (search->time <= oldest_live && oldest_live <= current_time)) {
  680. itemstats[i].crawler_reclaimed++;
  681. if (settings.verbose > 1) {
  682. int ii;
  683. char *key = ITEM_key(search);
  684. fprintf(stderr, "LRU crawler found an expired item (flags: %d, slab: %d): ",
  685. search->it_flags, search->slabs_clsid);
  686. for (ii = 0; ii < search->nkey; ++ii) {
  687. fprintf(stderr, "%c", key[ii]);
  688. }
  689. fprintf(stderr, "\n");
  690. }
  691. if ((search->it_flags & ITEM_FETCHED) == 0) {
  692. itemstats[i].expired_unfetched++;
  693. }
  694. do_item_unlink_nolock(search, hv);
  695. do_item_remove(search);
  696. assert(search->slabs_clsid == 0);
  697. } else {
  698. refcount_decr(&search->refcount);
  699. }
  700. }
  701. /**
  702. item爬虫线程入口,负责从lru链表中把过期的item free掉
  703. */
  704. static void *item_crawler_thread(void *arg) {
  705. int i;
  706. pthread_mutex_lock(&lru_crawler_lock);
  707. if (settings.verbose > 2)
  708. fprintf(stderr, "Starting LRU crawler background thread\n");
  709. while (do_run_lru_crawler_thread) {
  710. pthread_cond_wait(&lru_crawler_cond, &lru_crawler_lock);
  711. while (crawler_count) {
  712. item *search = NULL;
  713. void *hold_lock = NULL;
  714. for (i = 0; i < LARGEST_ID; i++) {
  715. if (crawlers[i].it_flags != 1) {
  716. continue;
  717. }
  718. pthread_mutex_lock(&cache_lock);
  719. search = crawler_crawl_q((item *)&crawlers[i]);
  720. if (search == NULL ||
  721. (crawlers[i].remaining && --crawlers[i].remaining < 1)) {
  722. if (settings.verbose > 2)
  723. fprintf(stderr, "Nothing left to crawl for %d\n", i);
  724. crawlers[i].it_flags = 0;
  725. crawler_count--;
  726. crawler_unlink_q((item *)&crawlers[i]);
  727. pthread_mutex_unlock(&cache_lock);
  728. continue;
  729. }
  730. uint32_t hv = hash(ITEM_key(search), search->nkey);
  731. /* Attempt to hash item lock the "search" item. If locked, no
  732. * other callers can incr the refcount
  733. */
  734. if ((hold_lock = item_trylock(hv)) == NULL) {
  735. pthread_mutex_unlock(&cache_lock);
  736. continue;
  737. }
  738. /* Now see if the item is refcount locked */
  739. if (refcount_incr(&search->refcount) != 2) {
  740. refcount_decr(&search->refcount);
  741. if (hold_lock)
  742. item_trylock_unlock(hold_lock);
  743. pthread_mutex_unlock(&cache_lock);
  744. continue;
  745. }
  746. item_crawler_evaluate(search, hv, i);
  747. if (hold_lock)
  748. item_trylock_unlock(hold_lock);
  749. pthread_mutex_unlock(&cache_lock);
  750. if (settings.lru_crawler_sleep)
  751. usleep(settings.lru_crawler_sleep);
  752. }
  753. }
  754. if (settings.verbose > 2)
  755. fprintf(stderr, "LRU crawler thread sleeping\n");
  756. STATS_LOCK();
  757. stats.lru_crawler_running = false;
  758. STATS_UNLOCK();
  759. }
  760. pthread_mutex_unlock(&lru_crawler_lock);
  761. if (settings.verbose > 2)
  762. fprintf(stderr, "LRU crawler thread stopping\n");
  763. return NULL;
  764. }
  765. static pthread_t item_crawler_tid;
  766. //停止item爬虫线程
  767. int stop_item_crawler_thread(void) {
  768. int ret;
  769. pthread_mutex_lock(&lru_crawler_lock);
  770. do_run_lru_crawler_thread = 0;
  771. pthread_cond_signal(&lru_crawler_cond);
  772. pthread_mutex_unlock(&lru_crawler_lock);
  773. if ((ret = pthread_join(item_crawler_tid, NULL)) != 0) {
  774. fprintf(stderr, "Failed to stop LRU crawler thread: %s\n", strerror(ret));
  775. return -1;
  776. }
  777. settings.lru_crawler = false;
  778. return 0;
  779. }
  780. /**
  781. 启动item 爬虫线程
  782. */
  783. int start_item_crawler_thread(void) {
  784. int ret;
  785. if (settings.lru_crawler)
  786. return -1;
  787. pthread_mutex_lock(&lru_crawler_lock);
  788. do_run_lru_crawler_thread = 1;
  789. settings.lru_crawler = true;
  790. if ((ret = pthread_create(&item_crawler_tid, NULL,
  791. item_crawler_thread, NULL)) != 0) {
  792. fprintf(stderr, "Can‘t create LRU crawler thread: %s\n",
  793. strerror(ret));
  794. pthread_mutex_unlock(&lru_crawler_lock);
  795. return -1;
  796. }
  797. pthread_mutex_unlock(&lru_crawler_lock);
  798. return 0;
  799. }
  800. enum crawler_result_type lru_crawler_crawl(char *slabs) {
  801. char *b = NULL;
  802. uint32_t sid = 0;
  803. uint8_t tocrawl[POWER_LARGEST];
  804. if (pthread_mutex_trylock(&lru_crawler_lock) != 0) {
  805. return CRAWLER_RUNNING;
  806. }
  807. pthread_mutex_lock(&cache_lock);
  808. if (strcmp(slabs, "all") == 0) {
  809. for (sid = 0; sid < LARGEST_ID; sid++) {
  810. tocrawl[sid] = 1;
  811. }
  812. } else {
  813. for (char *p = strtok_r(slabs, ",", &b);
  814. p != NULL;
  815. p = strtok_r(NULL, ",", &b)) {
  816. if (!safe_strtoul(p, &sid) || sid < POWER_SMALLEST
  817. || sid > POWER_LARGEST) {
  818. pthread_mutex_unlock(&cache_lock);
  819. pthread_mutex_unlock(&lru_crawler_lock);
  820. return CRAWLER_BADCLASS;
  821. }
  822. tocrawl[sid] = 1;
  823. }
  824. }
  825. for (sid = 0; sid < LARGEST_ID; sid++) {
  826. if (tocrawl[sid] != 0 && tails[sid] != NULL) {
  827. if (settings.verbose > 2)
  828. fprintf(stderr, "Kicking LRU crawler off for slab %d\n", sid);
  829. crawlers[sid].nbytes = 0;
  830. crawlers[sid].nkey = 0;
  831. crawlers[sid].it_flags = 1; /* For a crawler, this means enabled. */
  832. crawlers[sid].next = 0;
  833. crawlers[sid].prev = 0;
  834. crawlers[sid].time = 0;
  835. crawlers[sid].remaining = settings.lru_crawler_tocrawl;
  836. crawlers[sid].slabs_clsid = sid;
  837. crawler_link_q((item *)&crawlers[sid]);
  838. crawler_count++;
  839. }
  840. }
  841. pthread_mutex_unlock(&cache_lock);
  842. pthread_cond_signal(&lru_crawler_cond);
  843. STATS_LOCK();
  844. stats.lru_crawler_running = true;
  845. STATS_UNLOCK();
  846. pthread_mutex_unlock(&lru_crawler_lock);
  847. return CRAWLER_OK;
  848. }
  849. //初始化lru item爬虫线程
  850. int init_lru_crawler(void) {
  851. if (lru_crawler_initialized == 0) {
  852. if (pthread_cond_init(&lru_crawler_cond, NULL) != 0) {
  853. fprintf(stderr, "Can‘t initialize lru crawler condition\n");
  854. return -1;
  855. }
  856. pthread_mutex_init(&lru_crawler_lock, NULL);
  857. lru_crawler_initialized = 1;
  858. }
  859. return 0;
  860. }
时间: 2024-10-14 20:59:19

Memcached源码分析之items.c的相关文章

Memcached源码分析

作者:Calix,转载请注明出处:http://calixwu.com 最近研究了一下memcached的源码,在这里系统总结了一下笔记和理解,写了几 篇源码分析和大家分享,整个系列分为“结构篇”和“源码篇”,建议先从结构篇开始看起,要特别说明的是我本次分析的是memcached1.4.20的版 本,不同版本会有所差异,另外,文章均为本人的个人理解,如果解析得不好或者错误的地方敬请指正. 好了,不啰嗦了,下面是导航: [结构篇] Memcached源码分析之从SET命令开始说起 Memcache

memcached源码分析-----item过期失效处理以及LRU爬虫

memcached源码分析-----item过期失效处理以及LRU爬虫,memcached-----item 转载请注明出处:http://blog.csdn.net/luotuo44/article/details/42963793 温馨提示:本文用到了一些可以在启动memcached设置的全局变量.关于这些全局变量的含义可以参考<memcached启动参数详解>.对于这些全局变量,处理方式就像<如何阅读memcached源代码>所说的那样直接取其默认值. 过期失效处理: 一个i

Linux c 开发 - Memcached源码分析之命令解析(2)

前言 从我们上一章<Linux c 开发 - Memcached源码分析之基于Libevent的网络模型>我们基本了解了Memcached的网络模型.这一章节,我们需要详细解读Memcached的命令解析. 我们回顾上一章发现Memcached会分成主线程和N个工作线程.主线程主要用于监听accpet客户端的Socket连接,而工作线程主要用于接管具体的客户端连接. 主线程和工作线程之间主要通过基于Libevent的pipe的读写事件来监听,当有连接练上来的时候,主线程会将连接交个某一个工作线

Memcached源码分析之内存管理

先再说明一下,我本次分析的memcached版本是1.4.20,有些旧的版本关于内存管理的机制和数据结构与1.4.20有一定的差异(本文中会提到). 一)模型分析在开始解剖memcached关于内存管理的源代码之前,先宏观上分析一下memcached内存管理的模型是怎样子的: 提个建议,我觉得memcached内存管理的模型与我们平时做作业的作业本“画格子给我们往格子里面写字”的逻辑很像,一本本作业本就是我们的内存空间,而我们往里写的字就是我们要存下来的数据,所以分析的时候可以想像一下用方格作业

Memcached源码分析之从SET命令开始说起

作者:Calix 如果直接把memcached的源码从main函数开始说,恐怕会有点头大,所以这里以一句经典的“SET”命令简单地开个头,算是回忆一下memcached的作用,后面的结构篇中关于命令解析部分主要也是围绕着SET命令展开分析,相信把一句SET命令背后做的事情都搞清楚,那么memcached大部分源码都了解得七七八八了. 那么,回忆一下,set命令做了个什么事情? 无非就是把一个value set到某个key上面,保存在内存当中. 再细化一下: 1)memcached是一个缓存服务器

Memcached源码分析之线程模型

作者:Calix 一)模型分析 memcached到底是如何处理我们的网络连接的? memcached通过epoll(使用libevent,下面具体再讲)实现异步的服务器,但仍然使用多线程,主要有两种线程,分别是“主线程”和“worker线程”,一个主线程,多个worker线程. 主线程负责监听网络连接,并且accept连接.当监听到连接时,accept后,连接成功,把相应的client fd丢给其中一个worker线程.worker线程接收主线程丢过来的client fd,加入到自己的epol

memcached源码分析-----安装、调试以及如何阅读memcached源码

        转载请注明出处:http://blog.csdn.net/luotuo44/article/details/42639131 安装: 安装memcached之前要先安装Libevent.现在假定Libevent安装在/usr/local/libevent目录了. 因为memcached安装后不像Libevent那样,有一堆头文件和库文件.安装后的memcached不是用来编程而直接用来运行的.所以不需要在/usr/local目录下专门为memcached建立一个目录.直接把mem

memcached源码分析-----memcached启动参数详解以及关键配置的默认值

转载请注明出处: http://blog.csdn.net/luotuo44/article/details/42672913 本文开启本系列博文的代码分析.本系列博文研究是memcached版本是1.4.21. 本文将给出memcached启动时各个参数的详细解释以及一些关键配置的默认值.以便在分析memcached源码的时候好随时查看.当然也方便使用memcached时可以随时查看各个参数的含义.<如何阅读memcached源码>说到memcached有很多全局变量(也就是关键配置),这些

Memcached源码分析——process_command函数解析

以下为个人笔记 /** * process_command 在memcached中是用来处理用户发送的命令的, * 包括get set,add,delete,replace,stats,flush_all等常用的和不常用的命令,全在这进行处理的. * 一共有两个参数,conn *c 和字符串指针 char *command : * 关于conn结构体就先不说了,反正它是整个memcached中最重要的结构体就行了,等以后研究明白了再说,先从整体上领会精神吧. * 这里我想说一下的是, memca