update linux memory mgr

This commit is contained in:
linlongchen
2020-04-24 22:54:26 +08:00
parent 2276ec1274
commit 77e2e15cbb
3 changed files with 152 additions and 26 deletions

View File

@@ -205,14 +205,6 @@ Linux内核--内核地址空间分布和进程地址空间 https://my.oschina.ne
Linux内存管理 http://gityuan.com/2015/10/30/kernel-memory/
linux内核slab机制分析 https://www.jianshu.com/p/95d68389fbd1
Linux slab 分配器剖析 https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/index.html#table2
Linux内核内存管理算法Buddy和Slab https://zhuanlan.zhihu.com/p/36140017
Linux内存之Slab https://fivezh.github.io/2017/06/25/Linux-slab-info/
Linux Used内存到底哪里去了 http://blog.yufeng.info/archives/2456

View File

@@ -0,0 +1,134 @@
上一篇文章我们分析了 Linux 内存管理机制,
## 物理页管理
`Linux `系统中通过分页机制把物理内存划分4K大小的内存页 `Page`(也称作页框`Page Frame`),物理内存的分配和回收都是基于内存页进行,假如系统请求小块内存,可以预先分配一页给他,避免了反复的申请和释放小块内存带来频繁的系统开销。假如系统需要大块内存,则可以用多页内存拼凑,而不必要求大块连续内存。
我们知道无论内核还是进程,当实际需要访问内存的时候,如果虚拟内存没有映射到物理内存,会发生缺页中断,这时候会请求分配物理内存页框。
### 物理页管理面临问题
物理内存页分配会出现外部碎片和内部碎片问题,所谓的「内部」和「外部」是针对页框内外而言,页框内的内存碎片是内部碎片,页框间的碎片是外部碎片。
#### 外部碎片
分配物理内存页的时候会尽量分配连续的内存页面,频繁的分配与回收物理页导致大量的小块内存夹杂在已分配页面中间,形成外部碎片。举个例子:
#### 内部碎片
以页为单位管理和分配内存这就导致每次分配的都至少是4K大小的页面而内核中有很多需要以字节为单位分配内存的场景这样本来只想要几个字节而已却不得不分配一页内存除去用掉的字节剩下的就形成了内部碎片。
### 页面管理算法
#### Buddy伙伴分配算法
Linux内核中引入了伙伴系统算法(Buddy system)。把所有的空闲页框分组为11个块链表每个块链表分别包含大小为1248163264128256512和1024个连续页框的页框块。最大可以申请1024个连续页框对应4MB大小的连续内存。因为任何正整数都可以由2^n的和组成所以总能找到合适大小的内存块分配出去减少了外部碎片产生 。
比如我需要申请4个页框但是长度为4个连续页框块链表没有空闲的页框块伙伴系统会从连续8个页框块的链表获取一个并将其拆分为两个连续4个页框块放入连续4个页框块的链表中。释放的时候也一样会检查释放的这几个页框的之前和之后的物理页框是否空闲并且能否组成下一级长度的块。
#### 命令查看
```
[lemon]]# cat /proc/buddyinfo
Node 0, zone DMA 1 0 0 0 2 1 1 0 1 1 3
Node 0, zone DMA32 3198 4108 4940 4773 4030 2184 891 180 67 32 330
Node 0, zone Normal 42438 37404 16035 4386 610 121 22 3 0 0 1
```
#### slab分配器
伙伴系统分配出去的内存还是以页框为单位,对于内核的很多场景来说还是太大,于是就有了` slab `分配器。
Slab是一种内存分配器通过将内存划分不同大小的空间分配给对象使用来进行缓存管理应用于内核对象的缓存。
slab分配器是基于对象进行管理的所谓的对象就是内核中的数据结构例如`task_struct、file_struct`。相同类型的对象归为一类每当要申请这样一个对象时slab分配器就从一个slab列表中分配一个这样大小的单元出去而当要释放时将其重新保存在该列表中而不是直接返回给伙伴系统从而避免内部碎片。slab分配器并不丢弃已经分配的对象而是释放并把它们保存在内存中。slab分配对象时会使用最近释放的对象的内存块因此其驻留在cpu高速缓存中的概率会大大提高。
- Slab对小对象进行分配不用为每个小对象分配一个页节省了空间。
- 内核中一些小对象创建析构很频繁Slab对这些小对象做缓存可以重复利用一些相同的对象减少内存分配次数。
![image-20200421202943561](C:\Users\linlongchen\AppData\Roaming\Typora\typora-user-images\image-20200421202943561.png)
kmem_cache是一个cache_chain的链表描述了一个高速缓存每个高速缓存包含了一个slabs的列表这通常是一段连续的内存块。存在3种slab
- slabs_full(完全分配的slab)
- slabs_partial(部分分配的slab)
- slabs_empty(空slab,或者没有对象被分配)。
slab是slab分配器的最小单位在实现上一个slab有一个货多个连续的物理页组成通常只有一页。单个slab可以在slab链表之间移动例如如果一个半满slab被分配了对象后变满了就要从slabs_partial中被删除同时插入到slabs_full中去。
#### 命令查看
`cat /proc/slabinfo`
![image-20200421203357847](C:\Users\linlongchen\AppData\Roaming\Typora\typora-user-images\image-20200421203357847.png)
`kmalloc() ` 也是基于 SLAB 分配器的,只不过它所需要的管理结构头已经按照 2^n 的大小排列事先准备好了
可以看到slabinfo的信息有` kmalloc `相关 `slab `对象信息
![image-20200421204315596](C:\Users\linlongchen\AppData\Roaming\Typora\typora-user-images\image-20200421204315596.png)
`slabtop`
![image-20200421203542312](C:\Users\linlongchen\AppData\Roaming\Typora\typora-user-images\image-20200421203542312.png)
## 三个内存分配函数
### 用户空间malloc
当申请小于 128KB 小内存的时,` malloc `使用 `sbrk` 分配内存;当申请大于 128KB 的内存时,使用 `mmap` 函数申请内存;但是这只是分配了虚拟内存,还没有映射到物理内存,当访问申请的内存时,才会因为缺页异常,内核分配物理内存。
由于brk/sbrk/mmap属于系统调用如果每次申请内存都调用这三个函数中的一个那么每次都要产生系统调用开销即cpu从用户态切换到内核态的上下文切换这里要保存用户态数据等会还要切换回用户态这是非常影响性能的其次这样申请的内存容易产生碎片因为堆是从低地址到高地址如果低地址的内存没有被释放高地址的内存就不能被回收。
因此,` malloc `采用的是内存池的实现方式malloc内存池实现方式更类似于 STL 分配器和 memcached 的内存池,先申请一大块内存,然后将内存分成不同大小的内存块,然后用户申请内存时,直接从内存池中选择一块相近的内存块即可。
### 内核空间kmalloc
这个函数用于分配内核空间的虚拟内存
`kmalloc` 按字节为单位虚拟内存,一般用于分配小块内存,释放内存对应于 `kfree` ,可以分配连续的物理内存。函数原型在 `<linux/vmalloc.h>` 中声明。 kmalloc 分配内存是基于slab因此slab的一些特性包括着色对齐等都具备性能较好一般情况下在驱动程序中都是调用kmalloc()来给数据结构分配内存 。
kmalloc()分配的内存处于3GBhigh_memory之间的直接内存映射区。
### 内核空间vmalloc
`vmalloc` 按字节为单位虚拟内存,一般用分配大块内存,释放内存对应于 `vfree`,分配连续的虚拟内存,但是物理上不一定连续。函数原型在 `<linux/vmalloc.h>` 中声明。 一般用在为活动的交换区分配数据结构为某些I/O驱动程序分配缓冲区或为模块分配空间。
vmalloc()分配的内存在VMALLOC_START4GB之间也就是非连续的动态内存映射区。
## Reference
linux内核slab机制分析 https://www.jianshu.com/p/95d68389fbd1
Linux slab 分配器剖析 https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/index.html#table2
Linux内核内存管理算法Buddy和Slab https://zhuanlan.zhihu.com/p/36140017
Linux内存之Slab https://fivezh.github.io/2017/06/25/Linux-slab-info/
malloc 的实现原理 内存池 mmap sbrk 链表 https://zhuanlan.zhihu.com/p/57863097
malloc实现原理 http://luodw.cc/2016/02/17/malloc/
glibc内存管理那些事儿 https://www.jianshu.com/p/2fedeacfa797
Kmalloc和Vmalloc的区别 https://www.cnblogs.com/wuchanming/p/4465155.html

