引言 - 导航栏目
有些朋友可能对 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 = '