CppGuide社区 CppGuide社区
首页
  • 🔥最新谷歌C++风格指南(含C++17/20)
  • 🔥C++17详解
  • 🔥C++20完全指南
  • 🔥C++23快速入门
  • C++语言面试问题集锦
  • 🔥交易系统开发岗位求职与面试指南 (opens new window)
  • 第1章 高频C++11重难点知识解析
  • 第2章 Linux GDB高级调试指南
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 高性能网络通信协议设计精要
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 后端服务重要模块设计探索
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 源码分析系列

    • leveldb源码分析
    • libevent源码分析
    • Memcached源码分析
    • TeamTalk源码分析
    • 优质源码分享 (opens new window)
    • 🔥远程控制软件gh0st源码分析
  • 从零手写C++项目系列

    • 🔥C++游戏编程入门(零基础学C++)
    • 🔥使用C++17从零开发一个调试器 (opens new window)
    • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
    • 🔥使用C++从零写一个C语言编译器 (opens new window)
    • 🔥从零用C语言写一个Redis
  • 🔥Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • Go语言特性

    • Go系统接口编程
    • 高效Go并发编程
    • Go性能调优
    • Go项目架构设计
  • Go项目实战

    • 🔥使用Go从零开发一个数据库
    • 🔥使用Go从零开发一个编译器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥用Go从零写一个编排器(类Kubernetes) (opens new window)
Rust编程指南
  • SQL零基础指南
  • MySQL开发与调试指南
GitHub (opens new window)
首页
  • 🔥最新谷歌C++风格指南(含C++17/20)
  • 🔥C++17详解
  • 🔥C++20完全指南
  • 🔥C++23快速入门
  • C++语言面试问题集锦
  • 🔥交易系统开发岗位求职与面试指南 (opens new window)
  • 第1章 高频C++11重难点知识解析
  • 第2章 Linux GDB高级调试指南
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 高性能网络通信协议设计精要
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 后端服务重要模块设计探索
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 源码分析系列

    • leveldb源码分析
    • libevent源码分析
    • Memcached源码分析
    • TeamTalk源码分析
    • 优质源码分享 (opens new window)
    • 🔥远程控制软件gh0st源码分析
  • 从零手写C++项目系列

    • 🔥C++游戏编程入门(零基础学C++)
    • 🔥使用C++17从零开发一个调试器 (opens new window)
    • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
    • 🔥使用C++从零写一个C语言编译器 (opens new window)
    • 🔥从零用C语言写一个Redis
  • 🔥Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • Go语言特性

    • Go系统接口编程
    • 高效Go并发编程
    • Go性能调优
    • Go项目架构设计
  • Go项目实战

    • 🔥使用Go从零开发一个数据库
    • 🔥使用Go从零开发一个编译器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥用Go从零写一个编排器(类Kubernetes) (opens new window)
Rust编程指南
  • SQL零基础指南
  • MySQL开发与调试指南
GitHub (opens new window)
  • 服务器资源调整
  • 初始化参数解析
  • 网络监听的建立
  • 网络连接建立
  • 内存初始化
  • 资源初始化
  • get过程
  • cas属性
  • 内存池
  • 连接队列
  • Hash表操作
  • LRU操作
  • set操作
  • do_item_alloc操作
  • item结构
  • Hash表扩容
  • 线程交互
  • 状态机
  • Memcached源码分析
zhangxf
2023-04-02

内存初始化

# Memcached源码阅读四 内存初始化

Memcached作为内存cache服务器,内存高效管理是其最重要的任务之一,Memcached使用SLAB管理其内存,SLAB内存管理直观的解释就是分配一块大的内存,之后按不同的块(48byte, 64byte, … 1M)等切分这些内存,存储业务数据时,按需选择合适的内存空间存储数据。

Memcached首次默认分配64M的内存,之后所有的数据都是在这64M空间进行存储,在Memcached启动之后,不会对这些内存执行释放操作,这些内存只有到Memcached进程退出之后会被系统回收,下面分析下Memcached的内存初始化过程。

