NGINX原理 之 SLAB分配机制(转)

1 引言

  众所周知,操作系统使用伙伴系统管理内存,不仅会造成大量的内存碎片,同时处理效率也较低下。SLAB是一种内存管理机制,其拥有较高的处理效率,同时也有效的避免内存碎片的产生,其核心思想是预分配。其按照SIZE对内存进行分类管理的,当申请一块大小为SIZE的内存时,分配器就从SIZE集合中分配一个内存块(BLOCK)出去,当释放一个大小为SIZE的内存时,则将该内存块放回到原有集合,而不是释放给操作系统。当又要申请相同大小的内存时,可以复用之前被回收的内存块(BLOCK),从而避免了内存碎片的产生。[注:因SLAB处理过程的细节较多,在此只是做一个原理上的讲解]

 

2 总体结构

图1 SLAB内存结构

 

3 处理流程

如图1中所示:SLAB管理机制将内存大体上分为SLAB头、SLOT数组、PAGES数组、可分配空间、被浪费空间等模块进行分别管理,其中各模块的功能和作用:
  • SLAB头:包含SLAB管理的汇总信息,如最小分配单元(min_size)、最小分配单元对应的位移(min_shift)、页数组地址(pages)、空闲页链表(free)、可分配空间的起始地址(start)、内存块结束地址(end)等等信息(如代码1所示),在内存的管理过程中,内存的分配、回收、定位等等操作都依赖于这些数据。
  • SLOT数组:SLOT数组各成员分别负责固定大小的内存块(BLOCK)的分配和回收。在nginx中SLOT[0]~SLOT[7]分别负责区间在[1~8]、[9~16]、[17~32]、[33~64]、[65~128]、[129~256]、[257~512]、[513~1024]字节大小内存的分配,但为方便内存块(BLOCK)的分配和回收,每个内存块(BLOCK)的大小为各区间的上限(8、16、32、64、128、256、512、1024)。比如说:假如应用进程请求申请5个字节的空间,因5处在[1~8]的区间内,因此由SLOT[0]负责该内存的分配,但区间[1~8]的上限为8,因此即使申请5个字节,却依然分配8字节给应用进程。以此类推:假如申请12字节,12处于区间[9~16]之间,取上限16,因此由SLOT[1]分配16个字节给应用进程;假如申请50字节,50处于区间[33~64]之间,取上限64,因此由SLOT[2]分配64个字节给应用进程;假如申请84字节,84处于区间[65~128]之间,取上限128,因此由SLOT[3]分配128个字节;...;假如申请722字节,722处于区间[513~1024]之间,取上限1024,因此由SLOT[7]分配1024字节。
  • PAGES数组:PAGES数组各成员分别负责可分配空间中各页的查询、分配和回收,其处理流程可参考3.2节的说明。
  • 可分配空间:SLAB在逻辑上将可分配空间划分成M个内存页,每页大小为4K。每页内存与PAGES数组成员一一对应,由PAGES数组各成员负责各内存页的分配和回收。
  • 被浪费空间:按照每页4K的大小对空间进行划分时,满足4K的空间,将作为可分配空间被PAGES数组进行管理,而最后剩余的不足4K的内存将会被舍弃,也就是被浪费了!

3.1 初始化流程

  初始化阶段主要完成对SLOT头、SLOT数组、PAGES数组、可分配空间和被浪费空间的区域分化,各区域的划分可参考图1和各模块功能的说明。nginx中slab结构体如下所示:

[cpp] view plaincopy
print?
  1. typedef struct {  
  2.     size_t            min_size;     /* 最小分配单元 */  
  3.     size_t            min_shift;    /* 最小分配单元对应的位移 */  
  4.   
  5.     ngx_slab_page_t  *pages;        /* 页数组 */  
  6.     ngx_slab_page_t   free;         /* 空闲页链表 */  
  7.   
  8.     u_char           *start;        /* 可分配空间的起始地址 */  
  9.     u_char           *end;          /* 内存块的结束地址 */  
  10.   
  11.     ...                             /* 其他变量成员(省略) */  
  12. }ngx_slab_pool_t  

代码1 SLAB头部结构体

3.2 页的管理

3.2.1 页的分配

1)分配之前

  在SLAB初始化之后,所有页可以看成是一个连续的整体,其内存结构如下图所示:

图2 页的结构(分配之前)

2)申请一页

  当申请一页时,则将pages[0]从free链表中分离出去,如下图所示:

图3 页的结构(申请一页)

3)申请二页

  当再申请二页时,则将page[3]和pages[4]作为一个整体从free链表中分离出去,如下图所示:

图4 页的结构(申请二页)

3.2.2 页的回收

