Nginx内存池源码刨析

2024-05-07 15:52
文章标签 源码 内存 nginx 刨析

本文主要是介绍Nginx内存池源码刨析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Nginx 内存池刨析

实例源码刨析

#define BLOCK_SIZE  16   //每次分配内存块大小#define MEM_POOL_SIZE (1024 * 4) //内存池每块大小
	int i = 0, k = 0;int use_free = 0;ngx_pagesize = getpagesize();//获取系统的页大小//printf("pagesize: %zu\n",ngx_pagesize);char * ptr = NULL;for(k = 0; k< 1024 * 500; k++){ngx_pool_t * mem_pool = ngx_create_pool(MEM_POOL_SIZE);//创建池for(i = 0; i < 1024 ; i++){ptr = ngx_palloc(mem_pool,BLOCK_SIZE);if(!ptr) fprintf(stderr,"ngx_palloc failed. \n");else {*ptr = '\0';*(ptr + BLOCK_SIZE -1) = '\0';}}ngx_destroy_pool(mem_pool);}

一些函数和宏定义

#define NGX_ALIGNMENT   sizeof(unsigned long)    /* platform word 平台字节长*/
#define ngx_align(d, a)     (((d) + (a - 1)) & ~(a - 1)) /* b对齐为a的倍数*/
#define ngx_align_ptr(p, a)                                                   \(u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))/*用于将指针 p 对齐到指定的对齐字节数 a*/
#define ngx_memzero(buf, n)       (void) memset(buf, 0, n)

ngx_create_pool

创建线程池


ngx_pool_t * ngx_create_pool(size_t size)
{ngx_pool_t  *p;p = ngx_memalign(NGX_POOL_ALIGNMENT, size);if (p == NULL) {return NULL;}//它将 p 指针向后偏移 sizeof(ngx_pool_t) 字节,这样 p->d.last 就指向了内存块中除去 ngx_pool_t 结构体大小后的位置,即可用于分配的起始位置。//将 p 指向的内存块的 last 成员设置为指向当前内存块后面的一段内存的起始地址。//这段内存的大小为 sizeof(ngx_pool_t),即 ngx_pool_t 结构体的大小。//这样做是为了在这块内存中分配其他数据时,可以从这段内存的末尾开始分配,确保内存块的内存布局是连续的。p->d.last = (u_char *) p + sizeof(ngx_pool_t);//指向这块内存已使用区域的最后部分。//指向未使用区域的尾部p->d.end = (u_char *) p + size;//下一块内存块的地址p->d.next = NULL;//内存分配的失败次数p->d.failed = 0;//这行代码确定了内存池中可用于分配的最大内存大小。为了确保内存分配不会超出预先设定的阈值, size = size - sizeof(ngx_pool_t);p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;// 当前正在使用的数据块的指针p->current = p;//p->chain = NULL;p->large = NULL;//p->cleanup = NULL;//p->log = log;return p;
}

ngx_memalign

void * ngx_memalign(size_t alignment, size_t size)
{void  *p;int    err;err = posix_memalign(&p, alignment, size);//按照指定的对齐要求和大小分配内存块。if (err) {fprintf(stderr,"posix_memalign(%zu, %zu) failed", alignment, size);p = NULL;}if(debug) fprintf(stderr,"posix_memalign: %p:%zu @%zu", p, size, alignment);return p;
}

ngx_palloc分配内存

void *ngx_palloc(ngx_pool_t *pool, size_t size)
{//判断是分配内存池的内存还是单独分配一块内存位于大储存块区。
#if !(NGX_DEBUG_PALLOC)if (size <= pool->max) {return ngx_palloc_small(pool, size, 1);}
#endifreturn ngx_palloc_large(pool, size);
}
static inline void *ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align)
{u_char      *m;ngx_pool_t  *p;p = pool->current;do {m = p->d.last;if (align) {m = ngx_align_ptr(m, NGX_ALIGNMENT);}if ((size_t) (p->d.end - m) >= size) {p->d.last = m + size;return m;}p = p->d.next;} while (p);return ngx_palloc_block(pool, size);
}
  1. 定义一个指向内存池结构的指针p。
  2. 将当前内存池的指针赋值给p。
  3. 使用一个do-while循环来遍历内存池链表。
  4. 获取当前内存池的最后一个内存块的指针m。
  5. 如果align为真,则将m对齐到NGX_ALIGNMENT字节边界。
  6. 检查当前内存池中是否有足够的空间来分配size大小的内存块。如果有足够的空间,将内存块的指针m设置为分配内存块的起始地址,并将内存池的最后一个内存块的指针更新为m+size。然后返回m。
  7. 如果内存池中没有足够的空间,则将p指向下一个内存池,继续遍历内存池链表。
  8. 如果遍历完内存池链表仍然没有找到足够的空间,则调用ngx_palloc_block函数分配一个新的内存块,并将新分配的内存块的指针返回。