//内存初始化,settings.maxbytes是Memcached初始启动参数指定的内存值大小,settings.factor是内存增长因子
slabs_init(settings.maxbytes, settings.factor, preallocate);

#define POWER_SMALLEST 1   //最小slab编号
#define POWER_LARGEST  200 //首次初始化200个slab

//实现内存池管理相关的静态全局变量

static size_t mem_limit = 0;//总的内存大小
static size_t mem_malloced = 0;//初始化内存的大小,这个貌似没什么用
static void *mem_base = NULL;//指向总的内存的首地址
static void *mem_current = NULL;//当前分配到的内存地址
static size_t mem_avail = 0;//当前可用的内存大小

static slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES];//定义slab结合,总共200个

void slabs_init(const size_t limit, const double factor, const bool prealloc) {

    int i = POWER_SMALLEST - 1;
    //size表示申请空间的大小,其值由配置的chunk_size和单个item的大小来指定
    unsigned int size = sizeof(item) + settings.chunk_size;

    mem_limit = limit;//mem_limit是全局变量

    if (prealloc) {
           //支持预分配
        mem_base = malloc(mem_limit);//申请地址,mem_base指向申请的地址

        if (mem_base != NULL) {
              //mem_current指向当前地址
            mem_current = mem_base;
            //可用内存大小为mem_limit
               mem_avail = mem_limit;
        } else {
            //支持预分配失败
            fprintf(stderr, "Warning: Failed to allocate requested memory in"
                    " one large chunk.\nWill allocate in smaller chunks\n");
        }
    }

    //置空slabclass数组
    memset(slabclass, 0, sizeof(slabclass));
    //开始分配,i<200 && 单个chunk的size<单个item最大大小/内存增长因子

    while (++i < POWER_LARGEST && size <= settings.item_size_max / factor) {

        //size执行8byte对齐
        if (size % CHUNK_ALIGN_BYTES)
            size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);

        slabclass[i].size = size;
        //slab对应chunk的大小
        slabclass[i].perslab = settings.item_size_max / slabclass[i].size;
       //slab对应的chunk的个数
        size *= factor;
       //size下一个值为按增长因子的倍数增长

        if (settings.verbose > 1) {
            //如果有打开调试信息,则输出调试信息
            fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
                    i, slabclass[i].size, slabclass[i].perslab);
        }
    }

    //循环结束时,size已经增长到1M
    power_largest = i;//再增加一个slab
    slabclass[power_largest].size = settings.item_size_max;
    //slab的size为item_size_max
    slabclass[power_largest].perslab = 1;//chunk个数为1

    //打印调试信息
    if (settings.verbose > 1) {
        fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
                i, slabclass[i].size, slabclass[i].perslab);
    }

    //读取环境变量T_MEMD_INITIAL_MALLOC的值    
    {
        char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");
        if (t_initial_malloc) {
            mem_malloced = (size_t)atol(t_initial_malloc);
        }

    }

    if (prealloc) {
        //分配每个slab的内存空间,传入最大已经初始化的最大slab编号
        slabs_preallocate(power_largest);
    }
}

//分配每个slab的内存空间
static void slabs_preallocate (const unsigned int maxslabs) {
    int i;
    unsigned int prealloc = 0;

    for (i = POWER_SMALLEST; i <= POWER_LARGEST; i++) {
        if (++prealloc > maxslabs)
            return;
        
        //执行分配操作,对第i个slabclass执行分配操作
          if (do_slabs_newslab(i) == 0) {
            fprintf(stderr, "Error while preallocating slab memory!\n"
                "If using -L or other prealloc options, max memory must be "
                "at least %d megabytes.\n", power_largest);
            exit(1);
        }
    }
}

