Nginx源码分析&内存模块

  今天总结了下C语言的内存分配问题,那么就看看Nginx的内存分配相关模型的具体实现。还有内存对齐的内容~~不懂的可以看看~~

src/os/unix/Ngx_alloc.h&Ngx_alloc.c

  先上源码:

/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/ #ifndef _NGX_ALLOC_H_INCLUDED_
#define _NGX_ALLOC_H_INCLUDED_ #include <ngx_config.h>
#include <ngx_core.h> void *ngx_alloc(size_t size, ngx_log_t *log);
void *ngx_calloc(size_t size, ngx_log_t *log); #define ngx_free free /*
* Linux has memalign() or posix_memalign()
* Solaris has memalign()
* FreeBSD 7.0 has posix_memalign(), besides, early version's malloc()
* aligns allocations bigger than page size at the page boundary
*/ #if (NGX_HAVE_POSIX_MEMALIGN || NGX_HAVE_MEMALIGN) void *ngx_memalign(size_t alignment, size_t size, ngx_log_t *log); #else #define ngx_memalign(alignment, size, log) ngx_alloc(size, log) #endif extern ngx_uint_t ngx_pagesize;
extern ngx_uint_t ngx_pagesize_shift;
extern ngx_uint_t ngx_cacheline_size; #endif /* _NGX_ALLOC_H_INCLUDED_ */

  这里部分代码是关于内存的申请的,是对Linux原有的内存申请函数的再一次封装。

  1.函数声明

void *ngx_alloc(size_t size, ngx_log_t *log);    //申请空间
void *ngx_calloc(size_t size, ngx_log_t *log); //申请空间,并初始化为0

  2.源码解析

void * ngx_alloc(size_t size, ngx_log_t *log)
{
void *p;
p = malloc(size);//malloc就是返回一个void*指针,指向分配的size大小的内存
if (p == NULL) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
"malloc(%uz) failed", size);
}
ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, , "malloc: %p:%uz", p, size);
return p;
} void * ngx_calloc(size_t size, ngx_log_t *log)
{
void *p;
p = ngx_alloc(size, log);//调用上面的函数
if (p) {
ngx_memzero(p, size);//并初始化为0,#define ngx_memzero(buf, n) (void) memset(buf, 0, n)
  }
return p;
}

  3.POSIX_MEMALIGN与MEMALIGN申请对齐内存,可以参考Linux man page:http://man7.org/linux/man-pages/man3/valloc.3.html

#if (NGX_HAVE_POSIX_MEMALIGN || NGX_HAVE_MEMALIGN)

void *ngx_memalign(size_t alignment, size_t size, ngx_log_t *log);

#else

#define ngx_memalign(alignment, size, log)  ngx_alloc(size, log)

#endif
#if (NGX_HAVE_POSIX_MEMALIGN)
void * ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)
{
void *p;
int err;
err = posix_memalign(&p, alignment, size);//stdlib.h 新接口 if (err) {
ngx_log_error(NGX_LOG_EMERG, log, err,
"posix_memalign(%uz, %uz) failed", alignment, size);
p = NULL;
}
ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, ,
"posix_memalign: %p:%uz @%uz", p, size, alignment);
return p;
}
#elif (NGX_HAVE_MEMALIGN)
void * ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)
{
void *p;
p = memalign(alignment, size);//malloc.h 老接口
if (p == NULL) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
"memalign(%uz, %uz) failed", alignment, size);
}
ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, ,
"memalign: %p:%uz @%uz", p, size, alignment);
return p;
}
#endif

数据对齐

概念:

  对齐跟数据在内存中的位置有关,为了使CPU能够对变量进行快速的访问,变量的起始地址应该具有某些特性,即所谓的”对齐”。 比如4字节的int型,其起始地址应该位于4字节的边界上,即起始地址能够被4整除。

功能:

  字节对齐的作用不仅是便于cpu快速访问,同时合理的利用字节对齐可以有效地节省存储空间。

具体方法:

  指定对齐值:#pragma pack (value)时的指定对齐值value。

  取消对齐值:#pragma pach ()

具体分析:

struct A{
char a; //1
int b; //4
short c; //2
} struct B{
int b;
char a;
short c;
} #pragma pack(1)
struct C{
char a;
int b;
short c;
}
#pragma pack() #pragma pach(2)
struct D{
char a;
int b;
short c;
}
#pragma pack()

  代码如上,想一想答案都是多少?

    sizeof(struct A)=10  //默认情况下,1字节的a在0x00000000,而整形b只能放在0x00000004(必须从4的整数倍开始)~0x00000007,最后的c在0x00000008~0x00000009

  sizeof(struct B)=8    //分析同上

  sizeof(struct C)=7    //这里指定了对齐值为1,那么a在0x00000000,b在0x00000001~0x0000004,c在0x00000005~0x00000006

  sizeof(struct D)=8    //分析同上

