有些朋友可能對 redis 充滿着數不盡的求知慾, 也許是 redis 屬於工做, 交流(面試)的大頭戲,html
不得不 ... 而本身當下對於 redis 只是停留在會用層面, 細節層面幾乎沒有涉獵. 爲了更快的融於大node
家, 這裏嘗試拋磚引玉. 先帶你們手寫個 redis 中最簡單的數據結構, adlist 雙向鏈表. 讓咱們一linux
起對 redis 有個初步的認知. 本文會從下面幾個標題展開解讀(吐槽), 歡迎交流和指正.git
1. redis adlist 解析
2. redis config.h 分析
3. redis setproctitle.c 分析
4. redis atomicvar.h 分析
5. redis zmalloc 分析
6. redis Makefile 解析github
redis 大頭是 C 寫的, 而 C 啥也不缺, 就缺手寫, OK 開始廢話手寫之旅吧 :) 面試
全篇示例代碼都有手寫過, 不過爲了素材正規, 這裏直接原封不動的引用 redis
github.com/antirez/redis 中相關代碼.shell
1. redis adlist 解析json
1 /* adlist.h - A generic doubly linked list implementation 2 * 3 * Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * * Neither the name of Redis nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without 16 * specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */
30
31 #ifndef __ADLIST_H__ 32 #define __ADLIST_H__
33
34 /* Node, List, and Iterator are the only data structures used currently. */
35
36 typedef struct listNode { 37 struct listNode *prev; 38 struct listNode *next; 39 void *value; 40 } listNode; 41
42 typedef struct listIter { 43 listNode *next; 44 int direction; 45 } listIter; 46
47 typedef struct list { 48 listNode *head; 49 listNode *tail; 50 void *(*dup)(void *ptr); 51 void (*free)(void *ptr); 52 int (*match)(void *ptr, void *key); 53 unsigned long len; 54 } list; 55
56 /* Functions implemented as macros */
57 #define listLength(l) ((l)->len)
58 #define listFirst(l) ((l)->head)
59 #define listLast(l) ((l)->tail)
60 #define listPrevNode(n) ((n)->prev)
61 #define listNextNode(n) ((n)->next)
62 #define listNodeValue(n) ((n)->value)
63
64 #define listSetDupMethod(l,m) ((l)->dup = (m))
65 #define listSetFreeMethod(l,m) ((l)->free = (m))
66 #define listSetMatchMethod(l,m) ((l)->match = (m))
67
68 #define listGetDupMethod(l) ((l)->dup)
69 #define listGetFreeMethod(l) ((l)->free)
70 #define listGetMatchMethod(l) ((l)->match)
71
72 /* Prototypes */
73 list *listCreate(void); 74 void listRelease(list *list); 75 void listEmpty(list *list); 76 list *listAddNodeHead(list *list, void *value); 77 list *listAddNodeTail(list *list, void *value); 78 list *listInsertNode(list *list, listNode *old_node, void *value, int after); 79 void listDelNode(list *list, listNode *node); 80 listIter *listGetIterator(list *list, int direction); 81 listNode *listNext(listIter *iter); 82 void listReleaseIterator(listIter *iter); 83 list *listDup(list *orig); 84 listNode *listSearchKey(list *list, void *key); 85 listNode *listIndex(list *list, long index); 86 void listRewind(list *list, listIter *li); 87 void listRewindTail(list *list, listIter *li); 88 void listRotate(list *list); 89 void listJoin(list *l, list *o); 90
91 /* Directions for iterators */
92 #define AL_START_HEAD 0
93 #define AL_START_TAIL 1
94
95 #endif /* __ADLIST_H__ */
首先手寫的是 adlist.h 雙向鏈表的頭文件, 對於這個頭文件有幾點要聊一聊的. ruby
1.1' redis 中頭文件格式目前沒有統一
#ifndef __ADLIST_H__ #endif
#ifndef __REDIS_HELP_H #endif
#ifndef __ZMALLOC_H #endif
可能也是, redis 這個項目維護和開發都十年多了. 代碼風格在變(千奇百怪)也是正常.
這裏推薦第三種寫法 -> __{不帶後綴文件名}_H
1.2' adlist.h 中函數命名隨意
void listReleaseIterator(listIter *iter); list *listDup(list *orig); listNode *listSearchKey(list *list, void *key); listNode *listIndex(list *list, long index); void listRewind(list *list, listIter *li); void listRewindTail(list *list, listIter *li); void listRotate(list *list); void listJoin(list *l, list *o);
命名隨意不是個好習慣, 推薦參數名強區分. 例以下面這樣固定格式
extern void listReleaseIterator(listIter * iter); extern list * listDup(list * l); extern listNode * listSearchKey(list * l, void * key); extern listNode * listIndex(list * l, long index); extern void listRewind(list * l, listIter * iter); extern void listRewindTail(list * l, listIter * iter); extern void listRotate(list * l); extern void listJoin(list * l, list * o);
寫完 adlist.h 接口定義部分, 相信有些人對待實現的 adlist.c 也有了大體輪廓了吧 :)
1 /* adlist.c - A generic doubly linked list implementation 2 * 3 * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * * Neither the name of Redis nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without 16 * specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */
30
31
32 #include <stdlib.h>
33 #include "adlist.h"
34 #include "zmalloc.h"
35
36 /* Create a new list. The created list can be freed with 37 * AlFreeList(), but private value of every node need to be freed 38 * by the user before to call AlFreeList(). 39 * 40 * On error, NULL is returned. Otherwise the pointer to the new list. */
41 list *listCreate(void) 42 { 43 struct list *list; 44
45 if ((list = zmalloc(sizeof(*list))) == NULL) 46 return NULL; 47 list->head = list->tail = NULL; 48 list->len = 0; 49 list->dup = NULL; 50 list->free = NULL; 51 list->match = NULL; 52 return list; 53 } 54
55 /* Remove all the elements from the list without destroying the list itself. */
56 void listEmpty(list *list) 57 { 58 unsigned long len; 59 listNode *current, *next; 60
61 current = list->head; 62 len = list->len; 63 while(len--) { 64 next = current->next; 65 if (list->free) list->free(current->value); 66 zfree(current); 67 current = next; 68 } 69 list->head = list->tail = NULL; 70 list->len = 0; 71 } 72
73 /* Free the whole list. 74 * 75 * This function can't fail. */
76 void listRelease(list *list) 77 { 78 listEmpty(list); 79 zfree(list); 80 } 81
82 /* Add a new node to the list, to head, containing the specified 'value' 83 * pointer as value. 84 * 85 * On error, NULL is returned and no operation is performed (i.e. the 86 * list remains unaltered). 87 * On success the 'list' pointer you pass to the function is returned. */
88 list *listAddNodeHead(list *list, void *value) 89 { 90 listNode *node; 91
92 if ((node = zmalloc(sizeof(*node))) == NULL) 93 return NULL; 94 node->value = value; 95 if (list->len == 0) { 96 list->head = list->tail = node; 97 node->prev = node->next = NULL; 98 } else { 99 node->prev = NULL; 100 node->next = list->head; 101 list->head->prev = node; 102 list->head = node; 103 } 104 list->len++; 105 return list; 106 } 107
108 /* Add a new node to the list, to tail, containing the specified 'value' 109 * pointer as value. 110 * 111 * On error, NULL is returned and no operation is performed (i.e. the 112 * list remains unaltered). 113 * On success the 'list' pointer you pass to the function is returned. */
114 list *listAddNodeTail(list *list, void *value) 115 { 116 listNode *node; 117
118 if ((node = zmalloc(sizeof(*node))) == NULL) 119 return NULL; 120 node->value = value; 121 if (list->len == 0) { 122 list->head = list->tail = node; 123 node->prev = node->next = NULL; 124 } else { 125 node->prev = list->tail; 126 node->next = NULL; 127 list->tail->next = node; 128 list->tail = node; 129 } 130 list->len++; 131 return list; 132 } 133
134 list *listInsertNode(list *list, listNode *old_node, void *value, int after) { 135 listNode *node; 136
137 if ((node = zmalloc(sizeof(*node))) == NULL) 138 return NULL; 139 node->value = value; 140 if (after) { 141 node->prev = old_node; 142 node->next = old_node->next; 143 if (list->tail == old_node) { 144 list->tail = node; 145 } 146 } else { 147 node->next = old_node; 148 node->prev = old_node->prev; 149 if (list->head == old_node) { 150 list->head = node; 151 } 152 } 153 if (node->prev != NULL) { 154 node->prev->next = node; 155 } 156 if (node->next != NULL) { 157 node->next->prev = node; 158 } 159 list->len++; 160 return list; 161 } 162
163 /* Remove the specified node from the specified list. 164 * It's up to the caller to free the private value of the node. 165 * 166 * This function can't fail. */
167 void listDelNode(list *list, listNode *node) 168 { 169 if (node->prev) 170 node->prev->next = node->next; 171 else
172 list->head = node->next; 173 if (node->next) 174 node->next->prev = node->prev; 175 else
176 list->tail = node->prev; 177 if (list->free) list->free(node->value); 178 zfree(node); 179 list->len--; 180 } 181
182 /* Returns a list iterator 'iter'. After the initialization every 183 * call to listNext() will return the next element of the list. 184 * 185 * This function can't fail. */
186 listIter *listGetIterator(list *list, int direction) 187 { 188 listIter *iter; 189
190 if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL; 191 if (direction == AL_START_HEAD) 192 iter->next = list->head; 193 else
194 iter->next = list->tail; 195 iter->direction = direction; 196 return iter; 197 } 198
199 /* Release the iterator memory */
200 void listReleaseIterator(listIter *iter) { 201 zfree(iter); 202 } 203
204 /* Create an iterator in the list private iterator structure */
205 void listRewind(list *list, listIter *li) { 206 li->next = list->head; 207 li->direction = AL_START_HEAD; 208 } 209
210 void listRewindTail(list *list, listIter *li) { 211 li->next = list->tail; 212 li->direction = AL_START_TAIL; 213 } 214
215 /* Return the next element of an iterator. 216 * It's valid to remove the currently returned element using 217 * listDelNode(), but not to remove other elements. 218 * 219 * The function returns a pointer to the next element of the list, 220 * or NULL if there are no more elements, so the classical usage patter 221 * is: 222 * 223 * iter = listGetIterator(list,<direction>); 224 * while ((node = listNext(iter)) != NULL) { 225 * doSomethingWith(listNodeValue(node)); 226 * } 227 * 228 * */
229 listNode *listNext(listIter *iter) 230 { 231 listNode *current = iter->next; 232
233 if (current != NULL) { 234 if (iter->direction == AL_START_HEAD) 235 iter->next = current->next; 236 else
237 iter->next = current->prev; 238 } 239 return current; 240 } 241
242 /* Duplicate the whole list. On out of memory NULL is returned. 243 * On success a copy of the original list is returned. 244 * 245 * The 'Dup' method set with listSetDupMethod() function is used 246 * to copy the node value. Otherwise the same pointer value of 247 * the original node is used as value of the copied node. 248 * 249 * The original list both on success or error is never modified. */
250 list *listDup(list *orig) 251 { 252 list *copy; 253 listIter iter; 254 listNode *node; 255
256 if ((copy = listCreate()) == NULL) 257 return NULL; 258 copy->dup = orig->dup; 259 copy->free = orig->free; 260 copy->match = orig->match; 261 listRewind(orig, &iter); 262 while((node = listNext(&iter)) != NULL) { 263 void *value; 264
265 if (copy->dup) { 266 value = copy->dup(node->value); 267 if (value == NULL) { 268 listRelease(copy); 269 return NULL; 270 } 271 } else
272 value = node->value; 273 if (listAddNodeTail(copy, value) == NULL) { 274 listRelease(copy); 275 return NULL; 276 } 277 } 278 return copy; 279 } 280
281 /* Search the list for a node matching a given key. 282 * The match is performed using the 'match' method 283 * set with listSetMatchMethod(). If no 'match' method 284 * is set, the 'value' pointer of every node is directly 285 * compared with the 'key' pointer. 286 * 287 * On success the first matching node pointer is returned 288 * (search starts from head). If no matching node exists 289 * NULL is returned. */
290 listNode *listSearchKey(list *list, void *key) 291 { 292 listIter iter; 293 listNode *node; 294
295 listRewind(list, &iter); 296 while((node = listNext(&iter)) != NULL) { 297 if (list->match) { 298 if (list->match(node->value, key)) { 299 return node; 300 } 301 } else { 302 if (key == node->value) { 303 return node; 304 } 305 } 306 } 307 return NULL; 308 } 309
310 /* Return the element at the specified zero-based index 311 * where 0 is the head, 1 is the element next to head 312 * and so on. Negative integers are used in order to count 313 * from the tail, -1 is the last element, -2 the penultimate 314 * and so on. If the index is out of range NULL is returned. */
315 listNode *listIndex(list *list, long index) { 316 listNode *n; 317
318 if (index < 0) { 319 index = (-index)-1; 320 n = list->tail; 321 while(index-- && n) n = n->prev; 322 } else { 323 n = list->head; 324 while(index-- && n) n = n->next; 325 } 326 return n; 327 } 328
329 /* Rotate the list removing the tail node and inserting it to the head. */
330 void listRotate(list *list) { 331 listNode *tail = list->tail; 332
333 if (listLength(list) <= 1) return; 334
335 /* Detach current tail */
336 list->tail = tail->prev; 337 list->tail->next = NULL; 338 /* Move it as head */
339 list->head->prev = tail; 340 tail->prev = NULL; 341 tail->next = list->head; 342 list->head = tail; 343 } 344
345 /* Add all the elements of the list 'o' at the end of the 346 * list 'l'. The list 'other' remains empty but otherwise valid. */
347 void listJoin(list *l, list *o) { 348 if (o->head) 349 o->head->prev = l->tail; 350
351 if (l->tail) 352 l->tail->next = o->head; 353 else
354 l->head = o->head; 355
356 if (o->tail) l->tail = o->tail; 357 l->len += o->len; 358
359 /* Setup other as an empty list. */
360 o->head = o->tail = NULL; 361 o->len = 0; 362 }
是的, 就是這樣, 就是這樣簡單.
咱們稍微多講點, 其實對於 listCreate 能夠寫的更加簡約, 不是嗎?
struct list * listCreate(void) { return zcalloc(sizeof(struct list)); }
好了, 那咱們繼續交流(吐槽)吧.
1.3' 代碼括號 { } 位置隨意
這不是個好習慣, 畢竟誰也不喜歡兩面派. 大項目仍是得須要在大方向上統一風格和約束.
1.4' struct listIter::direction 不必定是個很好的設計
direction 經過與 AL_START_HEAD or AL_START_TAIL 宏進行運行時比對, 來區分遍歷的方向. 以爲
有點浪費. 心裏更傾向於幹掉運行時比對, 從一開始用戶就應該知道該怎麼遍歷更好, 畢竟這是全部數據結構
的標杆.
❤ 恭喜你們, 到這咱們關於 redis adlist 最基礎最簡單的數據結構已經手寫分析完畢, 後面能夠不用看了.
謝謝你們捧場 ~
簡單愉快的背後總會有些更深的不可捉摸. 離開了奶頭樂, 咱們將從 adlist.c 中一行代碼, 正式開啓
咱們這次探險之旅.
#include "zmalloc.h"
2. redis config.h 分析
一樣在 zmallo.c 中咱們發現了以下兩行代碼, 這就是咱們要說的一個主體之一 config.h
#include "config.h" #include "atomicvar.h"
config.h 主要做用是用於肯定程序的運行環境, 例如是什麼操做系統, 是什麼字節序, 要不要啓用某些功能
/* * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Redis nor the names of its contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef __CONFIG_H #define __CONFIG_H #ifdef __APPLE__ #include <AvailabilityMacros.h>
#endif #ifdef __linux__ #include <linux/version.h> #include <features.h>
#endif
/* Define redis_fstat to fstat or fstat64() */
#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
#define redis_fstat fstat64
#define redis_stat stat64
#else
#define redis_fstat fstat
#define redis_stat stat
#endif
/* Test for proc filesystem */ #ifdef __linux__ #define HAVE_PROC_STAT 1
#define HAVE_PROC_MAPS 1
#define HAVE_PROC_SMAPS 1
#define HAVE_PROC_SOMAXCONN 1
#endif
/* Test for task_info() */
#if defined(__APPLE__)
#define HAVE_TASKINFO 1
#endif
/* Test for backtrace() */
#if defined(__APPLE__) || (defined(__linux__) && defined(__GLIBC__)) || \ defined(__FreeBSD__) || (defined(__OpenBSD__) && defined(USE_BACKTRACE))\ || defined(__DragonFly__) #define HAVE_BACKTRACE 1
#endif
/* MSG_NOSIGNAL. */ #ifdef __linux__ #define HAVE_MSG_NOSIGNAL 1
#endif
/* Test for polling API */ #ifdef __linux__ #define HAVE_EPOLL 1
#endif
#if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)
#define HAVE_KQUEUE 1
#endif #ifdef __sun #include <sys/feature_tests.h> #ifdef _DTRACE_VERSION #define HAVE_EVPORT 1
#endif
#endif
/* Define redis_fsync to fdatasync() in Linux and fsync() for all the rest */ #ifdef __linux__ #define redis_fsync fdatasync
#else
#define redis_fsync fsync
#endif
/* Define rdb_fsync_range to sync_file_range() on Linux, otherwise we use * the plain fsync() call. */ #ifdef __linux__ #if defined(__GLIBC__) && defined(__GLIBC_PREREQ)
#if (LINUX_VERSION_CODE >= 0x020611 && __GLIBC_PREREQ(2, 6))
#define HAVE_SYNC_FILE_RANGE 1
#endif
#else
#if (LINUX_VERSION_CODE >= 0x020611)
#define HAVE_SYNC_FILE_RANGE 1
#endif
#endif
#endif #ifdef HAVE_SYNC_FILE_RANGE #define rdb_fsync_range(fd,off,size) sync_file_range(fd,off,size,SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE)
#else
#define rdb_fsync_range(fd,off,size) fsync(fd)
#endif
/* Check if we can use setproctitle(). * BSD systems have support for it, we provide an implementation for * Linux and osx. */
#if (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__)
#define USE_SETPROCTITLE
#endif
#if ((defined __linux && defined(__GLIBC__)) || defined __APPLE__)
#define USE_SETPROCTITLE
#define INIT_SETPROCTITLE_REPLACEMENT
void spt_init(int argc, char *argv[]); void setproctitle(const char *fmt, ...); #endif
/* Byte ordering detection */ #include <sys/types.h> /* This will likely define BYTE_ORDER */ #ifndef BYTE_ORDER #if (BSD >= 199103) # include <machine/endian.h>
#else
#if defined(linux) || defined(__linux__) # include <endian.h>
#else
#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax, pc) */
#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */
#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp)*/
#if defined(__i386__) || defined(__x86_64__) || defined(__amd64__) || \ defined(vax) || defined(ns32000) || defined(sun386) || \ defined(MIPSEL) || defined(_MIPSEL) || defined(BIT_ZERO_ON_RIGHT) || \ defined(__alpha__) || defined(__alpha) #define BYTE_ORDER LITTLE_ENDIAN
#endif
#if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \ defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \ defined(MIPSEB) || defined(_MIPSEB) || defined(_IBMR2) || defined(DGUX) ||\ defined(apollo) || defined(__convex__) || defined(_CRAY) || \ defined(__hppa) || defined(__hp9000) || \ defined(__hp9000s300) || defined(__hp9000s700) || \ defined (BIT_ZERO_ON_LEFT) || defined(m68k) || defined(__sparc) #define BYTE_ORDER BIG_ENDIAN
#endif
#endif /* linux */
#endif /* BSD */
#endif /* BYTE_ORDER */
/* Sometimes after including an OS-specific header that defines the * endianess we end with __BYTE_ORDER but not with BYTE_ORDER that is what * the Redis code uses. In this case let's define everything without the * underscores. */ #ifndef BYTE_ORDER #ifdef __BYTE_ORDER #if defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN) #ifndef LITTLE_ENDIAN #define LITTLE_ENDIAN __LITTLE_ENDIAN
#endif #ifndef BIG_ENDIAN #define BIG_ENDIAN __BIG_ENDIAN
#endif
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
#define BYTE_ORDER LITTLE_ENDIAN
#else
#define BYTE_ORDER BIG_ENDIAN
#endif
#endif
#endif
#endif
#if !defined(BYTE_ORDER) || \ (BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN) /* you must determine what the correct bit order is for * your compiler - the next line is an intentional error * which will force your compiles to bomb until you fix * the above macros. */
#error "Undefined or invalid BYTE_ORDER"
#endif
#if (__i386 || __amd64 || __powerpc__) && __GNUC__
#define GNUC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#if defined(__clang__)
#define HAVE_ATOMIC
#endif
#if (defined(__GLIBC__) && defined(__GLIBC_PREREQ))
#if (GNUC_VERSION >= 40100 && __GLIBC_PREREQ(2, 6))
#define HAVE_ATOMIC
#endif
#endif
#endif
/* Make sure we can test for ARM just checking for __arm__, since sometimes * __arm is defined but __arm__ is not. */
#if defined(__arm) && !defined(__arm__)
#define __arm__
#endif
#if defined (__aarch64__) && !defined(__arm64__)
#define __arm64__
#endif
/* Make sure we can test for SPARC just checking for __sparc__. */
#if defined(__sparc) && !defined(__sparc__)
#define __sparc__
#endif
#if defined(__sparc__) || defined(__arm__)
#define USE_ALIGNED_ACCESS
#endif
#endif
從宏定義中能夠看出來, redis 依賴 linux unix 類型的操做系統. 若是當時 redis 一心只爲
linux 服務, 預計開發和維護的心智負擔會小不少(純屬意淫). 那開始扯皮吧.
2.1' 宏排版差評, 寫起來辣眼睛
咱們以 BYTE_ORDER 爲例子, 不放給其排排版, 對對齊, 方便肉眼閱讀.
// Sometimes after including an OS-specific header that defines the // endianess we end with __BYTE_ORDER but not with BYTE_ORDER that is what // the Redis code uses. In this case let's define everything without the // underscores.
#ifndef BYTE_ORDER # ifdef __BYTE_ORDER # if defined __LITTLE_ENDIAN && defined __BIG_ENDIAN # ifndef LITTLE_ENDIAN # define LITTLE_ENGIAN __LITTLE_ENDIAN # endif # ifndef BIG_ENDIAN # define BIG_ENGIAN __BIG_ENGIAN # endif # if __BYTE_ORDER == __LITTLE_ENGIAN # define BYTE_ORDER LITTLE_ENGIAN # else # define BYTE_ORDER BIG_ENGIAN # endif # endif # endif #endif
你們看看這樣, 是否是清爽了不少.
而對於 config.h 咱們不繼續展開 config.c 了, 由於項目運行起點的就是 config. 這要再深刻下去
基本就 redis all in 了. 附贈聊聊邊角料 setproctitle 設置進程標題的話題.
#if (defined __linux && defined __GLIBC__) || (defined __APPLE__)
#define USE_SETPROCTITLE
#define INIT_SETPROCTITLE_REPLACEMENT
extern void spt_init(int argc, char * argv[]); extern void setproctitle(const char * fmt, ...); #endif
3. redis setproctitle.c 分析
1 /* ========================================================================== 2 * setproctitle.c - Linux/Darwin setproctitle. 3 * -------------------------------------------------------------------------- 4 * Copyright (C) 2010 William Ahern 5 * Copyright (C) 2013 Salvatore Sanfilippo 6 * Copyright (C) 2013 Stam He 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sublicense, and/or sell copies of the Software, and to permit 13 * persons to whom the Software is furnished to do so, subject to the 14 * following conditions: 15 * 16 * The above copyright notice and this permission notice shall be included 17 * in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 22 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 25 * USE OR OTHER DEALINGS IN THE SOFTWARE. 26 * ========================================================================== 27 */
28 #ifndef _GNU_SOURCE 29 #define _GNU_SOURCE
30 #endif
31
32 #include <stddef.h> /* NULL size_t */
33 #include <stdarg.h> /* va_list va_start va_end */
34 #include <stdlib.h> /* malloc(3) setenv(3) clearenv(3) setproctitle(3) getprogname(3) */
35 #include <stdio.h> /* vsnprintf(3) snprintf(3) */
36
37 #include <string.h> /* strlen(3) strchr(3) strdup(3) memset(3) memcpy(3) */
38
39 #include <errno.h> /* errno program_invocation_name program_invocation_short_name */
40
41 #if !defined(HAVE_SETPROCTITLE)
42 #if (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __DragonFly__)
43 #define HAVE_SETPROCTITLE 1
44 #else
45 #define HAVE_SETPROCTITLE 0
46 #endif
47 #endif
48
49
50 #if !HAVE_SETPROCTITLE
51 #if (defined __linux || defined __APPLE__)
52
53 extern char **environ; 54
55 static struct { 56 /* original value */
57 const char *arg0; 58
59 /* title space available */
60 char *base, *end; 61
62 /* pointer to original nul character within base */
63 char *nul; 64
65 _Bool reset; 66 int error; 67 } SPT; 68
69
70 #ifndef SPT_MIN 71 #define SPT_MIN(a, b) (((a) < (b))? (a) : (b))
72 #endif
73
74 static inline size_t spt_min(size_t a, size_t b) { 75 return SPT_MIN(a, b); 76 } /* spt_min() */
77
78
79 /*
80 * For discussion on the portability of the various methods, see 81 * http://lists.freebsd.org/pipermail/freebsd-stable/2008-June/043136.html
82 */
83 static int spt_clearenv(void) { 84 #if __GLIBC__
85 clearenv(); 86
87 return 0; 88 #else
89 extern char **environ; 90 static char **tmp; 91
92 if (!(tmp = malloc(sizeof *tmp))) 93 return errno; 94
95 tmp[0] = NULL; 96 environ = tmp; 97
98 return 0; 99 #endif
100 } /* spt_clearenv() */
101
102
103 static int spt_copyenv(char *oldenv[]) { 104 extern char **environ; 105 char *eq; 106 int i, error; 107
108 if (environ != oldenv) 109 return 0; 110
111 if ((error = spt_clearenv())) 112 goto error; 113
114 for (i = 0; oldenv[i]; i++) { 115 if (!(eq = strchr(oldenv[i], '='))) 116 continue; 117
118 *eq = '\0'; 119 error = (0 != setenv(oldenv[i], eq + 1, 1))? errno : 0; 120 *eq = '='; 121
122 if (error) 123 goto error; 124 } 125
126 return 0; 127 error: 128 environ = oldenv; 129
130 return error; 131 } /* spt_copyenv() */
132
133
134 static int spt_copyargs(int argc, char *argv[]) { 135 char *tmp; 136 int i; 137
138 for (i = 1; i < argc || (i >= argc && argv[i]); i++) { 139 if (!argv[i]) 140 continue; 141
142 if (!(tmp = strdup(argv[i]))) 143 return errno; 144
145 argv[i] = tmp; 146 } 147
148 return 0; 149 } /* spt_copyargs() */
150
151
152 void spt_init(int argc, char *argv[]) { 153 char **envp = environ; 154 char *base, *end, *nul, *tmp; 155 int i, error; 156
157 if (!(base = argv[0])) 158 return; 159
160 nul = &base[strlen(base)]; 161 end = nul + 1; 162
163 for (i = 0; i < argc || (i >= argc && argv[i]); i++) { 164 if (!argv[i] || argv[i] < end) 165 continue; 166
167 end = argv[i] + strlen(argv[i]) + 1; 168 } 169
170 for (i = 0; envp[i]; i++) { 171 if (envp[i] < end) 172 continue; 173
174 end = envp[i] + strlen(envp[i]) + 1; 175 } 176
177 if (!(SPT.arg0 = strdup(argv[0]))) 178 goto syerr; 179
180 #if __GLIBC__
181 if (!(tmp = strdup(program_invocation_name))) 182 goto syerr; 183
184 program_invocation_name = tmp; 185
186 if (!(tmp = strdup(program_invocation_short_name))) 187 goto syerr; 188
189 program_invocation_short_name = tmp; 190 #elif __APPLE__
191 if (!(tmp = strdup(getprogname()))) 192 goto syerr; 193
194 setprogname(tmp); 195 #endif
196
197
198 if ((error = spt_copyenv(envp))) 199 goto error; 200
201 if ((error = spt_copyargs(argc, argv))) 202 goto error; 203
204 SPT.nul = nul; 205 SPT.base = base; 206 SPT.end = end; 207
208 return; 209 syerr: 210 error = errno; 211 error: 212 SPT.error = error; 213 } /* spt_init() */
214
215
216 #ifndef SPT_MAXTITLE 217 #define SPT_MAXTITLE 255
218 #endif
219
220 void setproctitle(const char *fmt, ...) { 221 char buf[SPT_MAXTITLE + 1]; /* use buffer in case argv[0] is passed */
222 va_list ap; 223 char *nul; 224 int len, error; 225
226 if (!SPT.base) 227 return; 228
229 if (fmt) { 230 va_start(ap, fmt); 231 len = vsnprintf(buf, sizeof buf, fmt, ap); 232 va_end(ap); 233 } else { 234 len = snprintf(buf, sizeof buf, "%s", SPT.arg0); 235 } 236
237 if (len <= 0) 238 { error = errno; goto error; } 239
240 if (!SPT.reset) { 241 memset(SPT.base, 0, SPT.end - SPT.base); 242 SPT.reset = 1; 243 } else { 244 memset(SPT.base, 0, spt_min(sizeof buf, SPT.end - SPT.base)); 245 } 246
247 len = spt_min(len, spt_min(sizeof buf, SPT.end - SPT.base) - 1); 248 memcpy(SPT.base, buf, len); 249 nul = &SPT.base[len]; 250
251 if (nul < SPT.nul) { 252 *SPT.nul = '.'; 253 } else if (nul == SPT.nul && &nul[1] < SPT.end) { 254 *SPT.nul = ' '; 255 *++nul = '\0'; 256 } 257
258 return; 259 error: 260 SPT.error = error; 261 } /* setproctitle() */
262
263
264 #endif /* __linux || __APPLE__ */
265 #endif /* !HAVE_SETPROCTITLE */
3.1' spt_clearenv -> spt_copyenv -> setenv -> goto error -> environ = oldenv memory leak
https://github.com/antirez/redis/pull/6588/commits/ec5405b7ccf809929ff105aeb14d9854896f9d68
感興趣的朋友能夠一塊交流. 想了解更多也能夠參閱我和這個博主之間的交互(設置進程名稱)
http://www.javashuo.com/article/p-bxpiwdoc-er.html
4. redis atomicvar.h 分析
1 /* This file implements atomic counters using __atomic or __sync macros if 2 * available, otherwise synchronizing different threads using a mutex. 3 * 4 * The exported interface is composed of three macros: 5 * 6 * atomicIncr(var,count) -- Increment the atomic counter 7 * atomicGetIncr(var,oldvalue_var,count) -- Get and increment the atomic counter 8 * atomicDecr(var,count) -- Decrement the atomic counter 9 * atomicGet(var,dstvar) -- Fetch the atomic counter value 10 * atomicSet(var,value) -- Set the atomic counter value 11 * 12 * The variable 'var' should also have a declared mutex with the same 13 * name and the "_mutex" postfix, for instance: 14 * 15 * long myvar; 16 * pthread_mutex_t myvar_mutex; 17 * atomicSet(myvar,12345); 18 * 19 * If atomic primitives are available (tested in config.h) the mutex 20 * is not used. 21 * 22 * Never use return value from the macros, instead use the AtomicGetIncr() 23 * if you need to get the current value and increment it atomically, like 24 * in the followign example: 25 * 26 * long oldvalue; 27 * atomicGetIncr(myvar,oldvalue,1); 28 * doSomethingWith(oldvalue); 29 * 30 * ---------------------------------------------------------------------------- 31 * 32 * Copyright (c) 2015, Salvatore Sanfilippo <antirez at gmail dot com> 33 * All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions are met: 37 * 38 * * Redistributions of source code must retain the above copyright notice, 39 * this list of conditions and the following disclaimer. 40 * * Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * * Neither the name of Redis nor the names of its contributors may be used 44 * to endorse or promote products derived from this software without 45 * specific prior written permission. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 48 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 50 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 51 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 52 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 53 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 54 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 55 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 56 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 57 * POSSIBILITY OF SUCH DAMAGE. 58 */
59
60 #include <pthread.h>
61
62 #ifndef __ATOMIC_VAR_H 63 #define __ATOMIC_VAR_H
64
65 /* To test Redis with Helgrind (a Valgrind tool) it is useful to define 66 * the following macro, so that __sync macros are used: those can be detected 67 * by Helgrind (even if they are less efficient) so that no false positive 68 * is reported. */
69 // #define __ATOMIC_VAR_FORCE_SYNC_MACROS
70
71 #if !defined(__ATOMIC_VAR_FORCE_SYNC_MACROS) && defined(__ATOMIC_RELAXED) && !defined(__sun) && (!defined(__clang__) || !defined(__APPLE__) || __apple_build_version__ > 4210057)
72 /* Implementation using __atomic macros. */
73
74 #define atomicIncr(var,count) __atomic_add_fetch(&var,(count),__ATOMIC_RELAXED)
75 #define atomicGetIncr(var,oldvalue_var,count) do { \
76 oldvalue_var = __atomic_fetch_add(&var,(count),__ATOMIC_RELAXED); \ 77 } while(0) 78 #define atomicDecr(var,count) __atomic_sub_fetch(&var,(count),__ATOMIC_RELAXED)
79 #define atomicGet(var,dstvar) do { \
80 dstvar = __atomic_load_n(&var,__ATOMIC_RELAXED); \ 81 } while(0) 82 #define atomicSet(var,value) __atomic_store_n(&var,value,__ATOMIC_RELAXED)
83 #define REDIS_ATOMIC_API "atomic-builtin"
84
85 #elif defined(HAVE_ATOMIC)
86 /* Implementation using __sync macros. */
87
88 #define atomicIncr(var,count) __sync_add_and_fetch(&var,(count))
89 #define atomicGetIncr(var,oldvalue_var,count) do { \
90 oldvalue_var = __sync_fetch_and_add(&var,(count)); \ 91 } while(0) 92 #define atomicDecr(var,count) __sync_sub_and_fetch(&var,(count))
93 #define atomicGet(var,dstvar) do { \
94 dstvar = __sync_sub_and_fetch(&var,0); \ 95 } while(0) 96 #define atomicSet(var,value) do { \
97 while(!__sync_bool_compare_and_swap(&var,var,value)); \ 98 } while(0) 99 #define REDIS_ATOMIC_API "sync-builtin"
100
101 #else
102 /* Implementation using pthread mutex. */
103
104 #define atomicIncr(var,count) do { \
105 pthread_mutex_lock(&var ## _mutex); \ 106 var += (count); \ 107 pthread_mutex_unlock(&var ## _mutex); \ 108 } while(0) 109 #define atomicGetIncr(var,oldvalue_var,count) do { \
110 pthread_mutex_lock(&var ## _mutex); \ 111 oldvalue_var = var; \ 112 var += (count); \ 113 pthread_mutex_unlock(&var ## _mutex); \ 114 } while(0) 115 #define atomicDecr(var,count) do { \
116 pthread_mutex_lock(&var ## _mutex); \ 117 var -= (count); \ 118 pthread_mutex_unlock(&var ## _mutex); \ 119 } while(0) 120 #define atomicGet(var,dstvar) do { \
121 pthread_mutex_lock(&var ## _mutex); \ 122 dstvar = var; \ 123 pthread_mutex_unlock(&var ## _mutex); \ 124 } while(0) 125 #define atomicSet(var,value) do { \
126 pthread_mutex_lock(&var ## _mutex); \ 127 var = value; \ 128 pthread_mutex_unlock(&var ## _mutex); \ 129 } while(0) 130 #define REDIS_ATOMIC_API "pthread-mutex"
131
132 #endif
133 #endif /* __ATOMIC_VAR_H */
atomicvar.h 原子庫操做封裝思路有三種, C11 stdatomic.h 和 GCC sync 操做, 還有 POSIX pthread.h . 不過使用
pthread.h 封裝的"原子操做", 不是那麼通用, 由於和業務強綁定. 只能在 redis 項目下用. 緣由是它依賴事先定義
好變量 var##_mutex
pthread_mutex_lock(&var ## _mutex); \
能夠找到例子, 例如 src/lazyfree.c 中有段代碼以下
#include "server.h" #include "bio.h" #include "atomicvar.h" #include "cluster.h"
static size_t lazyfree_objects = 0; pthread_mutex_t lazyfree_objects_mutex = PTHREAD_MUTEX_INITIALIZER;
事先定義 lazyfree_objects 和 lazyfree_objects_mutex 才能使用 pthread 封裝的原子操做宏. 額外的優化
能夠經過 __sync_lock_test_and_set 替代 while __sync_bool_compare_and_swap 費力操做
https://github.com/antirez/redis/pull/6567/commits/dc1e369d6c12df73f128822b7ba30cdc4dd4357a
5. redis zmalloc 分析
1 /* zmalloc - total amount of allocated memory aware version of malloc() 2 * 3 * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * * Neither the name of Redis nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without 16 * specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */
30
31 #ifndef __ZMALLOC_H 32 #define __ZMALLOC_H
33
34 /* Double expansion needed for stringification of macro values. */
35 #define __xstr(s) __str(s)
36 #define __str(s) #s
37
38 #if defined(USE_TCMALLOC)
39 #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR))
40 #include <google/tcmalloc.h>
41 #if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1)
42 #define HAVE_MALLOC_SIZE 1
43 #define zmalloc_size(p) tc_malloc_size(p)
44 #else
45 #error "Newer version of tcmalloc required"
46 #endif
47
48 #elif defined(USE_JEMALLOC)
49 #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX))
50 #include <jemalloc/jemalloc.h>
51 #if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)
52 #define HAVE_MALLOC_SIZE 1
53 #define zmalloc_size(p) je_malloc_usable_size(p)
54 #else
55 #error "Newer version of jemalloc required"
56 #endif
57
58 #elif defined(__APPLE__)
59 #include <malloc/malloc.h>
60 #define HAVE_MALLOC_SIZE 1
61 #define zmalloc_size(p) malloc_size(p)
62 #endif
63
64 #ifndef ZMALLOC_LIB 65 #define ZMALLOC_LIB "libc"
66 #ifdef __GLIBC__ 67 #include <malloc.h>
68 #define HAVE_MALLOC_SIZE 1
69 #define zmalloc_size(p) malloc_usable_size(p)
70 #endif
71 #endif
72
73 /* We can enable the Redis defrag capabilities only if we are using Jemalloc 74 * and the version used is our special version modified for Redis having 75 * the ability to return per-allocation fragmentation hints. */
76 #if defined(USE_JEMALLOC) && defined(JEMALLOC_FRAG_HINT)
77 #define HAVE_DEFRAG
78 #endif
79
80 void *zmalloc(size_t size); 81 void *zcalloc(size_t size); 82 void *zrealloc(void *ptr, size_t size); 83 void zfree(void *ptr); 84 char *zstrdup(const char *s); 85 size_t zmalloc_used_memory(void); 86 void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); 87 size_t zmalloc_get_rss(void); 88 int zmalloc_get_allocator_info(size_t *allocated, size_t *active, size_t *resident); 89 void set_jemalloc_bg_thread(int enable); 90 int jemalloc_purge(); 91 size_t zmalloc_get_private_dirty(long pid); 92 size_t zmalloc_get_smap_bytes_by_field(char *field, long pid); 93 size_t zmalloc_get_memory_size(void); 94 void zlibc_free(void *ptr); 95
96 #ifdef HAVE_DEFRAG 97 void zfree_no_tcache(void *ptr); 98 void *zmalloc_no_tcache(size_t size); 99 #endif
100
101 #ifndef HAVE_MALLOC_SIZE 102 size_t zmalloc_size(void *ptr); 103 size_t zmalloc_usable(void *ptr); 104 #else
105 #define zmalloc_usable(p) zmalloc_size(p)
106 #endif
107
108 #ifdef REDIS_TEST 109 int zmalloc_test(int argc, char **argv); 110 #endif
111
112 #endif /* __ZMALLOC_H */
內存模塊支持外部庫豐富, 天然寫的就有點囉嗦. 咱們這裏有個訣竅, 咱們假定只使用 USE_JEMALLOC ,
而後代碼一路走下去, 是否是吼方便呢.
1 /* zmalloc - total amount of allocated memory aware version of malloc() 2 * 3 * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * * Neither the name of Redis nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without 16 * specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stdint.h>
34
35 /* This function provide us access to the original libc free(). This is useful 36 * for instance to free results obtained by backtrace_symbols(). We need 37 * to define this function before including zmalloc.h that may shadow the 38 * free implementation if we use jemalloc or another non standard allocator. */
39 void zlibc_free(void *ptr) { 40 free(ptr); 41 } 42
43 #include <string.h>
44 #include <pthread.h>
45 #include "config.h"
46 #include "zmalloc.h"
47 #include "atomicvar.h"
48
49 #ifdef HAVE_MALLOC_SIZE 50 #define PREFIX_SIZE (0)
51 #else
52 #if defined(__sun) || defined(__sparc) || defined(__sparc__)
53 #define PREFIX_SIZE (sizeof(long long))
54 #else
55 #define PREFIX_SIZE (sizeof(size_t))
56 #endif
57 #endif
58
59 /* Explicitly override malloc/free etc when using tcmalloc. */
60 #if defined(USE_TCMALLOC)
61 #define malloc(size) tc_malloc(size)
62 #define calloc(count,size) tc_calloc(count,size)
63 #define realloc(ptr,size) tc_realloc(ptr,size)
64 #define free(ptr) tc_free(ptr)
65 #elif defined(USE_JEMALLOC)
66 #define malloc(size) je_malloc(size)
67 #define calloc(count,size) je_calloc(count,size)
68 #define realloc(ptr,size) je_realloc(ptr,size)
69 #define free(ptr) je_free(ptr)
70 #define mallocx(size,flags) je_mallocx(size,flags)
71 #define dallocx(ptr,flags) je_dallocx(ptr,flags)
72 #endif
73
74 #define update_zmalloc_stat_alloc(__n) do { \
75 size_t _n = (__n); \ 76 if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ 77 atomicIncr(used_memory,__n); \ 78 } while(0) 79
80 #define update_zmalloc_stat_free(__n) do { \
81 size_t _n = (__n); \ 82 if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ 83 atomicDecr(used_memory,__n); \ 84 } while(0) 85
86 static size_t used_memory = 0; 87 pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER; 88
89 static void zmalloc_default_oom(size_t size) { 90 fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n", 91 size); 92 fflush(stderr); 93 abort(); 94 } 95
96 static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom; 97
98 void *zmalloc(size_t size) { 99 void *ptr = malloc(size+PREFIX_SIZE); 100
101 if (!ptr) zmalloc_oom_handler(size); 102 #ifdef HAVE_MALLOC_SIZE 103 update_zmalloc_stat_alloc(zmalloc_size(ptr)); 104 return ptr; 105 #else
106 *((size_t*)ptr) = size; 107 update_zmalloc_stat_alloc(size+PREFIX_SIZE); 108 return (char*)ptr+PREFIX_SIZE; 109 #endif
110 } 111
112 /* Allocation and free functions that bypass the thread cache 113 * and go straight to the allocator arena bins. 114 * Currently implemented only for jemalloc. Used for online defragmentation. */
115 #ifdef HAVE_DEFRAG 116 void *zmalloc_no_tcache(size_t size) { 117 void *ptr = mallocx(size+PREFIX_SIZE, MALLOCX_TCACHE_NONE); 118 if (!ptr) zmalloc_oom_handler(size); 119 update_zmalloc_stat_alloc(zmalloc_size(ptr)); 120 return ptr; 121 } 122
123 void zfree_no_tcache(void *ptr) { 124 if (ptr == NULL) return; 125 update_zmalloc_stat_free(zmalloc_size(ptr)); 126 dallocx(ptr, MALLOCX_TCACHE_NONE); 127 } 128 #endif
129
130 void *zcalloc(size_t size) { 131 void *ptr = calloc(1, size+PREFIX_SIZE); 132
133 if (!ptr) zmalloc_oom_handler(size); 134 #ifdef HAVE_MALLOC_SIZE 135 update_zmalloc_stat_alloc(zmalloc_size(ptr)); 136 return ptr; 137 #else
138 *((size_t*)ptr) = size; 139 update_zmalloc_stat_alloc(size+PREFIX_SIZE); 140 return (char*)ptr+PREFIX_SIZE; 141 #endif
142 } 143
144 void *zrealloc(void *ptr, size_t size) { 145 #ifndef HAVE_MALLOC_SIZE 146 void *realptr; 147 #endif
148 size_t oldsize; 149 void *newptr; 150
151 if (size == 0 && ptr != NULL) { 152 zfree(ptr); 153 return NULL; 154 } 155 if (ptr == NULL) return zmalloc(size); 156 #ifdef HAVE_MALLOC_SIZE 157 oldsize = zmalloc_size(ptr); 158 newptr = realloc(ptr,size); 159 if (!newptr) zmalloc_oom_handler(size); 160
161 update_zmalloc_stat_free(oldsize); 162 update_zmalloc_stat_alloc(zmalloc_size(newptr)); 163 return newptr; 164 #else
165 realptr = (char*)ptr-PREFIX_SIZE; 166 oldsize = *((size_t*)realptr); 167 newptr = realloc(realptr,size+PREFIX_SIZE); 168 if (!newptr) zmalloc_oom_handler(size); 169
170 *((size_t*)newptr) = size; 171 update_zmalloc_stat_free(oldsize+PREFIX_SIZE); 172 update_zmalloc_stat_alloc(size+PREFIX_SIZE); 173 return (char*)newptr+PREFIX_SIZE; 174 #endif
175 } 176
177 /* Provide zmalloc_size() for systems where this function is not provided by 178 * malloc itself, given that in that case we store a header with this 179 * information as the first bytes of every allocation. */
180 #ifndef HAVE_MALLOC_SIZE 181 size_t zmalloc_size(void *ptr) { 182 void *realptr = (char*)ptr-PREFIX_SIZE; 183 size_t size = *((size_t*)realptr); 184 /* Assume at least that all the allocations are padded at sizeof(long) by 185 * the underlying allocator. */
186 if (size&(sizeof(long)-1)) size += sizeof(long)-(size&(sizeof(long)-1)); 187 return size+PREFIX_SIZE; 188 } 189 size_t zmalloc_usable(void *ptr) { 190 return zmalloc_size(ptr)-PREFIX_SIZE; 191 } 192 #endif
193
194 void zfree(void *ptr) { 195 #ifndef HAVE_MALLOC_SIZE 196 void *realptr; 197 size_t oldsize; 198 #endif
199
200 if (ptr == NULL) return; 201 #ifdef HAVE_MALLOC_SIZE 202 update_zmalloc_stat_free(zmalloc_size(ptr)); 203 free(ptr); 204 #else
205 realptr = (char*)ptr-PREFIX_SIZE; 206 oldsize = *((size_t*)realptr); 207 update_zmalloc_stat_free(oldsize+PREFIX_SIZE); 208 free(realptr); 209 #endif
210 } 211
212 char *zstrdup(const char *s) { 213 size_t l = strlen(s)+1; 214 char *p = zmalloc(l); 215
216 memcpy(p,s,l); 217 return p; 218 } 219
220 size_t zmalloc_used_memory(void) { 221 size_t um; 222 atomicGet(used_memory,um); 223 return um; 224 } 225
226 void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) { 227 zmalloc_oom_handler = oom_handler; 228 } 229
230 /* Get the RSS information in an OS-specific way. 231 * 232 * WARNING: the function zmalloc_get_rss() is not designed to be fast 233 * and may not be called in the busy loops where Redis tries to release 234 * memory expiring or swapping out objects. 235 * 236 * For this kind of "fast RSS reporting" usages use instead the 237 * function RedisEstimateRSS() that is a much faster (and less precise) 238 * version of the function. */
239
240 #if defined(HAVE_PROC_STAT)
241 #include <unistd.h>
242 #include <sys/types.h>
243 #include <sys/stat.h>
244 #include <fcntl.h>
245
246 size_t zmalloc_get_rss(void) { 247 int page = sysconf(_SC_PAGESIZE); 248 size_t rss; 249 char buf[4096]; 250 char filename[256]; 251 int fd, count; 252 char *p, *x; 253
254 snprintf(filename,256,"/proc/%d/stat",getpid()); 255 if ((fd = open(filename,O_RDONLY)) == -1) return 0; 256 if (read(fd,buf,4096) <= 0) { 257 close(fd); 258 return 0; 259 } 260 close(fd); 261
262 p = buf; 263 count = 23; /* RSS is the 24th field in /proc/<pid>/stat */
264 while(p && count--) { 265 p = strchr(p,' '); 266 if (p) p++; 267 } 268 if (!p) return 0; 269 x = strchr(p,' '); 270 if (!x) return 0; 271 *x = '\0'; 272
273 rss = strtoll(p,NULL,10); 274 rss *= page; 275 return rss; 276 } 277 #elif defined(HAVE_TASKINFO)
278 #include <unistd.h>
279 #include <stdio.h>
280 #include <stdlib.h>
281 #include <sys/types.h>
282 #include <sys/sysctl.h>
283 #include <mach/task.h>
284 #include <mach/mach_init.h>
285
286 size_t zmalloc_get_rss(void) { 287 task_t task = MACH_PORT_NULL; 288 struct task_basic_info t_info; 289 mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; 290
291 if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS) 292 return 0; 293 task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count); 294
295 return t_info.resident_size; 296 } 297 #elif defined(__FreeBSD__)
298 #include <sys/types.h>
299 #include <sys/sysctl.h>
300 #include <sys/user.h>
301 #include <unistd.h>
302
303 size_t zmalloc_get_rss(void) { 304 struct kinfo_proc info; 305 size_t infolen = sizeof(info); 306 int mib[4]; 307 mib[0] = CTL_KERN; 308 mib[1] = KERN_PROC; 309 mib[2] = KERN_PROC_PID; 310 mib[3] = getpid(); 311
312 if (sysctl(mib, 4, &info, &infolen, NULL, 0) == 0) 313 return (size_t)info.ki_rssize; 314
315 return 0L; 316 } 317 #else
318 size_t zmalloc_get_rss(void) { 319 /* If we can't get the RSS in an OS-specific way for this system just 320 * return the memory usage we estimated in zmalloc().. 321 * 322 * Fragmentation will appear to be always 1 (no fragmentation) 323 * of course... */
324 return zmalloc_used_memory(); 325 } 326 #endif
327
328 #if defined(USE_JEMALLOC)
329
330 int zmalloc_get_allocator_info(size_t *allocated, 331 size_t *active, 332 size_t *resident) { 333 uint64_t epoch = 1; 334 size_t sz; 335 *allocated = *resident = *active = 0; 336 /* Update the statistics cached by mallctl. */
337 sz = sizeof(epoch); 338 je_mallctl("epoch", &epoch, &sz, &epoch, sz); 339 sz = sizeof(size_t); 340 /* Unlike RSS, this does not include RSS from shared libraries and other non 341 * heap mappings. */
342 je_mallctl("stats.resident", resident, &sz, NULL, 0); 343 /* Unlike resident, this doesn't not include the pages jemalloc reserves 344 * for re-use (purge will clean that). */
345 je_mallctl("stats.active", active, &sz, NULL, 0); 346 /* Unlike zmalloc_used_memory, this matches the stats.resident by taking 347 * into account all allocations done by this process (not only zmalloc). */
348 je_mallctl("stats.allocated", allocated, &sz, NULL, 0); 349 return 1; 350 } 351
352 void set_jemalloc_bg_thread(int enable) { 353 /* let jemalloc do purging asynchronously, required when there's no traffic 354 * after flushdb */
355 char val = !!enable; 356 je_mallctl("background_thread", NULL, 0, &val, 1); 357 } 358
359 int jemalloc_purge() { 360 /* return all unused (reserved) pages to the OS */
361 char tmp[32]; 362 unsigned narenas = 0; 363 size_t sz = sizeof(unsigned); 364 if (!je_mallctl("arenas.narenas", &narenas, &sz, NULL, 0)) { 365 sprintf(tmp, "arena.%d.purge", narenas); 366 if (!je_mallctl(tmp, NULL, 0, NULL, 0)) 367 return 0; 368 } 369 return -1; 370 } 371
372 #else
373
374 int zmalloc_get_allocator_info(size_t *allocated, 375 size_t *active, 376 size_t *resident) { 377 *allocated = *resident = *active = 0; 378 return 1; 379 } 380
381 void set_jemalloc_bg_thread(int enable) { 382 ((void)(enable)); 383 } 384
385 int jemalloc_purge() { 386 return 0; 387 } 388
389 #endif
390
391 /* Get the sum of the specified field (converted form kb to bytes) in 392 * /proc/self/smaps. The field must be specified with trailing ":" as it 393 * apperas in the smaps output. 394 * 395 * If a pid is specified, the information is extracted for such a pid, 396 * otherwise if pid is -1 the information is reported is about the 397 * current process. 398 * 399 * Example: zmalloc_get_smap_bytes_by_field("Rss:",-1); 400 */
401 #if defined(HAVE_PROC_SMAPS)
402 size_t zmalloc_get_smap_bytes_by_field(char *field, long pid) { 403 char line[1024]; 404 size_t bytes = 0; 405 int flen = strlen(field); 406 FILE *fp; 407
408 if (pid == -1) { 409 fp = fopen("/proc/self/smaps","r"); 410 } else { 411 char filename[128]; 412 snprintf(filename,sizeof(filename),"/proc/%ld/smaps",pid); 413 fp = fopen(filename,"r"); 414 } 415
416 if (!fp) return 0; 417 while(fgets(line,sizeof(line),fp) != NULL) { 418 if (strncmp(line,field,flen) == 0) { 419 char *p = strchr(line,'k'); 420 if (p) { 421 *p = '\0'; 422 bytes += strtol(line+flen,NULL,10) * 1024; 423 } 424 } 425 } 426 fclose(fp); 427 return bytes; 428 } 429 #else
430 size_t zmalloc_get_smap_bytes_by_field(char *field, long pid) { 431 ((void) field); 432 ((void) pid); 433 return 0; 434 } 435 #endif
436
437 size_t zmalloc_get_private_dirty(long pid) { 438 return zmalloc_get_smap_bytes_by_field("Private_Dirty:",pid); 439 } 440
441 /* Returns the size of physical memory (RAM) in bytes. 442 * It looks ugly, but this is the cleanest way to achieve cross platform results. 443 * Cleaned up from: 444 * 445 * http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system
446 * 447 * Note that this function: 448 * 1) Was released under the following CC attribution license: 449 * http://creativecommons.org/licenses/by/3.0/deed.en_US.
450 * 2) Was originally implemented by David Robert Nadeau. 451 * 3) Was modified for Redis by Matt Stancliff. 452 * 4) This note exists in order to comply with the original license. 453 */
454 size_t zmalloc_get_memory_size(void) { 455 #if defined(__unix__) || defined(__unix) || defined(unix) || \
456 (defined(__APPLE__) && defined(__MACH__)) 457 #if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64))
458 int mib[2]; 459 mib[0] = CTL_HW; 460 #if defined(HW_MEMSIZE)
461 mib[1] = HW_MEMSIZE; /* OSX. --------------------- */
462 #elif defined(HW_PHYSMEM64)
463 mib[1] = HW_PHYSMEM64; /* NetBSD, OpenBSD. --------- */
464 #endif
465 int64_t size = 0; /* 64-bit */
466 size_t len = sizeof(size); 467 if (sysctl( mib, 2, &size, &len, NULL, 0) == 0) 468 return (size_t)size; 469 return 0L; /* Failed? */
470
471 #elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
472 /* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */
473 return (size_t)sysconf(_SC_PHYS_PAGES) * (size_t)sysconf(_SC_PAGESIZE); 474
475 #elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM))
476 /* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */
477 int mib[2]; 478 mib[0] = CTL_HW; 479 #if defined(HW_REALMEM)
480 mib[1] = HW_REALMEM; /* FreeBSD. ----------------- */
481 #elif defined(HW_PHYSMEM)
482 mib[1] = HW_PHYSMEM; /* Others. ------------------ */
483 #endif
484 unsigned int size = 0; /* 32-bit */
485 size_t len = sizeof(size); 486 if (sysctl(mib, 2, &size, &len, NULL, 0) == 0) 487 return (size_t)size; 488 return 0L; /* Failed? */
489 #else
490 return 0L; /* Unknown method to get the data. */
491 #endif
492 #else
493 return 0L; /* Unknown OS. */
494 #endif
495 } 496
497 #ifdef REDIS_TEST 498 #define UNUSED(x) ((void)(x))
499 int zmalloc_test(int argc, char **argv) { 500 void *ptr; 501
502 UNUSED(argc); 503 UNUSED(argv); 504 printf("Initial used memory: %zu\n", zmalloc_used_memory()); 505 ptr = zmalloc(123); 506 printf("Allocated 123 bytes; used: %zu\n", zmalloc_used_memory()); 507 ptr = zrealloc(ptr, 456); 508 printf("Reallocated to 456 bytes; used: %zu\n", zmalloc_used_memory()); 509 zfree(ptr); 510 printf("Freed pointer; used: %zu\n", zmalloc_used_memory()); 511 return 0; 512 } 513 #endif
這裏提了一個優化, 減小代碼的無效操做.
https://github.com/antirez/redis/pull/6591/commits/ddeece5d690ec41af051a842afe20a3e0ba25d37
總體代碼很是簡單. PREFIX_SIZE 用於記錄申請內存塊大小用的, 額外的, 寫寫, 天然就全明白了. 相比其它
模塊多了 zmalloc_test 單元測試. 調用的地方能夠看 src/server.c
void _serverPanic(const char *file, int line, const char *msg, ...) { va_list ap; va_start(ap,msg); char fmtmsg[256]; vsnprintf(fmtmsg,sizeof(fmtmsg),msg,ap); va_end(ap); bugReportStart(); serverLog(LL_WARNING,"------------------------------------------------"); serverLog(LL_WARNING,"!!! Software Failure. Press left mouse button to continue"); serverLog(LL_WARNING,"Guru Meditation: %s #%s:%d",fmtmsg,file,line); #ifdef HAVE_BACKTRACE serverLog(LL_WARNING,"(forcing SIGSEGV in order to print the stack trace)"); #endif serverLog(LL_WARNING,"------------------------------------------------"); *((char*)-1) = 'x'; } #define serverPanic(...) _serverPanic(__FILE__,__LINE__,__VA_ARGS__),_exit(1) void redisOutOfMemoryHandler(size_t allocation_size) { serverLog(LL_WARNING,"Out Of Memory allocating %zu bytes!", allocation_size); serverPanic("Redis aborting for OUT OF MEMORY"); } void redisSetProcTitle(char *title) { #ifdef USE_SETPROCTITLE char *server_mode = ""; if (server.cluster_enabled) server_mode = " [cluster]"; else if (server.sentinel_mode) server_mode = " [sentinel]"; setproctitle("%s %s:%d%s", title, server.bindaddr_count ? server.bindaddr[0] : "*", server.port ? server.port : server.tls_port, server_mode); #else UNUSED(title); #endif } int main(int argc, char **argv) { struct timeval tv; int j; #ifdef REDIS_TEST if (argc == 3 && !strcasecmp(argv[1], "test")) { if (!strcasecmp(argv[2], "ziplist")) { return ziplistTest(argc, argv); } else if (!strcasecmp(argv[2], "quicklist")) { quicklistTest(argc, argv); } else if (!strcasecmp(argv[2], "intset")) { return intsetTest(argc, argv); } else if (!strcasecmp(argv[2], "zipmap")) { return zipmapTest(argc, argv); } else if (!strcasecmp(argv[2], "sha1test")) { return sha1Test(argc, argv); } else if (!strcasecmp(argv[2], "util")) { return utilTest(argc, argv); } else if (!strcasecmp(argv[2], "endianconv")) { return endianconvTest(argc, argv); } else if (!strcasecmp(argv[2], "crc64")) { return crc64Test(argc, argv); } else if (!strcasecmp(argv[2], "zmalloc")) { return zmalloc_test(argc, argv); } return -1; /* test not found */ } #endif /* We need to initialize our libraries, and the server configuration. */ #ifdef INIT_SETPROCTITLE_REPLACEMENT spt_init(argc, argv); #endif setlocale(LC_COLLATE,""); tzset(); /* Populates 'timezone' global. */ zmalloc_set_oom_handler(redisOutOfMemoryHandler); srand(time(NULL)^getpid()); gettimeofday(&tv,NULL);
...
..
.
經過這些預計你也看出點門道了吧. 關於 adlist 代碼應該也瞭解七七八八了. 最後咱們進入操練編譯環節,
打通邊角最後的一千米.
6. redis Makefile 解析
加了註釋, 方便你們自行逐個閱讀 mkreleasehdr.sh 和 Makefile, 感悟編譯的不容易.
1 #!/bin/sh
2
3 # git log 第一個八位hash 值 4 GIT_SHA1=`(git show-ref --head --hash=8 2>/dev/null || echo 00000000) | head -n1` 5
6 # 顯示提交, 提交和工做樹等之間的變化, 禁止外部差別驅動程序. 系統默認的 git diff
7 GIT_DIRTY=`git diff --no-ext-diff 2>/dev/null | wc -l` 8
9 # {網絡節點上的主機名}-{時間戳} 10 BUILD_ID=`uname -n`"-"`date +%s` 11 # -n 判斷 "$SOURCE_DATE_EPOCH" 是否不是空串, 不是爲真 12 if [ -n "$SOURCE_DATE_EPOCH" ] then
13 # SOURCE_DATE_EPOCH=1574154953 -> date -u -d "@1574154953" +%s -> 1574154953 仍是傳入的時間戳 14 # SOURCE_DATE_EPOCH=src -> date -u -r "src" +%s -> 1574153469 顯示指定文件的最後修改時間 15 # date -u +%s -> 1574155246 -> 輸出或者設置協調的通用時間 16 BUILD_ID=$(date -u -d "@$SOURCE_DATE_EPOCH" +%s 2>/dev/null || date -u -r "$SOURCE_DATE_EPOCH" +%s 2>/dev/null || date -u +%s) 17 fi
18
19 # 測試 release.h 文件是否存在, 不存在就建立 20 test -f release.h || touch release.h 21 # 判斷 release.h 是否已經建立 OK, OK 的話 exit 退出 22 (cat release.h | grep SHA1 | grep $GIT_SHA1) && \ 23 (cat release.h | grep DIRTY | grep $GIT_DIRTY) && exit 0 # Already up-to-date
24
25 # release.h 寫入宏配置 REDIS_GIT_SHA1 REDIS_GIT_DIRTY REDIS_BUILD_ID 26 echo "#define REDIS_GIT_SHA1 \"$GIT_SHA1\"" > release.h 27 echo "#define REDIS_GIT_DIRTY \"$GIT_DIRTY\"" >> release.h 28 echo "#define REDIS_BUILD_ID \"$BUILD_ID\"" >> release.h 29
30 # touch 更新 文件訪問時間 atime 文件內容更改時間 mtime 文件狀態改動時間 ctime 31 touch release.c # Force recompile release.c
1 # Redis Makefile 2 # Copyright (C) 2009 Salvatore Sanfilippo <antirez at gmail dot com>
3 # This file is released under the BSD license, see the COPYING file
4 # 5 # The Makefile composes the final FINAL_CFLAGS and FINAL_LDFLAGS using 6 # what is needed for Redis plus the standard CFLAGS and LDFLAGS passed. 7 # However when building the dependencies (Jemalloc, Lua, Hiredis, ...) 8 # CFLAGS and LDFLAGS are propagated to the dependencies, so to pass 9 # flags only to be used when compiling / linking Redis itself REDIS_CFLAGS 10 # and REDIS_LDFLAGS are used instead (this is the case of 'make gcov'). 11 # 12 # Dependencies are stored in the Makefile.dep file. To rebuild this file
13 # Just use 'make dep', but this is only needed by developers. 14
15 # Makefile run sh ./mkreleasehdr.sh
16 release_hdr := $(shell sh -c './mkreleasehdr.sh') 17 # 定義直接展現式變量 uname_S := 內核名稱, 例如 uname -s -> Linux 18 uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') 19 # 定義直接展現式變量 uname_M := 主機的硬件架構名稱, 例如 uname -s -> x86_64 20 uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not') 21 # 定義條件賦值變量, 當 OPTIMIZATION 不存在, 會有 OPTIMIZATION :=-O2, 不然不改變 OPTIMIZATION 22 OPTIMIZATION?=-O2 23 # 定義遞歸展開式變量 DEPENDENCY_TARGETS =hiredis linenoise lua 24 # Makefile 展開時候 = 後面若是有空格, 會保留. 25 DEPENDENCY_TARGETS=hiredis linenoise lua 26 # 定義直接展現式變量 NODEPS :=clean distclean 27 NODEPS:=clean distclean 28
29 # Default settings 30 # -std=c11 使用 C11 標準 31 # -pedantic 以ANSI/ISO C標準列出的全部警告 32 # -DREDIS_STATIC='' 進行 REDIS_STATIC='' 宏定義 33 STD=-std=c11 -pedantic -DREDIS_STATIC=''
34 # ifneq ($1, $2) $1 != $2 爲真 35 # $(findstring clang,$(CC)) 在 $(CC) 中查找 clang 字符串, 找到了返回 clang, 不然返回空串 36 # $(findstring FreeBSD,$(uname_S)) 在 $(uname_S) 中查找 FreeBSD 字符串 ... 37 # -Wno-c11-extensions 刪除 C11 擴展警告 38 ifneq (,$(findstring clang,$(CC))) 39 ifneq (,$(findstring FreeBSD,$(uname_S))) 40 STD+=-Wno-c11-extensions 41 endif 42 endif 43 # -Wall 顯示編譯後全部警告 44 # -W 相似-Wall 會顯示警告, 可是隻顯示編譯器認爲會出現錯誤的警告 45 # -Wno-missing-field-initializers 禁止結構初始化未指定全部字段警告 46 WARN=-Wall -W -Wno-missing-field-initializers 47 # 定義遞歸展開式變量 OPT =$(OPTIMIZATION) ?=-O2 48 OPT=$(OPTIMIZATION) 49
50 # # 定義條件賦值變量, 當 PREFIX 不存在, 會有 PREFIX :=/usr/local, 不然維持 PREFIX 變量不變 51 PREFIX?=/usr/local 52 INSTALL_BIN=$(PREFIX)/bin 53 INSTALL=install
54
55 # Default allocator defaults to Jemalloc if it's not an ARM
56 MALLOC=libc 57 # ifneq ($(uname_M),armv6l) $(uname_M) 硬件架構不是 armv6l 才爲真 58 # ifneq ($(uname_M),armv7l) $(uname_M) 硬件架構不是 armv7l 才爲真 59 # ifeq ($(uname_S),Linux) $(uname_S) 內核名稱是 Linux 才爲真 60 ifneq ($(uname_M),armv6l) 61 ifneq ($(uname_M),armv7l) 62 ifeq ($(uname_S),Linux) 63 MALLOC=jemalloc 64 endif 65 endif 66 endif 67
68 # To get ARM stack traces if Redis crashes we need a special C flag. 69 # $(filter aarch64 armv,$(uname_M)) $(uname_M) 硬件架構中若是是 aarch64 or armv 就保留 70 # -funwind-tables 開啓便於作 backtrace 71 # unwind table 表記錄了與函數相關的信息, 函數的起始地址, 函數的結束地址, 一個 info block 指針 72 ifneq (,$(filter aarch64 armv,$(uname_M))) 73 CFLAGS+=-funwind-tables 74 else
75 ifneq (,$(findstring armv,$(uname_M))) 76 CFLAGS+=-funwind-tables 77 endif 78 endif 79
80 # Backwards compatibility for selecting an allocator 81 ifeq ($(USE_TCMALLOC),yes) 82 MALLOC=tcmalloc 83 endif 84
85 ifeq ($(USE_TCMALLOC_MINIMAL),yes) 86 MALLOC=tcmalloc_minimal 87 endif 88
89 # ifeq ($(USE_JEMALLOC),yes) $(USE_JEMALLOC) == yes 會讓 MALLOC=jemalloc 90 ifeq ($(USE_JEMALLOC),yes) 91 MALLOC=jemalloc 92 endif 93
94 ifeq ($(USE_JEMALLOC),no) 95 MALLOC=libc 96 endif 97
98 # Override default settings if possible 99 # - 忽略錯誤 100 # include .make-settings 導入 .make-settings Makefile 文件 101 -include .make-settings 102
103 # 以 Linux jemalloc 爲例 104 # FINAL_CFLAGS=-std=c11 -pedantic -DREDIS_STATIC='' -Wall -W -Wno-missing-field-initializers -O2 -g -ggdb 105 # FINAL_LDFLAGS= -g -ggdb 106 # -g 操做系統的原生格式(native format)生成調試信息, 其它調試器也能夠用 107 # -ggdb 使 GCC 爲 GDB 生成專用的更爲豐富的調試信息 108 # -lm 連接 math.h 相關庫 109 FINAL_CFLAGS=$(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS) 110 FINAL_LDFLAGS=$(LDFLAGS) $(REDIS_LDFLAGS) $(DEBUG) 111 FINAL_LIBS=-lm 112 DEBUG=-g -ggdb 113
114 ifeq ($(uname_S),SunOS) 115 # SunOS 116 ifneq ($(@@),32bit) 117 CFLAGS+= -m64 118 LDFLAGS+= -m64 119 endif 120 DEBUG=-g 121 DEBUG_FLAGS=-g 122 export CFLAGS LDFLAGS DEBUG DEBUG_FLAGS 123 INSTALL=cp -pf 124 FINAL_CFLAGS+= -D__EXTENSIONS__ -D_XPG6 125 FINAL_LIBS+= -ldl -lnsl -lsocket -lresolv -lpthread -lrt 126 else
127 ifeq ($(uname_S),Darwin) 128 # Darwin 129 FINAL_LIBS+= -ldl 130 OPENSSL_CFLAGS=-I/usr/local/opt/openssl/include 131 OPENSSL_LDFLAGS=-L/usr/local/opt/openssl/lib 132 else
133 ifeq ($(uname_S),AIX) 134 # AIX 135 FINAL_LDFLAGS+= -Wl,-bexpall 136 FINAL_LIBS+=-ldl -pthread -lcrypt -lbsd 137 else
138 ifeq ($(uname_S),OpenBSD) 139 # OpenBSD 140 FINAL_LIBS+= -lpthread 141 ifeq ($(USE_BACKTRACE),yes) 142 FINAL_CFLAGS+= -DUSE_BACKTRACE -I/usr/local/include 143 FINAL_LDFLAGS+= -L/usr/local/lib 144 FINAL_LIBS+= -lexecinfo 145 endif 146
147 else
148 ifeq ($(uname_S),FreeBSD) 149 # FreeBSD 150 FINAL_LIBS+= -lpthread -lexecinfo 151 else
152 ifeq ($(uname_S),DragonFly) 153 # FreeBSD 154 FINAL_LIBS+= -lpthread -lexecinfo 155 else
156 # All the other OSes (notably Linux) 157 FINAL_LDFLAGS+= -rdynamic 158 FINAL_LIBS+=-ldl -pthread -lrt 159 endif 160 endif 161 endif 162 endif 163 endif 164 endif 165 # Include paths to dependencies 166 FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src 167
168 ifeq ($(MALLOC),tcmalloc) 169 FINAL_CFLAGS+= -DUSE_TCMALLOC 170 FINAL_LIBS+= -ltcmalloc 171 endif 172
173 ifeq ($(MALLOC),tcmalloc_minimal) 174 FINAL_CFLAGS+= -DUSE_TCMALLOC 175 FINAL_LIBS+= -ltcmalloc_minimal 176 endif 177
178 # ifeq ($(MALLOC),jemalloc) $(MALLOC) == jemalloc 爲真才進入條件分支 179 # DEPENDENCY_TARGETS += jemalloc -> DEPENDENCY_TARGETS = hiredis linenoise lua jemalloc 180 # FINAL_CFLAGS 繼續 += -DUSE_JEMALLOC -I../deps/jemalloc/include 引入 USE_JEMALLOC 宏和指定頭文件路徑 181 # FINAL_LIBS := ../deps/jemalloc/lib/libjemalloc.a -lm 182 ifeq ($(MALLOC),jemalloc) 183 DEPENDENCY_TARGETS+= jemalloc 184 FINAL_CFLAGS+= -DUSE_JEMALLOC -I../deps/jemalloc/include 185 FINAL_LIBS := ../deps/jemalloc/lib/libjemalloc.a $(FINAL_LIBS) 186 endif 187
188 # BUILD_TLS -> USE_OPENSSL 189 ifeq ($(BUILD_TLS),yes) 190 FINAL_CFLAGS+=-DUSE_OPENSSL $(OPENSSL_CFLAGS) 191 FINAL_LDFLAGS+=$(OPENSSL_LDFLAGS) 192 FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a -lssl -lcrypto 193 endif 194
195 REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS) 196 REDIS_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS) 197 REDIS_INSTALL=$(QUIET_INSTALL)$(INSTALL) 198
199 # CCCOLOR="\033[34m" 藍色字 200 # LINKCOLOR="\033[34;1m" 藍色字 高亮 201 # SRCCOLOR="\033[33m" 黃色字 202 # BINCOLOR="\033[37;1m" 白色字 高亮 203 # MAKECOLOR="\033[32;1m" 綠色字 高亮 204 # ENDCOLOR="\033[0m" 關閉全部屬性 205 CCCOLOR="\033[34m"
206 LINKCOLOR="\033[34;1m"
207 SRCCOLOR="\033[33m"
208 BINCOLOR="\033[37;1m"
209 MAKECOLOR="\033[32;1m"
210 ENDCOLOR="\033[0m"
211
212 # ifndef 不存在 V 那麼就爲真 213 # @printf Makefile 屏幕上再也不回顯執行的命令 214 # %b 相對應的參數被視爲含有要被處理的轉義序列之字符串 215 # 1>&2 標準輸出重定向到標準錯誤 216 # $@ 表示規則中的目標 217 # QUIET_CC 編譯, QUIET_LINK 連接, QUIET_INSTALL 安裝 218 ifndef V 219 QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR) 1>&2; 220 QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2; 221 QUIET_INSTALL = @printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2; 222 endif 223
224 REDIS_SERVER_NAME=redis-server 225 REDIS_SENTINEL_NAME=redis-sentinel 226 REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o 227 REDIS_CLI_NAME=redis-cli 228 REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o siphash.o crc16.o 229 REDIS_BENCHMARK_NAME=redis-benchmark 230 REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o siphash.o redis-benchmark.o 231 REDIS_CHECK_RDB_NAME=redis-check-rdb 232 REDIS_CHECK_AOF_NAME=redis-check-aof 233
234 all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) 235 @echo ""
236 @echo "Hint: It's a good idea to run 'make test' ;)"
237 @echo ""
238
239 # 構建 Makefile.dep 文件 240 # -M 不是輸出預編譯過程的結果, 而是輸出一個用於make的規則, 該規則描述了這個源文件的依賴關係. 241 # 預編譯器輸出的這個 make 規則包含名字與原文件相同的目標文件, 冒號和全部 include 文件的名字 242 # -MM 與 -M 類似, 只是不包含系統頭文件 243 Makefile.dep: 244 -$(REDIS_CC) -MM *.c > Makefile.dep 2> /dev/null || true
245
246 # $(words <text>) 單詞個數統計函數 247 # MAKECMDGOALS 會存放你所指定的終極目標的列表,若是在命令行上,你沒有指定目標,那麼,這個變量是空值 248 # NODEPS:=clean distclean 249 ifeq (0, $(words $(findstring $(MAKECMDGOALS), $(NODEPS)))) 250 -include Makefile.dep 251 endif 252
253 .PHONY: all 254
255 # 構建 .make-settings 256 persist-settings: distclean 257 echo STD=$(STD) >> .make-settings 258 echo WARN=$(WARN) >> .make-settings 259 echo OPT=$(OPT) >> .make-settings 260 echo MALLOC=$(MALLOC) >> .make-settings 261 echo CFLAGS=$(CFLAGS) >> .make-settings 262 echo LDFLAGS=$(LDFLAGS) >> .make-settings 263 echo REDIS_CFLAGS=$(REDIS_CFLAGS) >> .make-settings 264 echo REDIS_LDFLAGS=$(REDIS_LDFLAGS) >> .make-settings 265 echo PREV_FINAL_CFLAGS=$(FINAL_CFLAGS) >> .make-settings 266 echo PREV_FINAL_LDFLAGS=$(FINAL_LDFLAGS) >> .make-settings 267 -(cd ../deps && $(MAKE) $(DEPENDENCY_TARGETS)) 268
269 .PHONY: persist-settings 270
271 # Prerequisites target 272 .make-prerequisites: 273 @touch $@ 274
275 # Clean everything, persist settings and build dependencies if anything changed 276 ifneq ($(strip $(PREV_FINAL_CFLAGS)), $(strip $(FINAL_CFLAGS))) 277 .make-prerequisites: persist-settings 278 endif 279
280 # $(strip STRINT) 去空格函數 281 ifneq ($(strip $(PREV_FINAL_LDFLAGS)), $(strip $(FINAL_LDFLAGS))) 282 .make-prerequisites: persist-settings 283 endif 284
285 # redis-server 286 $(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ) 287 $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a $(FINAL_LIBS) 288
289 # redis-sentinel 290 $(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME) 291 $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) 292
293 # redis-check-rdb 294 $(REDIS_CHECK_RDB_NAME): $(REDIS_SERVER_NAME) 295 $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_RDB_NAME) 296
297 # redis-check-aof 298 $(REDIS_CHECK_AOF_NAME): $(REDIS_SERVER_NAME) 299 $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME) 300
301 # redis-cli 302 $(REDIS_CLI_NAME): $(REDIS_CLI_OBJ) 303 $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS) 304
305 # redis-benchmark 306 $(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ) 307 $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS) 308
309 dict-benchmark: dict.c zmalloc.c sds.c siphash.c 310 $(REDIS_CC) $(FINAL_CFLAGS) $^ -D DICT_BENCHMARK_MAIN -o $@ $(FINAL_LIBS) 311
312 # Because the jemalloc.h header is generated as a part of the jemalloc build, 313 # building it should complete before building any other object. Instead of 314 # depending on a single artifact, build all dependencies first. 315 %.o: %.c .make-prerequisites 316 $(REDIS_CC) -c $<
317
318 clean: 319 rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep dict-benchmark 320
321 .PHONY: clean 322
323 distclean: clean 324 -(cd ../deps && $(MAKE) distclean) 325 -(rm -f .make-*) 326
327 .PHONY: distclean 328
329 test: $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME) 330 @(cd ..; ./runtest) 331
332 test-sentinel: $(REDIS_SENTINEL_NAME) 333 @(cd ..; ./runtest-sentinel) 334
335 check: test 336
337 lcov: 338 $(MAKE) gcov 339 @(set -e; cd ..; ./runtest --clients 1) 340 @geninfo -o redis.info . 341 @genhtml --legend -o lcov-html redis.info
342
343 test-sds: sds.c sds.h 344 $(REDIS_CC) sds.c zmalloc.c -DSDS_TEST_MAIN $(FINAL_LIBS) -o /tmp/sds_test 345 /tmp/sds_test 346
347 .PHONY: lcov 348
349 bench: $(REDIS_BENCHMARK_NAME) 350 ./$(REDIS_BENCHMARK_NAME) 351
352 32bit: 353 @echo ""
354 @echo "WARNING: if it fails under Linux you probably need to install libc6-dev-i386"
355 @echo ""
356 $(MAKE) CFLAGS="-m32" LDFLAGS="-m32"
357
358 gcov: 359 $(MAKE) REDIS_CFLAGS="-fprofile-arcs -ftest-coverage -DCOVERAGE_TEST" REDIS_LDFLAGS="-fprofile-arcs -ftest-coverage"
360
361 noopt: 362 $(MAKE) OPTIMIZATION="-O0"
363
364 valgrind: 365 $(MAKE) OPTIMIZATION="-O0" MALLOC="libc"
366
367 helgrind: 368 $(MAKE) OPTIMIZATION="-O0" MALLOC="libc" CFLAGS="-D__ATOMIC_VAR_FORCE_SYNC_MACROS"
369
370 # 生成 src/help.h 頭文件 371 # @../utils/generate-command-help.rb > help.h 經過 ruby 腳本請求 372 # https://raw.githubusercontent.com/antirez/redis-doc/master/commands.json 構建相關 C 中全局區變量信息
373 src/help.h: 374 @../utils/generate-command-help.rb > help.h 375
376 install: all 377 @mkdir -p $(INSTALL_BIN) 378 $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(INSTALL_BIN) 379 $(REDIS_INSTALL) $(REDIS_BENCHMARK_NAME) $(INSTALL_BIN) 380 $(REDIS_INSTALL) $(REDIS_CLI_NAME) $(INSTALL_BIN) 381 $(REDIS_INSTALL) $(REDIS_CHECK_RDB_NAME) $(INSTALL_BIN) 382 $(REDIS_INSTALL) $(REDIS_CHECK_AOF_NAME) $(INSTALL_BIN) 383 @ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME) 384
385 uninstall: 386 rm -f $(INSTALL_BIN)/{$(REDIS_SERVER_NAME),$(REDIS_BENCHMARK_NAME),$(REDIS_CLI_NAME),$(REDIS_CHECK_RDB_NAME),$(REDIS_CHECK_AOF_NAME),$(REDIS_SENTINEL_NAME)}
感謝你們 ☆ redis 最簡單的數據結構 adlist 雙向鏈表就帶你們寫到這了. 有緣再見 ~
錯誤是不免, 歡迎你們指正交流, 共同提升 ~
IF《若是》——Rudyard Kipling(拉迪亞德.吉普林) If you can keep your head when all about you Are losing theirs and blaming it on you; If you can trust yourself when all men doubt you, But make allowance for their doubting too; If you can wait and not be tired by waiting, Or, being lied about, don't deal in lies, Or, being hated, don't give way to hating, And yet don't look too good, nor talk too wise; 若是全部人都失去理智,咒罵你, 你仍能保持頭腦清醒; 若是全部人都懷疑你, 你仍能堅信本身,讓全部的懷疑動搖; 若是你要等待,不要所以厭煩, 爲人所騙,不要所以騙人, 爲人所恨,不要所以抱恨, 不要太樂觀,不要自覺得是; If you can dream - and not make dreams your master; If you can think - and not make thoughts your aim; If you can meet with triumph and disaster And treat those two imposters just the same; If you can bear to hear the truth you've spoken Twisted by knaves to make a trap for fools, Or watch the things you gave your life to broken, And stoop and build ‘em up with worn-out tools; 若是你是個追夢人——不要被夢主宰; 若是你是個愛思考的人——不要以思想者自居; 若是你遇到驕傲和挫折 把二者當騙子看待; 若是你能忍受,你曾講過的事實 被惡棍扭曲,用於矇騙傻子; 或者,看着你用畢生去看護的東西被破壞, 俯下身去,用破舊的工具把它修補; If you can make one heap of all your winnings And risk it on one turn of pitch-and-toss, And lose, and start again at your beginnings And never breath a word about your loss; If you can force your heart and nerve and sinew To serve your turn long after they are gone, And so hold on when there is nothing in you Except the Will which says to them: "Hold on"; 若是在你贏得無數桂冠以後, 而後背注一擲再搏一次, 失敗事後,東山再起, 不要抱怨你的失敗; 若是你能迫使本身, 在別人走後,長久堅守陣地, 在你心中已空蕩蕩無一物, 只有意志告訴你「堅持!」; If you can talk with crowds and keep your virtue, Or walk with kings - nor lose the common touch; If neither foes nor loving friends can hurt you; If all men count with you, but none too much; If you can fill the unforgiving minute With sixty seconds' worth of distance run - Yours is the Earth and everything that's in it, 若是你與人交談,能保持風度, 伴王同行,能保持距離; 若是仇敵和好友都不害你; 若是全部人都期望你,卻無人全心全意; 若是你花六十秒進行短程跑, 填滿那不可饒恕的一分鐘—— 你就能夠擁有一個世界, 這個世界的一切都是你的, 更重要的是,孩子,你是個頂天立地的人。