1)回收一页

  当页被回收时,被回收的页并不会和未被分配的页进行合并,而是通过链表串联起来,这样将造成运行的时间越长,要申请到超过一页大小的空间也会变得越来越难。正是因为这个原因,所以slab机制不适合用来反复分配和回收超过一页大小的内存空间。如下图所示:[切记:slab机制不适合用来反复分配和回收超过一页大小的内存空间]

图5 页的结构(回收一页)

2)回收二页

  当页被回收时,被回收的页并不会和未被分配的页进行合并,而是通过链表串联起来,如下图所示:

图6 页的结构(回收二页)

3.4 SLOT的管理

  SLOT数组的作用可以参考第三章开头的阐述。SLOT数组各成员相当于链表头,在SLOT的分配和回收过程中,通过链表来组织用于分配各SIZE(1~1024)的PAGE。如,在某时刻,可能存在如下状态:

图7  SLOT和PAGES的关系

3.4.1 页的管理

1)初始状态

  在SLAB初始化后,slot链表头的下一个节点都为NULL,如下图所示:

图8 SLOT初始状态

2)添加一页

  SLOT[2]负责32(17~32)字节空间的分配和回收,假设现申请分配24字节(17~32之间)的空间,因此将从slot[2]中分配。但在初始状态下slot[2]的下一页为NULL,因此需要向页管理模块申请一页pages[x]内存,再将该页加入到slot[2]的链表中,添加之后的内存结构如下图所示:

图9 slot[2]增加一页

3)暂离链表

  SLOT[2]中的每一页有128(4K/32=128)个单元,当一页分配了128次时,表示该页可分配单元分配完毕,此时该页将会暂时从链表中剔除出去,以防止下次申请时,做无效的遍历。如下图所示:

图10 slot[2]第一页被使用完

 

4)再添一页

  当再次申请17~32字节时,此时slot[2]的后续链表为空,因此需要再次向页管理申请一页pages[y]内存,再将该页加入到slot[2]的链表中,如下图所示。如果该页又被分配完,则进行3)的处理。

图11 slot[2]再添一页

5)重入链表

  当所有单元被用完的页pages[x]中的一个单元被回收时,页pages[x]中将有1个单元可以再次被分配使用,此时应该将pages[x]重新加入到slot[2]的链表中,以便下次分配时可以从页pages[x]中进行查找。此时内存组织形式如下图所示:

图12 页pages[x]重入链表

6)回收整页

  当页pages[x]所有单元被释放后,则该页将会被全部回收:该页将从slot[2]的链表中被剔除,并将页pages[x]重新加入到free链表。此时的内存结构图如下图所示:

图13 回收页pages[x]

 

3.4.2 SLOT的分配

1)页内结构

  被加入到SLOT数组链表的页在逻辑上划分为很多的内存单元,每一小内存单元的使用情况是通过位图进行标记的,1表示被占用,0表示未被占用。如:第20位bit的值为1时,表示第20个内存单元被占用。假如此SLOT链表的PAGE正好可以划分为32块,则其逻辑组织结构如下图所示:

图14 PAGE内结构

2)分配单元

  假如此时在SLOT[s]链表的页中连续申请4个内存单元,则其前4个内存单元将首先被占用,则此时的位图结构如下图所示:

图15 分配单元

3) 释放单元

  假如此时释放SLOT[s]链表页中第3个内存单元,则此时的位图结构如下图所示:

图16 释放单元

 

转自:"祁峰"的CSDN博客

转载于:https://www.cnblogs.com/zl1991/p/8335908.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/390309.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

apk之间数据共享的方式

1、四大组件之ContentProvider大法2、shareUserId3、apk均去远端获取配置文件(或接口)4、AIDL(bindService)5、SharePreference设置为MODE_WORLD_READABLE|MODE_WORLD_WRITEABLE模式,由于存在安全问题,已被…

蓝桥杯java 基础练习 十六进制转十进制

问题描述从键盘输入一个不超过8位的正的十六进制数字符串,将它转换为正的十进制数后输出。注:十六进制数中的10~15分别用大写的英文字母A、B、C、D、E、F表示。样例输入FFFF样例输出65535import java.math.BigInteger; import java.util.Scanner;public …

dynamic web module消失不见