A、B、C、D的内存地址如图:

地址 0x00000000 0x01 0x02 0x03 004 0x05 0x06 0x07 0x08 0x09
A a       b c
B b a   c    
C a b c      
D a   b c    

src/core/Ngx_palloc.h&Ngx_palloc.cn内存池分析

  上源码:

/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/ #ifndef _NGX_PALLOC_H_INCLUDED_
#define _NGX_PALLOC_H_INCLUDED_ #include <ngx_config.h>
#include <ngx_core.h> /*
* NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.
* On Windows NT it decreases a number of locked pages in a kernel.
*/
#define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1) #define NGX_DEFAULT_POOL_SIZE (16 * 1024) #define NGX_POOL_ALIGNMENT 16
#define NGX_MIN_POOL_SIZE \
ngx_align((sizeof(ngx_pool_t) + * sizeof(ngx_pool_large_t)), \
NGX_POOL_ALIGNMENT) typedef void (*ngx_pool_cleanup_pt)(void *data); typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t; struct ngx_pool_cleanup_s {
ngx_pool_cleanup_pt handler;
void *data;
ngx_pool_cleanup_t *next;
}; typedef struct ngx_pool_large_s ngx_pool_large_t; struct ngx_pool_large_s {
ngx_pool_large_t *next;
void *alloc;
}; typedef struct {
u_char *last;
u_char *end;
ngx_pool_t *next;
ngx_uint_t failed;
} ngx_pool_data_t; struct ngx_pool_s {
ngx_pool_data_t d;
size_t max;
ngx_pool_t *current;
ngx_chain_t *chain;
ngx_pool_large_t *large;
ngx_pool_cleanup_t *cleanup;
ngx_log_t *log;
}; typedef struct {
ngx_fd_t fd;
u_char *name;
ngx_log_t *log;
} ngx_pool_cleanup_file_t; void *ngx_alloc(size_t size, ngx_log_t *log);
void *ngx_calloc(size_t size, ngx_log_t *log); ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
void ngx_destroy_pool(ngx_pool_t *pool);
void ngx_reset_pool(ngx_pool_t *pool); void *ngx_palloc(ngx_pool_t *pool, size_t size);
void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p); ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);
void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd);
void ngx_pool_cleanup_file(void *data);
void ngx_pool_delete_file(void *data); #endif /* _NGX_PALLOC_H_INCLUDED_ */

  1.#define NGX_DEFAULT_POOL_SIZE    (16 * 1024),表示NGX默认的内存池的大小为16*1024

  2.结构体ngx_pool_data_t内存数据块,ngx_pool_s内存池头部结构

 typedef struct {
u_char *last; //当前内存池分配到此处,即下一次分配从此处开始
u_char *end; //内存池结束位置
ngx_pool_t *next; //内存池里面有很多块内存,这些内存块就是通过该指针连成链表的
ngx_uint_t failed; //内存池分配失败次数
} ngx_pool_data_t; //内存池的数据块位置信息 struct ngx_pool_s{ //内存池头部结构
ngx_pool_data_t d; //内存池的数据块
size_t max; //内存池数据块的最大值
ngx_pool_t *current; //指向当前内存池
ngx_chain_t *chain; //该指针挂接一个ngx_chain_t结构
ngx_pool_large_t *large; //大块内存链表,即分配空间超过max的内存
ngx_pool_cleanup_t *cleanup; //释放内存池的callback
ngx_log_t *log; //日志信息
};

  3.创建和销毁内存池:

ngx_pool_t * ngx_create_pool(size_t size, ngx_log_t *log)//创建内存池
{
ngx_pool_t *p; p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log); //申请对齐内存空间
if (p == NULL) {
return NULL;
} p->d.last = (u_char *) p + sizeof(ngx_pool_t); //下一次分配的开始地址,sizeof(ngx_pool_t)为申请的P的大小
p->d.end = (u_char *) p + size;             //内存池结束位置,size是申请空间的小小
p->d.next = NULL; //内存链表的指向下一内存块的指针为空
p->d.failed = ; //失败次数 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;
} 
//该函数将遍历内存池链表,所有释放内存,如果注册了clenup(也是一个链表结构),亦将遍历该cleanup链表结构依次调用clenup的handler清理。同时,还将遍历large链表,释放大块内存。
void ngx_destroy_pool(ngx_pool_t *pool)//删除全部内存池(链上的所有内存块)
{
ngx_pool_t *p, *n;
ngx_pool_large_t *l;
ngx_pool_cleanup_t *c;
  //根据注册的ngx_pool_cleanup_s 来逐个销毁内存
for (c = pool->cleanup; c; c = c->next) {
   if (c->handler) {
      ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, , "run cleanup: %p", c);
      c->handler(c->data);
     }
}
//销毁大内存块
for (l = pool->large; l; l = l->next) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, , "free: %p", l->alloc);
if (l->alloc) {
ngx_free(l->alloc);
}
}
#if (NGX_DEBUG)
/*
* we could allocate the pool->log from this pool
* so we cannot use this log while free()ing the pool
*/ for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, ,
"free: %p, unused: %uz", p, p->d.end - p->d.last); if (n == NULL) {
break;
}
}
#endif

  //普通内存池
  for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
