主頁 > 後端開發 > C基礎 帶你手寫 redis adlist 雙向鏈表

C基礎 帶你手寫 redis adlist 雙向鏈表

2020-09-17 03:12:52 後端開發

引言 - 導航欄目

  有些朋友可能對 redis 充滿著數不盡的求知欲, 也許是 redis 屬于作業, 交流(面試)的大頭戲,

不得不 ... 而自己當下對于 redis 只是停留在會用層面, 細節層面幾乎沒有涉獵. 為了更快的融于大

家, 這里嘗試拋磚引玉. 先帶大家手寫個 redis 中最簡單的資料結構, adlist 雙向鏈表. 讓我們一

起對 redis 有個初步的認知. 本文會從下面幾個標題展開解讀(吐槽), 歡迎交流和指正.

  1. redis adlist 決議
  2. redis config.h 分析
  3. redis setproctitle.c 分析
  4. redis atomicvar.h 分析
  5. redis zmalloc 分析
  6. redis Makefile 決議

redis 大頭是 C 寫的, 而 C 啥也不缺, 就缺手寫, OK 開始廢話手寫之旅吧 :)

前言 - redis adlist      

  全篇示例代碼都有手寫過, 不過為了素材正規, 這里直接原封不動的參考 

github.com/antirez/redis 中相關代碼.

1. redis adlist 決議

 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 copyright12  *     notice, this list of conditions and the following disclaimer in the13  *     documentation and/or other materials provided with the distribution.14  *   * Neither the name of Redis nor the names of its contributors may be used15  *     to endorse or promote products derived from this software without16  *     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, THE20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN26  * 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 THE28  * 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 093 #define AL_START_TAIL 194 95 #endif /* __ADLIST_H__ */

首先手寫的是 adlist.h 雙向鏈表的頭檔案, 對于這個頭檔案有幾點要聊一聊的. 

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 =https://www.cnblogs.com/life2refuel/p/ 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. the112  * 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 =https://www.cnblogs.com/life2refuel/p/ 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 =https://www.cnblogs.com/life2refuel/p/ 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     else172         list->head = node->next;173     if (node->next)174         node->next->prev = node->prev;175     else176         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 every183  * 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     else194         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 using217  * 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 patter221  * 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         else237             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 used246  * to copy the node value. Otherwise the same pointer value of247  * 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 = https://www.cnblogs.com/life2refuel/p/copy->dup(node->value);267             if (value =https://www.cnblogs.com/life2refuel/p/= NULL) {268                 listRelease(copy);269                 return NULL;270             }271         } else272             value = https://www.cnblogs.com/life2refuel/p/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' method283  * set with listSetMatchMethod(). If no 'match' method284  * is set, the 'value' pointer of every node is directly285  * compared with the 'key' pointer.286  *287  * On success the first matching node pointer is returned288  * (search starts from head). If no matching node exists289  * 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 index311  * where 0 is the head, 1 is the element next to head312  * and so on. Negative integers are used in order to count313  * from the tail, -1 is the last element, -2 the penultimate314  * 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 the346  * 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     else354         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 周邊

   簡單愉快的背后總會有些更深的不可捉摸. 離開了奶頭樂, 我們將從 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_REPLACEMENTvoid 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_REPLACEMENTextern 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 #endif100 } /* 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 #endif196 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_MAXTITLE217 #define SPT_MAXTITLE 255218 #endif219 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  

感興趣的朋友可以一塊交流. 想了解更多也可以參閱我和這個博主之間的互動(設定行程名稱)