2019独角兽企业重金招聘Python工程师标准>>> 方法1:在project Facets选项中勾选Dynamic Web Module即可 方法2: 我用eclipse对项目进行修改名称,修改成功后。项目就没有Deployment Descriptor(如下图红色框中&#xff…

576. 出界的路径数

576. 出界的路径数 给你一个大小为 m x n 的网格和一个球。球的起始坐标为 [startRow, startColumn] 。你可以将球移到在四个方向上相邻的单元格内(可以穿过网格边界到达网格之外)。你 最多 可以移动 maxMove 次球。 给你五个整数 m、n、maxMove、star…

telnet命令发送邮件

下面的例子是用qq的smtp服务器。 set localecho 本地回显启用 telnet smtp.qq.com 25 220 smtp.qq.com Esmtp QQ Mail Server helo sis 250 smtp.qq.com//服务器返回250 smtp.qq.com STARTTLS 220 Ready to start TLS//服务器返回 220 准备开启TLS通讯 auth login 334 VXNlcm5h…

myelcipse和maven搭建项目

偷懒一下,完了补充 转载:https://www.cnblogs.com/jr1260/p/6438811.html https://www.cnblogs.com/yangmingyu/p/6908519.html https://www.cnblogs.com/henuyuxiang/p/6288476.html 转载于:https://www.cnblogs.com/0914lx/p/8342343.html

551. 学生出勤记录

551. 学生出勤记录 I 给你一个字符串 s 表示一个学生的出勤记录,其中的每个字符用来标记当天的出勤情况(缺勤、迟到、到场)。记录中只含下面三种字符: ‘A’:Absent,缺勤 ‘L’:Late&#xff…

JavaScript实现职责链模式

什么是职责链模式 职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。举个例子:当你从…

Metrics介绍和Spring的集成

参考: http://colobu.com/2014/08/08/Metrics-and-Spring-Integration/ https://www.cnblogs.com/yangecnu/p/Using-Metrics-to-Profiling-WebService-Performance.html

配置 aws cli_AWS CLI教程–如何安装,配置和使用AWS CLI了解您的资源环境

配置 aws cliHow to get exactly the account and environment information you need to manage your AWS account using just the AWS CLI如何仅使用AWS CLI准确获取管理AWS账户所需的账户和环境信息 Installing the AWS CLI is actually quite simple. The best way to get …

grep递归查找头文件_Grep命令教程–如何使用递归查找在Linux和Unix中搜索文件

grep递归查找头文件grep stands for Globally Search For Regular Expression and Print out. It is a command line tool used in UNIX and Linux systems to search a specified pattern in a file or group of files. grep代表全局搜索正则表达式并打印出来 。 它是UNIX和Li…

C++ 前置声明

(一)class的前置声明 class的前置声明有两种。 pre.hclass PreA {}; main.hclass PreA; class Main {};//或者 class Main {class PreA* A; }; (二) struct前置声明 struct的前置声明只能用第一种。 (三) 有typedef的前置声明 Pr…

2.18 特殊权限set_uid 2.19 特殊权限set_gid 2.20 特殊权限stick_bit 2.21 软链接文件 2.22 硬连接文件...

2019独角兽企业重金招聘Python工程师标准>>> 特殊权限set_uid set_uid:该权限针对二进制可执行文件,使文件在执行阶段具有文件所有者的权限; 通俗一点讲就是,普通用户想要访问一个没有其他用户可执行权限的目录时,暂时…

345. 反转字符串中的元音字母

345. 反转字符串中的元音字母 给你一个字符串 s ,仅反转字符串中的所有元音字母,并返回结果字符串。 元音字母包括 ‘a’、‘e’、‘i’、‘o’、‘u’,且可能以大小写两种形式出现。 示例 1: 输入:s “hello” 输…

通过制作数字桌面游戏和Web应用程序学习JavaScript

Building 2D games can be a great way to learn JavaScript, especially when working through the basics of complex tabletop game logic.制作2D游戏可能是学习JavaScript的好方法,尤其是在研究复杂的桌面游戏逻辑基础时。 In this series, I’m going to intr…

【HAVENT原创】Node Express API 通用配置

为什么80%的码农都做不了架构师?>>> ( 基于 Express 4.x ) 启动文件 /app.js: var express require(express); var bodyParser require(body-parser); var proxy require(http-proxy-middleware); var path require(path);var index re…

C#使用Json.NET解析Json

本文转载自 http://xiaosheng.me/2016/10/01/article25/ 最近在 C# 项目中需要使用到 Json 格式的数据,我简单上网搜索了一下,基本上有两种操作 Json 数据的方法: 使用 Windows 系统自带的类使用第三方的包本着“第三方包一定有比系统自带类优…

现在JavaScript日期–如何在JavaScript中获取当前日期

Many applications you build will have some sort of a date component, whether its the creation date of a resource, or the timestamp of an activity. 您构建的许多应用程序都将具有某种日期组件,无论是资源的创建日期还是活动的时间戳。 Dealing with date…

233. 数字 1 的个数

给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。 示例 1: 输入:n 13 输出:6 示例 2: 输入:n 0 输出:0 解题思路 正确性证明 例如:对于n3015&#xff0c…

Linux串口设置参数

为什么80%的码农都做不了架构师?>>> 在Linux环境下,串口名从ttyS0开始依次是ttyS1、ttyS2等。在本程序中,使用ttyS0作为通信串口。在打开ttyS0的时候选项 O_NOCTTY 表示不能把本串口当成控制终端,否则用户的键盘输入信…