ngx_free(p);
if (n == NULL) {
break;
}
}

  4.重置内存池:

//该函数将释放所有large内存,并且将d->last指针重新指向ngx_pool_t结构之后数据区的开始位置,同刚创建后的位置相同。
void ngx_reset_pool(ngx_pool_t *pool)
{
ngx_pool_t *p;
ngx_pool_large_t *l;
//删除大内存块
for (l = pool->large; l; l = l->next) {
if (l->alloc) {
ngx_free(l->alloc);//专门用于释放大内存ngx_free()
}
}
//大内存块置为空
pool->large = NULL;
//重新修改每个内存块的大小
for (p = pool; p; p = p->d.next) {
p->d.last = (u_char *) p + sizeof(ngx_pool_t);
}
}

  5.注册cleanup

//cleanup结构体
struct ngx_pool_cleanup_s {
ngx_pool_cleanup_pt handler;
void *data;
ngx_pool_cleanup_t *next;
}; //注册cleanup函数,为以后清除做准备
ngx_pool_cleanup_t * ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)
{
ngx_pool_cleanup_t *c;
c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));//申请内存池
if (c == NULL) {
return NULL;
}
if (size) {
c->data = ngx_palloc(p, size); //申请数据空间
if (c->data == NULL) {
return NULL;
}
} else {
c->data = NULL;
}
c->handler = NULL;
c->next = p->cleanup;
p->cleanup = c;
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, , "add cleanup: %p", c);
return c;
}

  6.内存分配函数

void *ngx_palloc(ngx_pool_t *pool, size_t size);
void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);

  主要介绍一下ngx_palloc()这个函数:

void * ngx_palloc(ngx_pool_t *pool, size_t size)
{
u_char *m;
ngx_pool_t *p; if (size <= pool->max) {//max与待分配内存进行比较
p = pool->current;//从当前位置开始遍历pool链表 do {
m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);
if ((size_t) (p->d.end - m) >= size) {
p->d.last = m + size;
return m; //成功分配size大小的内存
}
p = p->d.next;
} while (p); return ngx_palloc_block(pool, size); //链表里没有能分配size大小内存的节点,则生成一个新的节点并在其中分配内存
}
return ngx_palloc_large(pool, size); //大于max值,则在large链表里分配内存
}

  其中的ngx_palloc_block()函数:

//该函数分配一块内存,并加入到内存池中
static void * ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
u_char *m;
size_t psize;
ngx_pool_t *p, *new, *current; psize = (size_t) (pool->d.end - (u_char *) pool); //计算内存池大小 m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log); //申请与原来相同的大小,这样的话内存池就是以2的指数幂增大
if (m == NULL) {
return NULL;
} new = (ngx_pool_t *) m; //新的内存块 new->d.end = m + psize;
new->d.next = NULL;
new->d.failed = ; m += sizeof(ngx_pool_data_t);//让m指向该块内存ngx_pool_data_t结构体之后数据区起始位
m = ngx_align_ptr(m, NGX_ALIGNMENT);
new->d.last = m + size; //在数据区分配size大小的内存并设置last指针 current = pool->current; for (p = current; p->d.next; p = p->d.next) {
if (p->d.failed++ > ) { //失败4次以上移动current指针
current = p->d.next;
}
} p->d.next = new; //将这次分配的内存块new加入该内存池
pool->current = current ? current : new;
return m;
}

参考

http://hi.baidu.com/langwan/item/fdd3bf4a4ef66aefa4c06629

http://blog.csdn.net/wallwind/article/details/7463979

http://blog.csdn.net/livelylittlefish/article/details/6586946

