• C基础 带你手写 redis adlist 双向链表


    引言 - 导航栏目

      有些朋友可能对 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 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 双向链表的头文件, 对于这个头文件有几点要聊一聊的. 

    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 周边

       简单愉快的背后总会有些更深的不可捉摸. 离开了奶头乐, 我们将从 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 = '';
    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 = '';
    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 #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
    ",
     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 = '';
    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 = '';
    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
    ", zmalloc_used_memory());
    505     ptr = zmalloc(123);
    506     printf("Allocated 123 bytes; used: %zu
    ", zmalloc_used_memory());
    507     ptr = zrealloc(ptr, 456);
    508     printf("Reallocated to 456 bytes; used: %zu
    ", zmalloc_used_memory());
    509     zfree(ptr);
    510     printf("Freed pointer; used: %zu
    ", 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="33[34m"        蓝色字
    200 # LINKCOLOR="33[34;1m"    蓝色字 高亮
    201 # SRCCOLOR="33[33m"        黄色字
    202 # BINCOLOR="33[37;1m"        白色字 高亮
    203 # MAKECOLOR="33[32;1m"    绿色字 高亮
    204 # ENDCOLOR="33[0m"        关闭所有属性
    205 CCCOLOR="33[34m"
    206 LINKCOLOR="33[34;1m"
    207 SRCCOLOR="33[33m"
    208 BINCOLOR="33[37;1m"
    209 MAKECOLOR="33[32;1m"
    210 ENDCOLOR="33[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
    ' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR) 1>&2;
    220 QUIET_LINK = @printf '    %b %b
    ' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2;
    221 QUIET_INSTALL = @printf '    %b %b
    ' $(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,
      如果你与人交谈,能保持风度,
      伴王同行,能保持距离;
      如果仇敌和好友都不害你;
      如果所有人都指望你,却无人全心全意;
      如果你花六十秒进行短程跑,
      填满那不可饶恕的一分钟——
      你就可以拥有一个世界,
      这个世界的一切都是你的,
      更重要的是,孩子,你是个顶天立地的人。
  • 相关阅读:
    java核心技术记录之集合
    Set的非重复判断是根据什么判断的
    struts2-2.3.4.1的struts-default.xml源码
    纯代码搭建项目框架
    初始化项目
    项目部署
    使用estimatedRowHeight的优缺点
    懒加载
    闭包
    Swift 类的构造函数
  • 原文地址:https://www.cnblogs.com/life2refuel/p/11911839.html
Copyright © 2020-2023  润新知