//执行分配操作
static int do_slabs_newslab(const unsigned int id) {

    slabclass_t *p = &slabclass[id];//p指向第i个slabclass
    int len = settings.slab_reassign ? settings.item_size_max:p->size*p->perslab;
    char *ptr;
    
   
       //grow_slab_list初始化slabclass的slab_list,而slab_list中的指针指向每个slab
    //memory_allocate从内存池申请1M的空间
    if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) ||
        (grow_slab_list(id) == 0) ||
          ((ptr = memory_allocate((size_t)len)) == 0)) {
        MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id);
        return 0;
    }

    memset(ptr, 0, (size_t)len);
    //将申请的1M空间按slabclass的size进行切分
    split_slab_page_into_freelist(ptr, id);

    p->slab_list[p->slabs++] = ptr;//循环分配
    mem_malloced += len;//增加已经分配出去的内存数
    MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id);

    return 1;
}

//初始化slabclass的slab_class,而slab_list中的指针指向每个slab,id为slabclass的序号
static int grow_slab_list (const unsigned int id) {

    slabclass_t *p = &slabclass[id];
       //p指向第id个slabclass;
    
       if (p->slabs == p->list_size) {
        size_t new_size =  (p->list_size != 0) ? p->list_size * 2 : 16;//new_size如果是首次分配,则取16,否则按旧值的2倍扩容
        void *new_list = realloc(p->slab_list, new_size * sizeof(void *));//申请空间,这个空间是从系统分配,不是从内存池分配
        if (new_list == 0) return 0;
        p->list_size = new_size;//修改第id个slabclass的值
        p->slab_list = new_list;
    }
    return 1;
} 

//从内存池分配size个空间
static void *memory_allocate(size_t size) {
    void *ret;

    if (mem_base == NULL) {//如果内存池没创建,则从系统分配
        ret = malloc(size);
    } else {
        ret = mem_current;
        //size大于剩余的空间
        if (size > mem_avail) {
            return NULL;
        }

        //按8字节对齐
        if (size % CHUNK_ALIGN_BYTES) {
            size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
        }
        //扣除size个空间
        mem_current = ((char*)mem_current) + size;
        if (size < mem_avail) {
            mem_avail -= size;//更新剩余空间大小
        } else {
            mem_avail = 0;
        }
    }

    return ret;
}

//将ptr指向的内存空间按第id个slabclass的size进行切分
static void split_slab_page_into_freelist(char *ptr, const unsigned int id) {

    slabclass_t *p = &slabclass[id];
    int x;
    //每个slabclass有多个slab,对每个slab按slabclass对应的size进行切分

    for (x = 0; x < p->perslab; x++) {
        do_slabs_free(ptr, 0, id);//创建空闲item
        ptr += p->size;
    }
} 

//创建空闲item
static void do_slabs_free(void *ptr, const size_t size, unsigned int id)
 {
    slabclass_t *p;
    item *it;

    assert(((item *)ptr)->slabs_clsid == 0);
    assert(id >= POWER_SMALLEST && id <= power_largest);//判断id有效性
    if (id < POWER_SMALLEST || id > power_largest)
        return;

    MEMCACHED_SLABS_FREE(size, id, ptr);
    p = &slabclass[id];

    it = (item *)ptr;
    it->it_flags |= ITEM_SLABBED;
    it->prev = 0;
    it->next = p->slots;//挂载到slabclass的空闲链表中
    if (it->next) it->next->prev = it;
    p->slots = it;

    p->sl_curr++;//空闲item个数+1
    p->requested -= size;//已经申请到的空间数量更新
    return;
}
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
编辑 (opens new window)
上次更新: 2023/12/11, 22:32:09
网络连接建立
资源初始化

← 网络连接建立 资源初始化→

最近更新
01
第二章 关键字static及其不同用法
03-27
02
第一章 auto与类型推导
03-27
03
C++语言面试问题集锦 目录与说明
03-27
更多文章>
Copyright © 2024-2025 沪ICP备2023015129号 张小方 版权所有
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式