https://www.cnblogs.com/imlgc/p/3823990.html#4428962

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 #else102 /* 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 #endif133 #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 #endif100 101 #ifndef HAVE_MALLOC_SIZE102 size_t zmalloc_size(void *ptr);103 size_t zmalloc_usable(void *ptr);104 #else105 #define zmalloc_usable(p) zmalloc_size(p)106 #endif107 108 #ifdef REDIS_TEST109 int zmalloc_test(int argc, char **argv);110 #endif111 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_SIZE103     update_zmalloc_stat_alloc(zmalloc_size(ptr));104     return ptr;105 #else106     *((size_t*)ptr) = size;107     update_zmalloc_stat_alloc(size+PREFIX_SIZE);108     return (char*)ptr+PREFIX_SIZE;109 #endif110 }111 112 /* Allocation and free functions that bypass the thread cache113  * and go straight to the allocator arena bins.114  * Currently implemented only for jemalloc. Used for online defragmentation. */115 #ifdef HAVE_DEFRAG116 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 #endif129 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_SIZE135     update_zmalloc_stat_alloc(zmalloc_size(ptr));136     return ptr;137 #else138     *((size_t*)ptr) = size;139     update_zmalloc_stat_alloc(size+PREFIX_SIZE);140     return (char*)ptr+PREFIX_SIZE;141 #endif142 }143 144 void *zrealloc(void *ptr, size_t size) {145 #ifndef HAVE_MALLOC_SIZE146     void *realptr;147 #endif148     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_SIZE157     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 #else165     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 #endif175 }176 177 /* Provide zmalloc_size() for systems where this function is not provided by178  * malloc itself, given that in that case we store a header with this179  * information as the first bytes of every allocation. */180 #ifndef HAVE_MALLOC_SIZE181 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) by185      * 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 #endif193 194 void zfree(void *ptr) {195 #ifndef HAVE_MALLOC_SIZE196     void *realptr;197     size_t oldsize;198 #endif199 200     if (ptr == NULL) return;201 #ifdef HAVE_MALLOC_SIZE202     update_zmalloc_stat_free(zmalloc_size(ptr));203     free(ptr);204 #else205     realptr = (char*)ptr-PREFIX_SIZE;206     oldsize = *((size_t*)realptr);207     update_zmalloc_stat_free(oldsize+PREFIX_SIZE);208     free(realptr);209 #endif210 }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 fast233  * and may not be called in the busy loops where Redis tries to release234  * memory expiring or swapping out objects.235  *236  * For this kind of "fast RSS reporting" usages use instead the237  * 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 #else318 size_t zmalloc_get_rss(void) {319     /* If we can't get the RSS in an OS-specific way for this system just320      * 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 #endif327 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 non341      * heap mappings. */342     je_mallctl("stats.resident", resident, &sz, NULL, 0);343     /* Unlike resident, this doesn't not include the pages jemalloc reserves344      * 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 taking347      * 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 #else373 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 #endif390 391 /* Get the sum of the specified field (converted form kb to bytes) in392  * /proc/self/smaps. The field must be specified with trailing ":" as it393  * 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 the397  * 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 #else430 size_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {431     ((void) field);432     ((void) pid);433     return 0;434 }435 #endif436 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_system446  *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 #endif465     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 #endif484     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 #else490     return 0L;          /* Unknown method to get the data. */491 #endif492 #else493     return 0L;          /* Unknown OS. */494 #endif495 }496 497 #ifdef REDIS_TEST498 #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" ] then13     # 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 fi18 19 # 測驗 release.h 檔案是否存在, 不存在就創建20 test -f release.h || touch release.h21 # 判斷 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-date24 25 # release.h 寫入宏配置 REDIS_GIT_SHA1 REDIS_GIT_DIRTY REDIS_BUILD_ID26 echo "#define REDIS_GIT_SHA1 \"$GIT_SHA1\"" > release.h27 echo "#define REDIS_GIT_DIRTY \"$GIT_DIRTY\"" >> release.h28 echo "#define REDIS_BUILD_ID \"$BUILD_ID\"" >> release.h29 30 # touch 更新 檔案訪問時間 atime 檔案內容更改時間 mtime 檔案狀態改動時間 ctime31 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-settings102 103 # 以 Linux jemalloc 為例104 # FINAL_CFLAGS=-std=c11 -pedantic -DREDIS_STATIC='' -Wall -W -Wno-missing-field-initializers -O2 -g -ggdb 105 # FINAL_LDFLAGS= -g -ggdb106 # -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=-lm112 DEBUG=-g -ggdb113 114 ifeq ($(uname_S),SunOS)115     # SunOS116         ifneq ($(@@),32bit)117         CFLAGS+= -m64118         LDFLAGS+= -m64119     endif120     DEBUG=-g121     DEBUG_FLAGS=-g122     export CFLAGS LDFLAGS DEBUG DEBUG_FLAGS123     INSTALL=cp -pf124     FINAL_CFLAGS+= -D__EXTENSIONS__ -D_XPG6125     FINAL_LIBS+= -ldl -lnsl -lsocket -lresolv -lpthread -lrt126 else127 ifeq ($(uname_S),Darwin)128     # Darwin129     FINAL_LIBS+= -ldl130     OPENSSL_CFLAGS=-I/usr/local/opt/openssl/include131     OPENSSL_LDFLAGS=-L/usr/local/opt/openssl/lib132 else133 ifeq ($(uname_S),AIX)134         # AIX135         FINAL_LDFLAGS+= -Wl,-bexpall136         FINAL_LIBS+=-ldl -pthread -lcrypt -lbsd137 else138 ifeq ($(uname_S),OpenBSD)139     # OpenBSD140     FINAL_LIBS+= -lpthread141     ifeq ($(USE_BACKTRACE),yes)142         FINAL_CFLAGS+= -DUSE_BACKTRACE -I/usr/local/include143         FINAL_LDFLAGS+= -L/usr/local/lib144         FINAL_LIBS+= -lexecinfo145         endif146 147 else148 ifeq ($(uname_S),FreeBSD)149     # FreeBSD150     FINAL_LIBS+= -lpthread -lexecinfo151 else152 ifeq ($(uname_S),DragonFly)153     # FreeBSD154     FINAL_LIBS+= -lpthread -lexecinfo155 else156     # All the other OSes (notably Linux)157     FINAL_LDFLAGS+= -rdynamic158     FINAL_LIBS+=-ldl -pthread -lrt159 endif160 endif161 endif162 endif163 endif164 endif165 # Include paths to dependencies166 FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src167 168 ifeq ($(MALLOC),tcmalloc)169     FINAL_CFLAGS+= -DUSE_TCMALLOC170     FINAL_LIBS+= -ltcmalloc171 endif172 173 ifeq ($(MALLOC),tcmalloc_minimal)174     FINAL_CFLAGS+= -DUSE_TCMALLOC175     FINAL_LIBS+= -ltcmalloc_minimal176 endif177 178 # ifeq ($(MALLOC),jemalloc) $(MALLOC) == jemalloc 為真才進入條件分支179 # DEPENDENCY_TARGETS += jemalloc -> DEPENDENCY_TARGETS = hiredis linenoise lua jemalloc180 # FINAL_CFLAGS 繼續 += -DUSE_JEMALLOC -I../deps/jemalloc/include 引入 USE_JEMALLOC 宏和指定頭檔案路徑181 # FINAL_LIBS := ../deps/jemalloc/lib/libjemalloc.a -lm182 ifeq ($(MALLOC),jemalloc)183     DEPENDENCY_TARGETS+= jemalloc184     FINAL_CFLAGS+= -DUSE_JEMALLOC -I../deps/jemalloc/include185     FINAL_LIBS := ../deps/jemalloc/lib/libjemalloc.a $(FINAL_LIBS)186 endif187 188 # BUILD_TLS -> USE_OPENSSL189 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 -lcrypto193 endif194 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 V219 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 endif223 224 REDIS_SERVER_NAME=redis-server225 REDIS_SENTINEL_NAME=redis-sentinel226 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.o227 REDIS_CLI_NAME=redis-cli228 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.o229 REDIS_BENCHMARK_NAME=redis-benchmark230 REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o siphash.o redis-benchmark.o231 REDIS_CHECK_RDB_NAME=redis-check-rdb232 REDIS_CHECK_AOF_NAME=redis-check-aof233 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 || true245 246 # $(words <text>) 單詞個數統計函式247 # MAKECMDGOALS 會存放你所指定的終極目標的串列,如果在命令列上,你沒有指定目標,那么,這個變數是空值248 # NODEPS:=clean distclean249 ifeq (0, $(words $(findstring $(MAKECMDGOALS), $(NODEPS))))250 -include Makefile.dep251 endif252 253 .PHONY: all254 255 # 構建 .make-settings256 persist-settings: distclean257     echo STD=$(STD) >> .make-settings258     echo WARN=$(WARN) >> .make-settings259     echo OPT=$(OPT) >> .make-settings260     echo MALLOC=$(MALLOC) >> .make-settings261     echo CFLAGS=$(CFLAGS) >> .make-settings262     echo LDFLAGS=$(LDFLAGS) >> .make-settings263     echo REDIS_CFLAGS=$(REDIS_CFLAGS) >> .make-settings264     echo REDIS_LDFLAGS=$(REDIS_LDFLAGS) >> .make-settings265     echo PREV_FINAL_CFLAGS=$(FINAL_CFLAGS) >> .make-settings266     echo PREV_FINAL_LDFLAGS=$(FINAL_LDFLAGS) >> .make-settings267     -(cd ../deps && $(MAKE) $(DEPENDENCY_TARGETS))268 269 .PHONY: persist-settings270 271 # Prerequisites target272 .make-prerequisites:273     @touch $@274 275 # Clean everything, persist settings and build dependencies if anything changed276 ifneq ($(strip $(PREV_FINAL_CFLAGS)), $(strip $(FINAL_CFLAGS)))277 .make-prerequisites: persist-settings278 endif279 280 # $(strip STRINT) 去空格函式281 ifneq ($(strip $(PREV_FINAL_LDFLAGS)), $(strip $(FINAL_LDFLAGS)))282 .make-prerequisites: persist-settings283 endif284 285 # redis-server286 $(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ)287     $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a $(FINAL_LIBS)288 289 # redis-sentinel290 $(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME)291     $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME)292 293 # redis-check-rdb294 $(REDIS_CHECK_RDB_NAME): $(REDIS_SERVER_NAME)295     $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_RDB_NAME)296 297 # redis-check-aof298 $(REDIS_CHECK_AOF_NAME): $(REDIS_SERVER_NAME)299     $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME)300 301 # redis-cli302 $(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)303     $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS)304 305 # redis-benchmark306 $(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.c310     $(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 of314 # depending on a single artifact, build all dependencies first.315 %.o: %.c .make-prerequisites316     $(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-benchmark320 321 .PHONY: clean322 323 distclean: clean324     -(cd ../deps && $(MAKE) distclean)325     -(rm -f .make-*)326 327 .PHONY: distclean328 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: test336 337 lcov:338     $(MAKE) gcov339     @(set -e; cd ..; ./runtest --clients 1)340     @geninfo -o redis.info .341     @genhtml --legend -o lcov-html redis.info342 343 test-sds: sds.c sds.h344     $(REDIS_CC) sds.c zmalloc.c -DSDS_TEST_MAIN $(FINAL_LIBS) -o /tmp/sds_test345     /tmp/sds_test346 347 .PHONY: lcov348 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.h375 376 install: all377     @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,  如果你與人交談,能保持風度,  伴王同行,能保持距離;  如果仇敵和好友都不害你;  如果所有人都指望你,卻無人全心全意;  如果你花六十秒進行短程跑,  填滿那不可饒恕的一分鐘——  你就可以擁有一個世界,  這個世界的一切都是你的,  更重要的是,孩子,你是個頂天立地的人,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/61754.html

標籤:C

上一篇:C學習筆記(9)--- 前處理器,頭檔案

下一篇:C Primer Plus 第六版—— 6.16 編程練習題(附代碼)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more