Nginx学习笔记(五) 源码分析&内存模块&内存对齐的更多相关文章

  1. Nginx学习笔记4 源码分析

    Nginx学习笔记(四) 源码分析 源码分析 在茫茫的源码中,看到了几个好像挺熟悉的名字(socket/UDP/shmem).那就来看看这个文件吧!从简单的开始~~~ src/os/unix/Ngx_ ...

  2. Hadoop学习笔记(9) ——源码初窥

    Hadoop学习笔记(9) ——源码初窥 之前我们把Hadoop算是入了门,下载的源码,写了HelloWorld,简要分析了其编程要点,然后也编了个较复杂的示例.接下来其实就有两条路可走了,一条是继续 ...

  3. [转]OpenTK学习笔记(1)-源码、官网地址

    OpenTK源码下载地址:https://github.com/opentk/opentk OpenTK使用Nuget安装命令:OpenTK:Install-Package OpenTK -Versi ...

  4. Netty学习篇⑥--ByteBuf源码分析

    什么是ByteBuf? ByteBuf在Netty中充当着非常重要的角色:它是在数据传输中负责装载字节数据的一个容器;其内部结构和数组类似,初始化默认长度为256,默认最大长度为Integer.MAX ...

  5. v76.01 鸿蒙内核源码分析(共享内存) | 进程间最快通讯方式 | 百篇博客分析OpenHarmony源码

    百篇博客分析|本篇为:(共享内存篇) | 进程间最快通讯方式 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析(互斥锁) | 同样 ...

  6. Memcached源码分析之内存管理

    先再说明一下,我本次分析的memcached版本是1.4.20,有些旧的版本关于内存管理的机制和数据结构与1.4.20有一定的差异(本文中会提到). 一)模型分析在开始解剖memcached关于内存管 ...

  7. MQTT再学习 -- MQTT 客户端源码分析

    MQTT 源码分析,搜索了一下发现网络上讲的很少,多是逍遥子的那几篇. 参看:逍遥子_mosquitto源码分析系列 参看:MQTT libmosquitto源码分析 参看:Mosquitto学习笔记 ...

  8. nginx健康检查模块源码分析

    nginx健康检查模块 本文所说的nginx健康检查模块是指nginx_upstream_check_module模块.nginx_upstream_check_module模块是Taobao定制的用 ...

  9. Redis学习——ae事件处理源码分析

    0. 前言 Redis在封装事件的处理采用了Reactor模式,添加了定时事件的处理.Redis处理事件是单进程单线程的,而经典Reator模式对事件是串行处理的.即如果有一个事件阻塞过久的话会导致整 ...

随机推荐

  1. Asp.net using Oracle.DataAccess.dll access oracle 11g 64bit & x86

    使用.net访问oracle数据库时一般需要在机器上安装instantclient才能正常连接. 下面介绍一种不用安装instantclient直接引用dll就用.net能连接oracle数据库的方法 ...

  2. 给pcm格式文件加wav文件头

    #include <stdlib.h>#include <stdio.h>#include <string.h>void main(){ //wav头的结构如下所示 ...

  3. VC++ 如何在显示对话框的时候,指定焦点控件!

    很简单: 在你的CAddDlg类的OnInitDialog函数中加上你上面的代码GetDlgItem(IDC_EDIT1)->SetFocus();最后记得return FALSE; 其实,不知 ...

  4. Win7 64位系统 VS2010连接Oracle报错的问题

    1,异常现象: TNS无法识别 2,异常分析:VS2010启动调试的时候,出现异常.但是使用IIS作为服务器的时候,是不会出现异常,也就是可以正常访问到数据库,后来分析是由于VS自带服务器ASP.NE ...

  5. The certificate used to sign “AppName” has either expired or has been revoked. An updated certificate is required to sign and install the application解决

    问题 The certificate used to sign "AppName" has either expired or has been revoked. An updat ...

  6. linux运维笔记——常用命令详解diff

    1.diff 你可以把diff看成是linux上的文件比对工具 例子文件内容: [root@localhost disks]# cat test1.txt a b c d [root@localhos ...

  7. CSS3--背景颜色的渐变效果

    一.渐变:gradients 线性渐变(linear gradients) background:-webkit-linear-gradient(90deg,red 10%,blue); 1.需要有内 ...

  8. DateTools时间插件

    import java.text.DateFormat;import java.text.ParsePosition;import java.text.SimpleDateFormat;import ...

  9. 20145225《Java程序设计》 第10周学习总结

    20145225<Java程序设计> 第10周学习总结 教材学习内容总结 一.网络编程 网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据: 程序员所作的事情就是把数据发送到指定 ...

  10. window 2003 配置FTP +防火墙设置

    2保险的做法是 不允许匿名登录,吧钩去掉 后面我们会添加一个用户,并且赋予权限 3 主目录 可以设置时当前计算机目录或者是另一台计算机目录(映射) FTP站点目录:浏览定位FTP文件所在站点,给予是否 ...