View File

@@ -1,8 +1,8 @@
本文是后端微服务架构系列的第二篇文章。在微服务架构中服务之间的通信方式常见的有两种:`RPC`` REST`,关于微服务和 `RPC` 的更多细节,可以参考我上一篇文章[]()
这篇文章主要介绍什么是 `REST` 风格设计以及 `RESTfull` 接口。阅读完本文你将收获以下知识点:
这篇文章主要介绍什么是 `REST` 风格设计以及 `RESTful` 接口。阅读完本文你将收获以下知识点:
- 什么是 `REST``RESTfull`
- 什么是 `REST``RESTful`
- `REST` 接口设计规范是什么
- `REST` 为什么要设计成无状态
- 接口无状态真的是没有状态吗
@@ -10,7 +10,7 @@
### REST和RESTfull
### REST和RESTful
`RESTRepresentational State Transfer表述性状态转移` 是一种软件架构风格。REST提出了一组架构约束条件和原则任何满足 `REST` 约束条件和原则的架构,都称为 `RESTful` 架构。
@@ -31,12 +31,12 @@
`URL` 是统一资源定位器,它是一种具体的 `URI`,即 `URL` 可以用来标识一个资源,而且还指明了如何定位这个资源,`URL``URI` 的子集。
`URN` 统一资源命名,是通过名字来标识资源。`URN `也是 `URI` 的子集。
![URI-URN-URL](https://upload-images.jianshu.io/upload_images/7842464-8a3c31c67d4d5c3b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![URI-URN-URL](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LThhM2MzMWM2N2Q0ZDVjM2IucG5n?x-oss-process=image/format,png)
`HTTP` 协议中用 `URL` 标识资源,也就是浏览器地址栏你看到的那一串网址。
![地址栏URL](https://upload-images.jianshu.io/upload_images/7842464-a6d585a693a51811.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![地址栏URL](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LWE2ZDU4NWE2OTNhNTE4MTEucG5n?x-oss-process=image/format,png)
#### 资源表述性
@@ -99,7 +99,7 @@ phone : 18666666666
<img src="http://wx2.sinaimg.cn/large/005LQqKVgy1felzzeyzyzj30k00k0wfi.jpg" height="200" width="200" />
![](http://wx2.sinaimg.cn/large/005LQqKVgy1felzzeyzyzj30k00k0wfi.jpg)
![](https://imgconvert.csdnimg.cn/aHR0cDovL3d4Mi5zaW5haW1nLmNuL2xhcmdlLzAwNUxRcUtWZ3kxZmVsenpleXp5emozMGswMGswd2ZpLmpwZw?x-oss-process=image/format,png)
这样做最显然的好处是,减少了服务之间的耦合。客户端访问服务资源之前不需要知道资源在服务端的具体存储格式,只需描述资源形式即可修改、创建、更新、删除服务端的资源。
@@ -110,7 +110,7 @@ phone : 18666666666
搞懂了「资源描述性」接下来看下什么是「状态转移」?状态转移就是客户端通过一系列请求动作,推动服务端的资源状态发生变化,资源的状态可以在「创建-修改-查看-删除」之间转移。
![资源状态转移](https://upload-images.jianshu.io/upload_images/7842464-b9db31aa568eac4b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![资源状态转移](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LWI5ZGIzMWFhNTY4ZWFjNGIucG5n?x-oss-process=image/format,png)
资源状态的变化在宏观上的反应就是业务流程推进。打个比方,你去银行系统开户、查余额、销户,这个过程你推动了你的银行账户这个「资源」经历了不同的状态转移让你完成了不同的业务操作。
@@ -121,7 +121,7 @@ phone : 18666666666
#### 协议选择
`REST` 本身并没有提到底层应该使用什么协议,日常实践案例中最常用的是基于 `HTTP` 的 `RESTfull` 实现。
`REST` 本身并没有提到底层应该使用什么协议,日常实践案例中最常用的是基于 `HTTP` 的 `RESTful` 实现。
这是因为 `HTTP` 协议自带的动词 `GET/POST/PUT/DELETE` 可以作为推动状态转移的方法,另外`HTTP` 的制定了规范的状态码。还有其他的一些 `HTTP` 特性,这些特性使得在`HTTP` 之上实现 `REST` 要简单得多,而如果使用其他协议的话,就需要自己实现这些特性。
@@ -129,7 +129,7 @@ phone : 18666666666
#### 请求规范
` RESTfull` 架构中,发生状态转换的是「资源」,所以`URI` 中一般只能包含代表「资源」的名词,并且推荐是复数,而不应该在 `URI` 中包对资源进行操作的动词。
` RESTful` 架构中,发生状态转换的是「资源」,所以`URI` 中一般只能包含代表「资源」的名词,并且推荐是复数,而不应该在 `URI` 中包对资源进行操作的动词。
对资源执行的`CURD「增删改查」`动作应该在`HTTP`请求方法的`GET/POST/PUT/DELETE`中体现。
@@ -155,7 +155,7 @@ POST http://www.test.com/Deletelemon //删除
#### 状态码
服务端消息响应携带状态码,指示客户端进行下一步处理。符合 `RESTfull` 规范的接口返回状态码都是通用的,不需要额外约定,利用`HTTP Status Code 状态码` 表示请求处理结果,降低了微服务间互操作成本。
服务端消息响应携带状态码,指示客户端进行下一步处理。符合 `RESTful` 规范的接口返回状态码都是通用的,不需要额外约定,利用`HTTP Status Code 状态码` 表示请求处理结果,降低了微服务间互操作成本。
| 状态码 | 状态码含义 |
| ------ | ---------------------------------------------- |
@@ -175,7 +175,7 @@ POST http://www.test.com/Deletelemon //删除
#### 无状态
`RESTfull`接口要求是「无状态」。无状态指的是任意一个Web请求必须完全与其他请求隔离当客户端发起请求时消息本身包含了服务端识别这一请求上下文所需的全部信息。
`RESTful`接口要求是「无状态」。无状态指的是任意一个Web请求必须完全与其他请求隔离当客户端发起请求时消息本身包含了服务端识别这一请求上下文所需的全部信息。
@@ -190,7 +190,7 @@ POST http://www.test.com/Deletelemon //删除
- lemon登录之后点击取款100万产生一个取款请求
- 服务端收到取款请求,执行取款逻辑并返回操作结果
![取款业务流程](https://upload-images.jianshu.io/upload_images/7842464-6abdaf73029fb0dd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![取款业务流程](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LTZhYmRhZjczMDI5ZmIwZGQucG5n?x-oss-process=image/format,png)
这里有个问题,服务端在不同时间点收到登录请求和取款请求,这两个请求都是用户 `lemon` 产生的,如果不在技术层面做对独立的 `HTTP` 请求做关联的话,服务端就无法知道这两个请求其实是都是用户`lemon` 「取款业务」的组成部分。
@@ -205,19 +205,19 @@ POST http://www.test.com/Deletelemon //删除
服务端维护一个会话状态信息列表,用`session-id`唯一标识一个状态信息,`session-id`一般包含在`HTTP`响应的`Set-Cookie`头部返回给客户端,后续客户端请求携带包含`session-id`信息的`cookie`头部,服务端解析`cookie`取出`session-id`,去维护的状态列表中取回该消息对应的状态信息,这样就把无状态的`HTTP`变成有状态的了。
![session会话](https://upload-images.jianshu.io/upload_images/7842464-050a100672be8a6b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![session会话](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LTA1MGExMDA2NzJiZThhNmIucG5n?x-oss-process=image/format,png)
2. `Token` 方式。服务端不保存会话状态,客户端每次请求都携带完整的会话状态信息(一般是加密的)给服务端。
`Token`也称作是「令牌」或临时证书签名,状态信息都被加密到`token`中,这样每当服务器收到请求后解密`token`就能获取该请求对应的状态信息,也就能把不同的请求消息关联到同一个业务流程中来,和`session`方式有类似的效果,只不过这次的状态信息不保存在服务端。
![Token会话](https://upload-images.jianshu.io/upload_images/7842464-228c9b5758286053.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![Token会话](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LTIyOGM5YjU3NTgyODYwNTMucG5n?x-oss-process=image/format,png)
以上两种实现中,第一种 `Session` 方式是有状态的,第二种 `Token` 方式是无状态的。
如果你要实现 `RESTfull` 接口最好按第二种技术方案实现,当然要实现无状态也还有其他方式,思路都是「服务端不保持会话状态」就对了。
如果你要实现 `RESTful` 接口最好按第二种技术方案实现,当然要实现无状态也还有其他方式,思路都是「服务端不保持会话状态」就对了。
@@ -225,12 +225,12 @@ POST http://www.test.com/Deletelemon //删除
为了高可用性和负载均衡需求,多个微服务通过负载均衡实现分布式集群化部署,集群中每个服务都是独立和对等的。如果服务器在收到客户端请求之时不可用或者宕机,无状态请求可以由任何其他可用服务器处理并作出应答,这在分布式应用中非常重要。
![REST无状态接口](https://upload-images.jianshu.io/upload_images/7842464-107394c52657bc98.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![REST无状态接口](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LTEwNzM5NGM1MjY1N2JjOTgucG5n?x-oss-process=image/format,png)
想象一下如果服务端保存状态,一个事务内的每个请求都必须落到同一台服务器去处理,这就失去了分布式的意义和优势。
所以, `RESTfull` 接口要求是无状态的,是为了更好的适应分布式业务场景,发挥微服务集群优势。
所以, `RESTful` 接口要求是无状态的,是为了更好的适应分布式业务场景,发挥微服务集群优势。
@@ -246,7 +246,7 @@ POST http://www.test.com/Deletelemon //删除
很多 `RPC` 框架提供的消息传输都是基于二进制的,比如`Thrift`、`Protocol buffers`。这样做的好处是消息结构比较紧凑,对于频繁调用或者大流量、低时延要求的应用场景,能够显著减少网络开销;另一个约束是某些 `RPC` 框架有很强的技术耦合性,比如 `Dubbo` 只能用于 `java` 技术栈。综上,`RPC ` **更加适用于系统内部微服务之间的高效通信。**
`RESTfull`接口由于提供了统一的基于 `HTTP `的 `REST` 设计标准,只需 `web` 框架支持 `HTTP` 协议,并设计`RESTfull` 风格的接口即可,极大的方便了第三方服务接入调用,**适合用于微服务系统对外暴露的接口设计标准。**
`RESTful`接口由于提供了统一的基于 `HTTP `的 `REST` 设计标准,只需 `web` 框架支持 `HTTP` 协议,并设计`RESTful` 风格的接口即可,极大的方便了第三方服务接入调用,**适合用于微服务系统对外暴露的接口设计标准。**