ngx_palloc_large


static void *ngx_palloc_large(ngx_pool_t *pool, size_t size)
{void              *p;ngx_uint_t         n;ngx_pool_large_t  *large;p = ngx_alloc(size);if (p == NULL) {return NULL;}n = 0;for (large = pool->large; large; large = large->next) {if (large->alloc == NULL) {large->alloc = p;return p;}if (n++ > 3) {break;}}large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);if (large == NULL) {ngx_free(p);return NULL;}large->alloc = p;large->next = pool->large;pool->large = large;return p;
}
  1. 首先,尝试使用ngx_alloc函数分配一块大小为size的内存。如果分配成功,则将该内存块的指针赋值给p
  2. 然后,遍历内存池中的large链表,查找一个alloc字段为NULL的节点。如果找到,则将分配的内存块的指针赋值给该节点的alloc字段,并返回该内存块的指针。
  3. 如果遍历了large链表3次仍然没有找到合适的节点,则跳出循环。
  4. 分配一个新的ngx_pool_large_t结构体,将其alloc字段赋值为分配的内存块的指针,并将该结构体添加到内存池的large链表中。
  5. 返回分配的内存块的指针。

函数ngx_palloc_large的用途是在Nginx的内存池中分配大块内存。在Nginx中,大块内存通常用于存储文件句柄、连接池等。

注意事项:

  1. 在使用ngx_palloc_large分配内存后,需要确保在适当的时候释放内存,以避免内存泄漏。
  2. 在使用ngx_palloc_large分配内存时,如果内存池中的large链表已经很长,可能会导致内存分配效率降低。因此,在分配大块内存时,可以考虑使用其他内存分配机制,如ngx_palloc函数。

void * ngx_alloc(size_t size)
{void  *p;p = malloc(size);if (p == NULL) {fprintf(stderr,"malloc(%zu) failed", size);}if(debug) fprintf(stderr, "malloc: %p:%zu", p, size);return p;
}

void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)
{void              *p;ngx_pool_large_t  *large;p = ngx_memalign(alignment, size);if (p == NULL) {return NULL;}large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);if (large == NULL) {ngx_free(p);return NULL;}large->alloc = p;large->next = pool->large;pool->large = large;return p;
}
  1. 使用ngx_memalign函数尝试分配一块大小为size且对齐为alignment的内存。如果分配失败,直接返回NULL
  2. 使用ngx_palloc_small函数在内存池中分配一个ngx_pool_large_t结构体的空间,用于存储分配的内存块信息。如果分配失败,释放之前分配的内存,并返回NULL
  3. 将分配的内存块的指针存入ngx_pool_large_t结构体的alloc字段,并将该结构体添加到内存池的large链表中。
  4. 返回分配的内存块的指针。


void * ngx_pcalloc(ngx_pool_t *pool, size_t size)
{void *p;p = ngx_palloc(pool, size);if (p) {ngx_memzero(p, size);}return p;
}
  1. 首先,通过ngx_palloc函数尝试在内存池中分配一块大小为size的内存。如果分配成功,则将返回的指针赋值给p
  2. 如果p不为空,使用ngx_memzero函数将p指向的内存块初始化为全零。ngx_memzero函数的作用是将一块内存块的所有字节设置为零,这在初始化内存时非常有用,可以确保内存中的所有字节都被初始化为零,从而避免未初始化的内存导致的错误。
  3. 最后,返回p,即分配的内存块的指针。

这篇关于Nginx内存池源码刨析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/967801

相关文章

NameNode内存生产配置

Hadoop2.x 系列,配置 NameNode 内存 NameNode 内存默认 2000m ,如果服务器内存 4G , NameNode 内存可以配置 3g 。在 hadoop-env.sh 文件中配置如下。 HADOOP_NAMENODE_OPTS=-Xmx3072m Hadoop3.x 系列,配置 Nam

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除

Windows下Nginx的安装及开机启动

1、将nginx-1.16.1.zip解压拷贝至D:\web\nginx目录下。 2、启动Nginx,两种方法: (1)直接双击nginx.exe,双击后一个黑色的弹窗一闪而过。 (2)打开cmd命令窗口,切换到nginx目录下,输入命令 nginx.exe 或者 start nginx ,回车即可。 3、检查nginx是否启动成功。 直接在浏览器地址栏输入网址 http://lo

JVM内存调优原则及几种JVM内存调优方法

JVM内存调优原则及几种JVM内存调优方法 1、堆大小设置。 2、回收器选择。   1、在对JVM内存调优的时候不能只看操作系统级别Java进程所占用的内存,这个数值不能准确的反应堆内存的真实占用情况,因为GC过后这个值是不会变化的,因此内存调优的时候要更多地使用JDK提供的内存查看工具,比如JConsole和Java VisualVM。   2、对JVM内存的系统级的调优主要的目的是减少