内存分配是Redis代码中底层的部分,Redis实现的数据结构String,List,Set等都需要该部分实现对内存的管理。

文件依赖

主程序: zmalloc.c 头文件: config.h zmalloc.h

详细分析

1. config.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#ifndef __CONFIG_H
#define __CONFIG_H

#ifdef __APPLE__
#include <AvailabilityMacros.h>
#endif

/* test for malloc_size() */
#ifdef __APPLE__
#include <malloc/malloc.h>
#define HAVE_MALLOC_SIZE 1
#define redis_malloc_size(p) malloc_size(p)
#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 backtrace() */
#if defined(__APPLE__) || defined(__linux__)
#define HAVE_BACKTRACE 1
#endif

#endif

1、2、29行是头文件保护,防止头文件中定义或声明的变量或函数被多次包含。
剩下的部分通过测试编译器“预定义宏”来定义不同的宏,实现redis能同时MacOS和linux(至于能不能运行于MacOS我也不太清楚,没条件进行测试)。
在此需要特别说明的是”defined”关键字,出现在16行和25行,实际上“#if defind MACRO”和“#ifdef MACRO”还有“#if defined(MACRO)”是等价的,不过“#if defind MACRO”和“#if defined(MACRO)”相对于“#ifdef MACRO”来说,可以进行关系运算,就像16行和25行那样。
4-6行是为了能在MacOS下运行包含了特殊的头文件“AvailabilityMacros.h”,这个头文件定义了一些在MacOS下可用的宏。
9-13行也是MacOS的特别设定,malloc_size函数的地址

2. zmalloc.h

1
2
3
4
5
6
7
8
9
10
#ifndef _ZMALLOC_H
#define _ZMALLOC_H

void *zmalloc(size_t size);
void *zrealloc(void *ptr, size_t size);
void zfree(void *ptr);
char *zstrdup(const char *s);
size_t zmalloc_used_memory(void);

#endif /* _ZMALLOC_H */

这个文件没什么好说的,就是几个函数的声明。

3. zmalloc.c(内存分配的主程序)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/* zmalloc - total amount of allocated memory aware version of malloc()
*
* Copyright (c) 2006-2009, 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.
*/


#include <stdlib.h>
#include <string.h>
#include "config.h"

static size_t used_memory = 0;

void *zmalloc(size_t size) {
void *ptr = malloc(size+sizeof(size_t));

if (!ptr) return NULL;
#ifdef HAVE_MALLOC_SIZE
used_memory += redis_malloc_size(ptr);
return ptr;
#else
*((size_t*)ptr) = size;
used_memory += size+sizeof(size_t);
return (char*)ptr+sizeof(size_t);
#endif
}

void *zrealloc(void *ptr, size_t size) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
#endif
size_t oldsize;
void *newptr;

if (ptr == NULL) return zmalloc(size);
#ifdef HAVE_MALLOC_SIZE
oldsize = redis_malloc_size(ptr);
newptr = realloc(ptr,size);
if (!newptr) return NULL;

used_memory -= oldsize;
used_memory += redis_malloc_size(newptr);
return newptr;
#else
realptr = (char*)ptr-sizeof(size_t);
oldsize = *((size_t*)realptr);
newptr = realloc(realptr,size+sizeof(size_t));
if (!newptr) return NULL;

*((size_t*)newptr) = size;
used_memory -= oldsize;
used_memory += size;
return (char*)newptr+sizeof(size_t);
#endif
}

void zfree(void *ptr) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
size_t oldsize;
#endif

if (ptr == NULL) return;
#ifdef HAVE_MALLOC_SIZE
used_memory -= redis_malloc_size(ptr);
free(ptr);
#else
realptr = (char*)ptr-sizeof(size_t);
oldsize = *((size_t*)realptr);
used_memory -= oldsize+sizeof(size_t);
free(realptr);
#endif
}

char *zstrdup(const char *s) {
size_t l = strlen(s)+1;
char *p = zmalloc(l);

memcpy(p,s,l);
return p;
}

size_t zmalloc_used_memory(void) {
return used_memory;
}

定义了一个全局的静态变量used_memory来定义当前已经使用了多少内存。

1> zmallloc(37-49行)

参数:size_t size
返回值:void *
功能:分配size+sizeof(size_t)大小的内存。
41-43行是为MacOS编写的程序,没用过苹果的系统,实在是不懂,所以暂且不讨论。
38行调用malloc函数分配 size + sizeof(size_t) 大小的内存,出错返回NULL。多分配的 sizeof(size_t) 字节用来存储size,即函数调用者申请的内存大小,此时的ptr和ptr1指向的内存地址是一样的。

45行将ptr指向的 sizeof(size_t) 大小的内存存储 size。
46行更新used_memory的大小。
返回值是 (char*)ptr+sizeof(size_t) ,返回的是ptr2指向的内存地址。

2> zrealloc (51-78行)

参数: void *ptr(要操作的内存的首地址) size_t size(将内存调整为size大小)
返回值:正常返回新的内存地址,出错返回NULL
功能:将ptr指向的内存调整为size大小
以下只考虑为linux运行编写的代码,以后也是这样,macos的代码暂不考虑,这应该对理解整个系统没有影响。
68行使用realptr取得真实的内存地址,即ptr1。 69行取得ptr指向的内存实际使用的大小,即size,存储到oldsize。
70行调用realloc函数重新分配内存,如果出错,返回NULL。
73-76行更新变量,类似于zmalloc函数的处理方法。

3>zfree (80-96行)

参数:void *ptr (指向要操作的内存的首地址)
返回值:无返回值
功能:释放ptr指向的内存
91行取得真实的内存地址,和ptr1指向的内存地址相同,存储到realptr中。
93行更新used_memory的值。
最后调用free函数释放掉realptr指向的内存。

4>zstrdup (98-104行)

参数:const char s
返回值:char

功能: 将s指向的字符串复制到p中。
分配的内存大小为strlen(s) +1,包含了字符串结尾的’\0’
102行调用memcpy。

5>zmalloc_used_memory (106-108行)

参数:无参数
返回值:used_memory
功能:返回当前已经申请的内存的大小
(结束)