目录
  1. 1. redis源码阅读-zmalloc(内存分配)
    1. 1.1. zmalloc介绍
    2. 1.2. zmalloc.h
    3. 1.3. zmalloc.c
redis源码阅读-zmalloc(内存分配)

redis源码阅读-zmalloc(内存分配)

zmalloc介绍

  • redis支持使用tcmalloc,jemalloc内存分配器分配内存,也支持操作系统平台提供的内存分配库。
  • zmalloc是redis封装的内存分配函数。redis将多种内存分配方法封装为统一的接口zmalloc,让其统一对外提供功能,避免跨平台不兼容问题。

zmalloc.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
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
/* zmalloc - total amount of allocated memory aware version of malloc()
*
* Copyright (c) 2009-2010, 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 __ZMALLOC_H
#define __ZMALLOC_H
/* Double expansion needed for stringification of macro values. */
// 使用宏对象双向链表进行细化扩展
#define __xstr(s) __str(s)
#define __str(s) #s // 给s加上双引号""; #@s(给s加单引号); s##s(连接两个字符串)
// 这段对内存分配器的定义,是为了将内存分配器抽象为zmalloc,不用管内存分配器的基础库是什么
#if defined(USE_TCMALLOC) // 使用tcmalloc内存分配器(还有一个jemalloc)
#define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) // 定义zmalloc库版本(tcmalloc-主版本-小版本)
#include <google/tcmalloc.h>
#if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1) // 如果tcmalloc主版本等于1且小版本大于等于6或者是主版本大于1,就将tc_malloc_size重命名为zmalloc_size函数,否则tcmalloc版本太旧
#define HAVE_MALLOC_SIZE 1 // 表示存在malloc_size函数
#define zmalloc_size(p) tc_malloc_size(p) // 定义tzmalloc_size(p)为zmalloc_size(p)
#else
#error "Newer version of tcmalloc required"
#endif
#elif defined(USE_JEMALLOC) // 如果用的是jemalloc内存分配器
#define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX)) // 定义zmalloc库版本(jemalloc-主版本-小版本-bug修复版本)
#include <jemalloc/jemalloc.h>
#if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)
#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) je_malloc_usable_size(p)
#else
#error "Newer version of jemalloc required"
#endif
#elif defined(__APPLE__) // 如果是mac平台
#include <malloc/malloc.h>
#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) malloc_size(p)
#endif
#ifndef ZMALLOC_LIB // 如果没有内存分配器被定义,则使用原生malloc
#define ZMALLOC_LIB "libc"
#ifdef __GLIBC__
#include <malloc.h>
#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) malloc_usable_size(p)
#endif
#endif
/* We can enable the Redis defrag capabilities only if we are using Jemalloc
* and the version used is our special version modified for Redis having
* the ability to return per-allocation fragmentation hints. */
// 用jemalloc是会定义HAVE_DEFRAG
#if defined(USE_JEMALLOC) && defined(JEMALLOC_FRAG_HINT)
#define HAVE_DEFRAG
#endif
void *zmalloc(size_t size); // 分配长度为size的内存(需要手动计算分配空间大小),不会设置初始值,效率高
void *zcalloc(size_t size); // 并不需要人为计算分配空间大小,会给每一个分配的空间设置初始值,效率比较低
void *zrealloc(void *ptr, size_t size); // 动态内存扩容,ptr指向原来的空间地址,size是需要分配的空间大小
void zfree(void *ptr); // 释放被分配的内存
char *zstrdup(const char *s); // 字符串复制
size_t zmalloc_used_memory(void); // 分配被用的内存
void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); // 设置内存用完了的句柄
// 获取实际使用的物理内存
// VSS:Virtual Set Size 虚拟耗用的内存(包含与其他进程共享占用的虚拟内存)
// RSS:Resident Set Size 实际使用的物理内存(包含与其他进程共享占用的内存)
// PSS:Proportional Set Size 实际使用的物理内存(按比例包含与其他进程共享占用的内存)
// USS:Unique Set Size 进程独自占用的物理内存(不包含与其他进程共享占用的内存)
size_t zmalloc_get_rss(void);
// 获取被分配的内存信息
int zmalloc_get_allocator_info(size_t *allocated, size_t *active, size_t *resident);
size_t zmalloc_get_private_dirty(long pid); // 通过进程pid获取私有脏数据
size_t zmalloc_get_smap_bytes_by_field(char *field, long pid); // 通过字段和pid获取
size_t zmalloc_get_memory_size(void); // 获取内存长度
void zlibc_free(void *ptr); // 释放内存
#ifdef HAVE_DEFRAG // 如果使用的是jemalloc
void zfree_no_tcache(void *ptr);
void *zmalloc_no_tcache(size_t size);
#endif
#ifndef HAVE_MALLOC_SIZE
size_t zmalloc_size(void *ptr);
size_t zmalloc_usable(void *ptr);
#else
#define zmalloc_usable(p) zmalloc_size(p)
#endif
#ifdef REDIS_TEST // 如果定义了redis测试
int zmalloc_test(int argc, char **argv);
#endif
#endif /* __ZMALLOC_H */

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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
/* zmalloc - total amount of allocated memory aware version of malloc()
*
* Copyright (c) 2009-2010, 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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
/* This function provide us access to the original libc free(). This is useful
* for instance to free results obtained by backtrace_symbols(). We need
* to define this function before including zmalloc.h that may shadow the
* free implementation if we use jemalloc or another non standard allocator. */
// 这个方法提供给我们的是libc原生的free函数,通过backtrace_symbols()函数释放栈
void zlibc_free(void *ptr) {
free(ptr);
}
#include <string.h>
#include <pthread.h>
#include "config.h"
#include "zmalloc.h" // 提供内存操作函数原型
#include "atomicvar.h" // 原子性(线程安全)
// 如果定义了HAVE_MALLOC_SIZE,表示使用了tcmalloc或mac, 不需要记录申请内存大小
// 如果没有定义HAVE_MALLOC_SIZE,表示使用了linux或sun,需要记录申请内存大小
#ifdef HAVE_MALLOC_SIZE // tcmalloc 和 Mac平台下的 malloc 函数族提供了计算已分配空间大小的函数(分别是tcmallocsize和mallocsize),所以就不需要单独分配一段空间记录大小了
#define PREFIX_SIZE (0)
#else
// sun平台和linux没有提供计算分配空间大小的函数,需要一段初始的空间来记录当前申请的内存空间大小(PREFIX_SIZE)
#if defined(__sun) || defined(__sparc) || defined(__sparc__)
#define PREFIX_SIZE (sizeof(long long)) // sun平台使用sizeof(long long)定长字段记录
#else
#define PREFIX_SIZE (sizeof(size_t)) // linux使用sizeof(size_t)定长字段记录
#endif
#endif
/* Explicitly override malloc/free etc when using tcmalloc. */
// 如果用tcmalloc,直接重写malloc/free等原生函数
#if defined(USE_TCMALLOC)
#define malloc(size) tc_malloc(size)
#define calloc(count,size) tc_calloc(count,size)
#define realloc(ptr,size) tc_realloc(ptr,size)
#define free(ptr) tc_free(ptr)
#elif defined(USE_JEMALLOC)
#define malloc(size) je_malloc(size)
#define calloc(count,size) je_calloc(count,size)
#define realloc(ptr,size) je_realloc(ptr,size)
#define free(ptr) je_free(ptr)
#define mallocx(size,flags) je_mallocx(size,flags)
#define dallocx(ptr,flags) je_dallocx(ptr,flags)
#endif
#define update_zmalloc_stat_alloc(__n) do { \
size_t _n = (__n); \
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
atomicIncr(used_memory,__n); \
} while(0)
#define update_zmalloc_stat_free(__n) do { \
size_t _n = (__n); \
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
atomicDecr(used_memory,__n); \
} while(0)
// 初始化被用内存
static size_t used_memory = 0;
// 被用内存的线程互斥量,用于多线程中的线程安全
pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;
// 默认的内存溢出函数
static void zmalloc_default_oom(size_t size) {
fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",
size);
fflush(stderr);
abort();
}
// 默认的内存溢出函数指针,用于zmalloc_set_oom_handler函数
static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;
// 将malloc函数包装 , 空类型指针,并不是返回空,在这里表示可以返回任何类型的指针
void *zmalloc(size_t size) {
void *ptr = malloc(size+PREFIX_SIZE); // 先分配内存
if (!ptr) zmalloc_oom_handler(size); // 如果分配的内存为空,表示内存溢出
#ifdef HAVE_MALLOC_SIZE // 如果定义了HAVE_MALLOC_SIZE,表示使用了tcmalloc或mac , 不需要PREFIX_SIZE,直接获取ptr中的内存大小
update_zmalloc_stat_alloc(zmalloc_size(ptr)); // 获取分配的内存信息(size+0)
return ptr; // 只返回申请的内存空间
#else // 没有则使用原生的
*((size_t*)ptr) = size; // 为指针重新赋值为size
update_zmalloc_stat_alloc(size+PREFIX_SIZE); // size+sizeof(long long) 或者 size+sizeof(size_t)
return (char*)ptr+PREFIX_SIZE; // 返回分配的内存空间+记录申请内存空间的空间
#endif
}
/* Allocation and free functions that bypass the thread cache
* and go straight to the allocator arena bins.
* Currently implemented only for jemalloc. Used for online defragmentation. */
// 仅用jemalloc实现,被用于碎片整理
#ifdef HAVE_DEFRAG
void *zmalloc_no_tcache(size_t size) {
void *ptr = mallocx(size+PREFIX_SIZE, MALLOCX_TCACHE_NONE);
if (!ptr) zmalloc_oom_handler(size);
update_zmalloc_stat_alloc(zmalloc_size(ptr));
return ptr;
}
void zfree_no_tcache(void *ptr) {
if (ptr == NULL) return;
update_zmalloc_stat_free(zmalloc_size(ptr));
dallocx(ptr, MALLOCX_TCACHE_NONE);
}
#endif
// 为分配的内存设初始值 calloc函数的包装
void *zcalloc(size_t size) {
void *ptr = calloc(1, size+PREFIX_SIZE);
if (!ptr) zmalloc_oom_handler(size);
#ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_alloc(zmalloc_size(ptr));
return ptr;
#else
*((size_t*)ptr) = size;
update_zmalloc_stat_alloc(size+PREFIX_SIZE);
return (char*)ptr+PREFIX_SIZE;
#endif
}
// 动态分配内存 realloc函数的包装
// 分配内存步骤
// 1. 判断原内存有没有,如果没有,则使用malloc进行分配
// 2. 如果有,先将老内存+新指定的内存,然后释放老内存,重新分配新内存
void *zrealloc(void *ptr, size_t size) {
#ifndef HAVE_MALLOC_SIZE // 如果没定义,则是使用sun和linux平台
void *realptr;
#endif
size_t oldsize;
void *newptr;
if (ptr == NULL) return zmalloc(size); // 如果原来分配的内存为空,就直接使用malloc分配size
#ifdef HAVE_MALLOC_SIZE // PREFIX_SIZE=0,不需要记录分配内存大小
oldsize = zmalloc_size(ptr);
newptr = realloc(ptr,size);
if (!newptr) zmalloc_oom_handler(size);
update_zmalloc_stat_free(oldsize);
update_zmalloc_stat_alloc(zmalloc_size(newptr));
return newptr;
#else // PREFIX_SIZE 需要记录分配内存大小
realptr = (char*)ptr-PREFIX_SIZE; // 已分配的内存需要将记录大小的空间去掉,才是真实的分配的内存
oldsize = *((size_t*)realptr); // 这才是原来的被分配的内存
newptr = realloc(realptr,size+PREFIX_SIZE); // 扩充szie大小的内存+PREFIX_SIZE(记录大小)
if (!newptr) zmalloc_oom_handler(size);
*((size_t*)newptr) = size;
update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
update_zmalloc_stat_alloc(size+PREFIX_SIZE);
return (char*)newptr+PREFIX_SIZE;
#endif
}
/* Provide zmalloc_size() for systems where this function is not provided by
* malloc itself, given that in that case we store a header with this
* information as the first bytes of every allocation. */
// 获取分配的总内存
#ifndef HAVE_MALLOC_SIZE // 如果没有定义,则需要有记录申请空间大小
size_t zmalloc_size(void *ptr) {
void *realptr = (char*)ptr-PREFIX_SIZE;
size_t size = *((size_t*)realptr);
/* Assume at least that all the allocations are padded at sizeof(long) by
* the underlying allocator. */
// 按位与操作,判断是否分配的是整long型的字节,如果有字节不能填充long型,则使用size += sizeof(long)-(size&(sizeof(long)-1))将剩余的字节填充为long型
// 总是分配整个long型字节数,将分配的字节填充为long型 ; 例如,size=63(0011 1111)=sizeof(long)-1 , 就是一个long型size&(sizeof(long)-1)=0,表示为假条件
// size=65(0100 0001) size&(sizeof(long)-1)=0000 0001 ,为真,进入语句。size = 65+(63-1) = 127(0111 1111) ,这就补充为一个long型
if (size&(sizeof(long)-1)) size += sizeof(long)-(size&(sizeof(long)-1));
return size+PREFIX_SIZE;
}
// 获取分配的可用内存(可用的被分配的内存,减去记录大小的空间)
size_t zmalloc_usable(void *ptr) {
return zmalloc_size(ptr)-PREFIX_SIZE;
}
#endif
// 释放内存
void zfree(void *ptr) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
size_t oldsize;
#endif
if (ptr == NULL) return;
#ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_free(zmalloc_size(ptr));
free(ptr);
#else
realptr = (char*)ptr-PREFIX_SIZE;
oldsize = *((size_t*)realptr);
update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
free(realptr);
#endif
}
// 复制字符串
char *zstrdup(const char *s) {
size_t l = strlen(s)+1; // 字符串是char类型(1个字节,不需要扩充)
char *p = zmalloc(l);
// 使用内存复制函数
// void *memcpy(void *dest, const void *src, size_t n);
// dest : 目标字符串
// src : 需要复制的字符串
// n: 复制字符串的大小
memcpy(p,s,l);
return p;
}
// 获取被用内存(原子计数器值)
size_t zmalloc_used_memory(void) {
size_t um;
atomicGet(used_memory,um); // 获取原子计数器值,存入um
return um;
}
// 设置内存溢出句柄(回调函数)
void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) {
zmalloc_oom_handler = oom_handler;
}
/* Get the RSS information in an OS-specific way.
*
* WARNING: the function zmalloc_get_rss() is not designed to be fast
* and may not be called in the busy loops where Redis tries to release
* memory expiring or swapping out objects.
*
* For this kind of "fast RSS reporting" usages use instead the
* function RedisEstimateRSS() that is a much faster (and less precise)
* version of the function. */
// 通过proc里面的stat文件获取使用内存信息
#if defined(HAVE_PROC_STAT)
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// 获取实际物理内存(不能用于频繁的循环中,比如释放过期内存和交换出对象)
size_t zmalloc_get_rss(void) {
int page = sysconf(_SC_PAGESIZE); // linux系统调用,获取运行时的配置信息,_SC_PAGESIZE是系统定义的内存页大小
size_t rss; // 定义存放实际物理内存大小的变量
char buf[4096];
char filename[256]; // 文件名(linux中文件名长度最大为256)
int fd, count; // 定义文件符和计数
char *p, *x;
//int snprintf(char *str, size_t size, const char *format, ...);
// 将/proc/getpid()/stat这个文件名放入filename中(当前进程产生的进程状态文件)
snprintf(filename,256,"/proc/%d/stat",getpid());
if ((fd = open(filename,O_RDONLY)) == -1) return 0; // 用只读方式打开/proc/getpid()/stat文件
if (read(fd,buf,4096) <= 0) { // 将读取的数据放入buf中
close(fd);
return 0;
}
close(fd);
p = buf; // 让指针p指向数组buf,指针p指向的是一个连续的内存区域
// 从/proc/getpid()/stat中读取的信息
// 1 (init) S 0 1 1 0 -1 4202752 6006 112996409400 582 3211148 242 1303 712327273 324618393 20 0 1 0 4 19828736 170 18446744073709551615 1 1 0 0 0 0 0 4096 536962595 18446744073709551615 0 0 0 1 0 0 380 0 0
count = 23; /* RSS is the 24th field in /proc/<pid>/stat */ // rss是第24个字段
// 循环结束后得到的p就是第24个字段的字符串(rss)
while(p && count--) {
/* char *strchr(const char *s, int c);
const char* s : 输入的字符串
c : 字符串中的字符
return : 返回字符串s中第一个c字符的位置(返回的值是这个位置的后面字符串,包括字符c)
*/
p = strchr(p,' ');
if (p) p++; // 忽略空格,指向空格空面的区域
}
// 如果该字段不为空就不执行return
if (!p) return 0;
x = strchr(p,' '); // 将指针p指向内存区域的第一个空格以及其后面的字符返回给指针x,指针p现在指向的就只有rss段的内存区域
if (!x) return 0;
*x = '\0'; // 将指针x指向的内存区域设置为'\0'
// long int strtol(const char *nptr, char **endptr, int base);
// 将字符串转换为一个长整型(获取数字部分) 10表示10进制
rss = strtoll(p,NULL,10);
rss *= page; // 将分布在多个内存页上的数据相乘,获取实际使用的物理内存
return rss;
}
// 通过TASKINF获取实际使用内存
#elif defined(HAVE_TASKINFO)
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <mach/task.h>
#include <mach/mach_init.h>
// 通过task函数获取
size_t zmalloc_get_rss(void) {
task_t task = MACH_PORT_NULL;
struct task_basic_info t_info;
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS)
return 0;
task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
return t_info.resident_size;
}
#else
// 当我们不能使用操作系统给的方法获取rss,我们就用zmalloc获取分配的内存
size_t zmalloc_get_rss(void) {
/* If we can't get the RSS in an OS-specific way for this system just
* return the memory usage we estimated in zmalloc()..
*
* Fragmentation will appear to be always 1 (no fragmentation)
* of course... */
return zmalloc_used_memory();
}
#endif
// 如果使用jemalloc
#if defined(USE_JEMALLOC)
//
int zmalloc_get_allocator_info(size_t *allocated,
size_t *active,
size_t *resident) {
uint64_t epoch = 1;
size_t sz;
*allocated = *resident = *active = 0;
/* Update the statistics cached by mallctl. */
sz = sizeof(epoch);
je_mallctl("epoch", &epoch, &sz, &epoch, sz);
sz = sizeof(size_t);
/* Unlike RSS, this does not include RSS from shared libraries and other non
* heap mappings. */
je_mallctl("stats.resident", resident, &sz, NULL, 0);
/* Unlike resident, this doesn't not include the pages jemalloc reserves
* for re-use (purge will clean that). */
je_mallctl("stats.active", active, &sz, NULL, 0);
/* Unlike zmalloc_used_memory, this matches the stats.resident by taking
* into account all allocations done by this process (not only zmalloc). */
je_mallctl("stats.allocated", allocated, &sz, NULL, 0);
return 1;
}
void set_jemalloc_bg_thread(int enable) {
/* let jemalloc do purging asynchronously, required when there's no traffic
* after flushdb */
char val = !!enable;
je_mallctl("background_thread", NULL, 0, &val, 1);
}
#else
int zmalloc_get_allocator_info(size_t *allocated,
size_t *active,
size_t *resident) {
*allocated = *resident = *active = 0;
return 1;
}
#endif
/* Get the sum of the specified field (converted form kb to bytes) in
* /proc/self/smaps. The field must be specified with trailing ":" as it
* apperas in the smaps output.
*
* If a pid is specified, the information is extracted for such a pid,
* otherwise if pid is -1 the information is reported is about the
* current process.
*
* Example: zmalloc_get_smap_bytes_by_field("Rss:",-1);
*/
// 通过/proc/getpid()/smaps文件获取内存信息(smaps使用key-value格式,通过":"隔开)
#if defined(HAVE_PROC_SMAPS)
size_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {
char line[1024];
size_t bytes = 0;
int flen = strlen(field);
FILE *fp;
if (pid == -1) {
fp = fopen("/proc/self/smaps","r");
} else {
char filename[128];
snprintf(filename,sizeof(filename),"/proc/%ld/smaps",pid);
fp = fopen(filename,"r");
}
if (!fp) return 0;
while(fgets(line,sizeof(line),fp) != NULL) { // 读取一行,存入line
if (strncmp(line,field,flen) == 0) { // 比较flen长度的字符是否相等,使用strncmp防止内存泄漏 ; 这里是匹配key
char *p = strchr(line,'k'); // 指针p指向"kB"字符串
if (p) {
*p = '\0'; // 将"kB"设置为空
bytes += strtol(line+flen,NULL,10) * 1024; // line表示数组首地址,line+flen表示从line[flen]开始,这过滤掉key,直接获取数字
}
}
}
fclose(fp);
return bytes;
}
#else
// 系统中没有smaps文件,直接置0
size_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {
((void) field);
((void) pid);
return 0;
}
#endif
// 从/proc/getpid()/smaps文件获取字段"Private_Dirty:"的值
size_t zmalloc_get_private_dirty(long pid) {
return zmalloc_get_smap_bytes_by_field("Private_Dirty:",pid);
}
/* Returns the size of physical memory (RAM) in bytes.
* It looks ugly, but this is the cleanest way to achieve cross platform results.
* Cleaned up from:
*
* http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system
*
* Note that this function:
* 1) Was released under the following CC attribution license:
* http://creativecommons.org/licenses/by/3.0/deed.en_US.
* 2) Was originally implemented by David Robert Nadeau.
* 3) Was modified for Redis by Matt Stancliff.
* 4) This note exists in order to comply with the original license.
*/
// 获取物理内存字节数(最适合跨平台使用)
size_t zmalloc_get_memory_size(void) {
#if defined(__unix__) || defined(__unix) || defined(unix) || \
(defined(__APPLE__) && defined(__MACH__))
#if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64))
int mib[2];
mib[0] = CTL_HW;
#if defined(HW_MEMSIZE)
mib[1] = HW_MEMSIZE; /* OSX. --------------------- */
#elif defined(HW_PHYSMEM64)
mib[1] = HW_PHYSMEM64; /* NetBSD, OpenBSD. --------- */
#endif
int64_t size = 0; /* 64-bit */
size_t len = sizeof(size);
if (sysctl( mib, 2, &size, &len, NULL, 0) == 0)
return (size_t)size;
return 0L; /* Failed? */
#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
/* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */
return (size_t)sysconf(_SC_PHYS_PAGES) * (size_t)sysconf(_SC_PAGESIZE);
#elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM))
/* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */
int mib[2];
mib[0] = CTL_HW;
#if defined(HW_REALMEM)
mib[1] = HW_REALMEM; /* FreeBSD. ----------------- */
#elif defined(HW_PHYSMEM)
mib[1] = HW_PHYSMEM; /* Others. ------------------ */
#endif
unsigned int size = 0; /* 32-bit */
size_t len = sizeof(size);
if (sysctl(mib, 2, &size, &len, NULL, 0) == 0)
return (size_t)size;
return 0L; /* Failed? */
#else
return 0L; /* Unknown method to get the data. */
#endif
#else
return 0L; /* Unknown OS. */
#endif
}
#ifdef REDIS_TEST
#define UNUSED(x) ((void)(x))
int zmalloc_test(int argc, char **argv) {
void *ptr;
UNUSED(argc);
UNUSED(argv);
printf("Initial used memory: %zu\n", zmalloc_used_memory());
ptr = zmalloc(123);
printf("Allocated 123 bytes; used: %zu\n", zmalloc_used_memory());
ptr = zrealloc(ptr, 456);
printf("Reallocated to 456 bytes; used: %zu\n", zmalloc_used_memory());
zfree(ptr);
printf("Freed pointer; used: %zu\n", zmalloc_used_memory());
return 0;
}
#endif
文章作者: rack-leen
文章链接: http://yoursite.com/2019/12/16/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB/C/redis/redis%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB-zmalloc-%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 rack-leen's blog
打赏
  • 微信
  • 支付宝

评论