在nginx的进程模型下,类似流量统计、流量控制、数据共享、等需要多个工作进程共同配合完成任务,共享内存是一个重要的进程通讯的方案。本文介绍在nginx的代码中与共享内存相关的功能,包括ngx_shmem与ngx_slab的使用与注意事项,但不包括ngx_slab中实现的内存管理算法。

ngx_shmem的使用

ngx_shmem.c/h文件只是对mmap()/munmap()系统调用或者shmget()/shmdt()的一个很简单的封装。实现了ngx风格的基础库,可以申请和释放一段连续的共享内存空间。一般用于固定长度的共享数据使用,使用过程中数据长度固定不会伸缩。

typedef struct {
u_char *addr;
size_t size;
...
} ngx_shm_t;
ngx_int_t ngx_shm_alloc(ngx_shm_t *shm);
void ngx_shm_free(ngx_shm_t *shm);

在ngxin中共享内存的使用流程,一般是由master进程创建,worker进程通过继承的方式获得内存指针。

关于ngx_shmem的使用,可以参考ngx_event_module_init()中部分片段,这部分代码在共享内存中创建了若干个变量,用于记录各个状态(accepted/reading/writing...)的请求数量,并在ngx_event_module中的几个关键事件入口对这几个变量进行加减统计操作。实现统计所有worker进程当前的请求状态。

shm.size = size;
ngx_str_set(&shm.name, "nginx_shared_zone");
shm.log = cycle->log; if (ngx_shm_alloc(&shm) != NGX_OK) {
return NGX_ERROR;
} shared = shm.addr;
...
ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl);
ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl);
ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl);
ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl);
ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl);
ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl);
ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl);

关于这个功能的更多细节,可以查看代码中的NGX_STAT_STUB宏定义相关代码与ngx_http_stub_status_module。

ngx_slab的使用

ngx_shmem是一层极简的封装,实现了共享内存的基本功能。但我们程序中大部分的场景共享数据并不会一个固定大小的结构,而更多是像ngx_array、ngx_list、ngx_queue、ngx_rbtree这类大小可以变化的数据结构。

我们期望能有像ngx_pool_t一样可以动态申请释放空间一个内存池。ngx_slab正是一个这样的结构体,原理上与系统的malloc()有相识之处都是通过一系列算法实现对一段段内存片段的申请与释放。只不过ngx_slab操作的对象是基于ngx_shmem的共享内存。

先看一下ngx_slab的接口

typedef struct {
ngx_shmtx_t mutex;
...
void *data; /* 一般存放从pool中申请获得的根数据地址(pool中第一个申请的数据接口) */
void *addr; /* 使用ngx_shmem申请获得的共享内存基地址 */
} ngx_slab_pool_t; void ngx_slab_init(ngx_slab_pool_t *pool);
void *ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size);
void *ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size);
void *ngx_slab_calloc(ngx_slab_pool_t *pool, size_t size);
void *ngx_slab_calloc_locked(ngx_slab_pool_t *pool, size_t size);
void ngx_slab_free(ngx_slab_pool_t *pool, void *p);
void ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p);

可以看到接口并不复杂,alloc与calloc的区别在于是否对申请获得的内存段清零,_locked结尾的接口表示操作的pool已经是获取到锁的。在ngx_slab_pool_t的结构体有一个ngx_shmtx_t的互斥锁用于同步多进程同时访问pool的并发场景。注意ngx_slab_alloc()会先获取锁、然后申请空间、最后释放锁。而ngx_slab_alloc_locked()则直接申请空间,认为程序已经在其他逻辑中获得锁了。

在nginx的开发中使用ngx_shmem一般需要遵循以下初始化流程:

  • 模块在配置解析过程中调用ngx_shared_memory_add()接口,注册一段共享内存。提供共享内存大小与内存初始化的回调函数。
  • 框架在ngx_init_cycle()中使用ngx_shmem申请内存,并初始化ngx_slab,然后回调模块注册的初始化函数
  • 模块使用ngx_slab的申请/是否接口

在这个流程中,涉及到ngx_shared_memory_add()接口与对应的ngx_shm_zone_t结构体。

struct ngx_shm_zone_s {
void *data;
ngx_shm_t shm;
ngx_shm_zone_init_pt init;
void *tag;
void *sync;
ngx_uint_t noreuse; /* unsigned noreuse:1; */
};
ngx_shm_zone_t *ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name,
size_t size, void *tag);

其中值得一提的是noreuse属性,这个属性控制了在nginx的reload过程中是否会重新申请共享内存。

由于关于ngx_init_cycle()函数较长,这个流程可以通过查找/* create shared memory */这个注释或者cycle->shared_memory这个对象查看相关代码。

关于ngx_slab更多细节的使用,建议可以参考ngx_http_limit_conn_module,这是通过共享内存实现连接数限制的模块,模块复杂度底,是一个很好的参考范例。

参考资料

同时安利一波《深入理解Nginx》作者 陶辉 在极客时间出版的《Nginx核心知识100讲》,近期618似乎有打折活动,通过我分享的链接进行购买,我也将获得部分返现,感谢支持。

nginx中共享内存的使用的更多相关文章

  1. Nginx之共享内存与slab机制

    1. 共享内存 在 Nginx 里,一块完整的共享内存以结构体 ngx_shm_zone_t 来封装,如下: typedef struct ngx_shm_zone_s ngx_shm_zone_t; ...

  2. python学习笔记——多进程中共享内存Value & Array

    1 共享内存 基本特点: (1)共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝. (2)为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将 ...

  3. c++中共享内存原理及实现

    共享内存 (也叫内存映射文件) 主要是通过映射机制实现的 , Windows 下进程的地址空间在逻辑上是相互隔离的 , 但在物理上却是重叠的 ; 所谓的重叠是指同一块内存区域可能被多个进程同时使用 , ...

  4. 10-Java中共享内存可见性以及synchronized和volatile关键字

    Java中共享变量的内存可见性 我们首先来看一下在多线程下处理共享变量时Java的内存模型,如图所示 Java内存模型规定,将所有的变量都存放在主存中,当线程使用变量的时候,会把主内存里面的变量赋值到 ...

  5. 进程间通信 - 动态链接库中共享内存(利用DLL的2~3G的地址段空间)

    前言 进程是装入内存并准备执行的程序,每个进程都有私有的虚拟地址空间,由代码.数据,以及其他的一些资源组成.32位系统的进程分配4G的虚拟地址空间.内存地址范围是0x00000000-0xFFFFFF ...

  6. Nginx + Lua + 共享内存

    转自:http://blog.csdn.net/lxb_champagne/article/details/17099383 lua_package_path "/usr/local/sha ...

  7. Nginx之进程间的通信机制(共享内存、原子操作)

    1. 概述 Linux 提供了多种进程间传递消息的方式,如共享内存.套接字.管道.消息队列.信号等,而 Nginx 框架使用了 3 种传递消息的传递方式:共享内存.套接字.信号. 在进程间访问共享资源 ...

  8. nginx的共享字典项api(操作方法)

    nginx的共享内存,称为共享字典项,对于所有的worker进程都可见,是一种全局变量. 备注一下内容中的 [] 是 备注. 源码分析文档:https://www.codercto.com/a/948 ...

  9. UNIX环境下的共享内存

    好久没更新博客了,最近几个月一直在忙项目,现在终于有时间进一步学习了.这次记录的是unix环境中共享内存的使用方法.  在我理解,共享内存就是在内存中开辟一段空间,各个毫不相干的进程就可以通过访问这段 ...

随机推荐

  1. PHP的对象和引用

    PHP 的引用是别名,就是两个不同的变量名字指向相同的内容.在 PHP 5,一个对象变量已经不再保存整个对象的值.只是保存一个标识符来访问真正的对象内容. 当对象作为参数传递,作为结果返回,或者赋值给 ...

  2. anaconda3.5 3.6 2.7

    https://repo.continuum.io/archive/ Filename Size Last Modified MD5 Anaconda2-5.0.1-Linux-x86.sh 413. ...

  3. warning: mysql-community-libs-5.7.11-1.el7.x86_64.rpm: Header V3 DSA/SHA1 Signature, key ID 5072e1f5

    1.错误描写叙述 [root@ mysql]# rpm -ivh mysql-community-libs-5.7.11-1.el7.x86_64.rpm warning: mysql-communi ...

  4. eclipse中三大利器

    eclipse中两大利器: 首先说下用eclipse开发工具.进行java代码,开发的时候,我们开发完成以后.需要测试.大部分我们用Junit测试工具.可是内部的代码覆盖率.和结构我们看的不是那么详细 ...

  5. 【BZOJ1835】[ZJOI2010]base 基站选址 线段树+DP

    [BZOJ1835][ZJOI2010]base 基站选址 Description 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯 ...

  6. EasyPlayerPro(Windows)流媒体播放器开发之接口设计

    EasyPlayerPro(windows)接口说明如下: EasyPlayerPro_Open 说明:打开一个媒体流或者媒体文件进行播放,同时返回一个 player 对象指针 参数说明: fileU ...

  7. zookeeper curator ( 实战一)

    目录 zookeeper 的伪集群搭建 写在前面 1.1. zookeeper 安装&配置 1.1.1. 创建数据目录和日志目录: 1.1.2. 创建myid文件 1.1.3. 创建和修改配置 ...

  8. Hadoop实战-Flume之Source regex_filter(十三)

    a1.sources = r1 a1.sinks = k1 a1.channels = c1 # Describe/configure the source a1.sources.r1.type = ...

  9. 我的Android进阶之旅------>Android检测wifi连接状态

    今天要实现监听系统Wifi连接状态,下面代码简化后提取出来的,以备后用. step1. 编写BroadcastReceiver import android.content.BroadcastRece ...

  10. mysql 二:操作表

    的存储.在操作表之前,首先要用选定数据库,因为表都是建立在对应的数据库里面的.在这里我们使用之前建立的test数据库 mysql> use test; Database changed 创建表的 ...