nginx是多进程的运行的一个程序,对需要跨进程访问的资源,nginx提供了共享内存上的slab来管理。这里我们实现一个在slab上的红黑树,这里把代码贴出来,对流程的理解都在代码的注释里!!

首先,我们在配置阶段来创建一块共享内存,然后再该共享内存上使用nginx的slab机制,把这块共享内存管理起来,方便我们分配红黑树相关的内容。

static void
ngx_jtxy_create_shared_memory(ngx_conf_t *cf, ngx_jtxy_conf_t* jtxy_cf)
{ngx_shm_zone_t *shm_zone;void* tag = (void*)&ngx_jtxy_module;//在配置阶段调用ngx_shared_memory_add,//说明要创建一块名为sean_share_data_name的共享内存,//此时,并没有真正创建出来,要等配置阶段完成,//nginx统计出所有模块要创建的所有共享内存后,//会在ngx_init_cycle里真正的分配每一块有名的共享内存//这里再提一下,再nginx里,几乎所有的add,都是返回出一个指针的做法,//add函数内会分配出一块内存并返回这块内存的//指针给调用者使用。//比如他的list,array都是这样,拿到返回的指针后再初始化这个元素,//而不像是我们常规的做法,先初始化好一个元素,//再把这个元素添加到list,array里,这是因为nginx里list,//array都是在pool里分配的,它的add实际是向pool申请一块内存出来使用。shm_zone = ngx_shared_memory_add(cf, &jtxy_share_uri_data_name, ngx_pagesize * 16, tag);if (shm_zone->data) {//多个location都配置了的,之后的就会到这里ngx_log_error(NGX_LOG_EMERG, cf->cycle->log, 0,"ngx_jtxy_create_shared_memory %V is already add",&jtxy_share_uri_data_name);ngx_jtxy_conf_t* last_jtxy_cf = (ngx_jtxy_conf_t*)shm_zone->data;jtxy_cf->uri_shctx = last_jtxy_cf->uri_shctx;return;}ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,"ngx_jtxy_create_shared_memory  %V, shm_zone %p, sean_cf %p, ""shm_zone->data %p, noreuse %d, ngx_pagesize * 2 %d",&jtxy_share_uri_data_name, shm_zone, jtxy_cf, shm_zone->data, shm_zone->noreuse, ngx_pagesize * 2);ngx_http_jtxy_shctx_t* shctx = (ngx_http_jtxy_shctx_t*)ngx_pcalloc(cf->pool, sizeof(ngx_http_jtxy_shctx_t));if (shctx == NULL) {return;}jtxy_cf->uri_shctx = shctx;//这里为什么要设置一下init函数和data值呢,因为上面讲到//ngx_shared_memory_add时并没有真正的去分配出一块共享内存出来,//他得等到统计完所有的模块后才会去真正分配一块共享内存出来//到那时,共享内存分配出来了,就会给用户一个时机,//调用这里设置的回调ngx_sean_shared_memory_init来做一些初始化的事情。shm_zone->init = ngx_jtxy_shared_memory_init;shm_zone->data = (void*)jtxy_cf;shm_zone->noreuse = 1;  //每次重启都新建共享内存ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,"ngx_sean_create_shared_memory 2 %V, shm_zone %p, ""sean_cf %p, shm_zone->data %p, noreuse %d",&jtxy_share_uri_data_name, shm_zone, jtxy_cf, shm_zone->data, shm_zone->noreuse);
}

上面的函数就是我们在配置阶段开始分配共享内存 ,比如如下,我们就是在ngx_jtxy_block函数里调用ngx_jtxy_create_shared_memory。

typedef struct {ngx_rbtree_t                  uri_rbtree;           //统计共享内存中的红黑树ngx_rbtree_node_t             uri_rbtree_sentinel;  //统计共享内存中的红黑树
} ngx_http_jtxy_shctx_rbtree_t;    //共享内存下的红黑树typedef struct {ngx_slab_pool_t*              uri_shpool;       //统计的slab共享内存ngx_http_jtxy_shctx_rbtree_t* rbtree;           //统计共享内存中的红黑树
} ngx_http_jtxy_shctx_t;    //共享内存下的红黑树typedef struct
{ngx_http_jtxy_shctx_t*       uri_shctx; //保存共享内存的相关信息
}ngx_jtxy_conf_t;static ngx_command_t  ngx_jtxy_commands[] = {{ ngx_string("jtxy"),         //               nameNGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,   //typengx_jtxy_block,     //  char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);NGX_HTTP_LOC_CONF_OFFSET,                       //   conf0,                       //   offset NULL },              //   postngx_null_command
};...
...
...static char *ngx_jtxy_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{... ......ngx_jtxy_conf_t* jtxy_cf = (ngx_jtxy_conf_t*)conf;//uri的slab上的红黑树ngx_jtxy_create_shared_memory(cf, jtxy_cf);.........return NGX_CONF_OK;
}

在配置阶段调用ngx_shared_memory_add,说明要创建一块名为sean_share_data_name的共享内存,此时,并没有真正创建出来,要等配置阶段完成,nginx统计出所有模块要创建的所有共享内存后,会在ngx_init_cycle里真正的分配每一块有名的共享内存。因为可能在不同的location里会使用相同名称的共享内存,先做统一的名称统计,然后再分配。

这里再提一下,在nginx里,几乎所有的add函数,都是返回出一个指针的做法。add函数内会分配出一块内存并返回这块内存的指针给调用者使用。比如他的list,array都是这样,拿到返回的指针后再初始化这个元素,而不像是我们常规的做法,先初始化好一个元素,再把这个元素添加到list,array里,这是因为nginx里list,array都是在pool里分配的,它的add实际是向pool申请一块内存出来使用。

我们看看对shm_zone->data的判断和赋值

    if (shm_zone->data) {//多个location都配置了的,之后的就会到这里ngx_log_error(NGX_LOG_EMERG, cf->cycle->log, 0,"ngx_jtxy_create_shared_memory %V is already add",&jtxy_share_uri_data_name);ngx_jtxy_conf_t* last_jtxy_cf = (ngx_jtxy_conf_t*)shm_zone->data;jtxy_cf->uri_shctx = last_jtxy_cf->uri_shctx;return;}ngx_http_jtxy_shctx_t* shctx = (ngx_http_jtxy_shctx_t*)ngx_pcalloc(cf->pool, sizeof(ngx_http_jtxy_shctx_t));if (shctx == NULL) {return;}jtxy_cf->uri_shctx = shctx;shm_zone->init = ngx_jtxy_shared_memory_init;shm_zone->data = (void*)jtxy_cf;

这里的逻辑是怎么回事呢?这个共享内存确实是在一个module里创建的,但是,如果我们在多个location里都配置了该模块,那么该共享内存的配置就应该保存到每个location的配置里,ngx_jtxy_create_shared_memory传进来的参数cf和jtxy_cf就是ngx_jtxy_block传入的每个location的上下文信息。注意不同的location的cf和jtxy_cf是不同的,指针指向不同的内存块。

这里呢,我们的共享内存块只有一块,他的shm_zone->data只能保存一个值,一个location的上下文保存在了shm_zone->data里,其他的location就保存不到shm_zone->data里了,那么其他location的上下文如何拿到共享内存的值呢,当然就是上面的if (shm_zone->data)判断里了,让其他location能拿到保存着共享内存信息的那片内存的指针。

然后看着两行代码

 shm_zone->init = ngx_jtxy_shared_memory_init;shm_zone->data = (void*)jtxy_cf;

这里为什么要设置一下init函数和data值呢,因为上面讲到ngx_shared_memory_add时并没有真正的去分配出一块共享内存出来,他得等到统计完所有的模块后才会去真正分配一块共享内存出来,到那时,共享内存分配出来了,就会给用户一个时机,调用这里设置的回调ngx_sean_shared_memory_init来做一些初始化的事情。接下来再看看ngx_sean_shared_memory_init函数。

static ngx_int_t
ngx_jtxy_shared_memory_init(ngx_shm_zone_t *shm_zone, void *data)
{//void *data 共享内存重用时有用,就是上次(reload前)分配的共享内存ngx_jtxy_conf_t* jtxy_cf = (ngx_jtxy_conf_t*)shm_zone->data; //ngx_sean_conf_t//把这片共享内存设置为内存池,//可以看到,这里并没有初始化shpool的ngx_slab_pool_t结构体,//只是简单的把shm_zone->shm.addr赋值给了shpool,//难道不用初始化shpool吗,其实初始化ngx_slab_pool_t的过程nginx已经做了。//在ngx_init_zone_pool函数里//ngx_init_cycle会调用ngx_init_zone_pool来完成。//这里我们就只是保存一下,拿到他的指针而已jtxy_cf->uri_shctx->uri_shpool = (ngx_slab_pool_t *)shm_zone->shm.addr;if (shm_zone->shm.exists) {//如果这片共享内存池之前已经存在了,拿一下之前的共享内存上的shctx//为什么会有这段代码呢,因为我们创建的是有名的共享内存,//即使nginx停掉了,这块内存还是存在的,//等nginx重新启动后,拿到这片共享内存后就会在这里得到之前的上下文。//为什么会重用这个共享内存呢,因为shm_zone->noreuse被设置为了0,//这也nginx是默认设置(ngx_shared_memory_add里),它表明是否复用之前的共享内存// jtxy_cf->uri_shctx = (ngx_http_jtxy_shctx_t*)jtxy_cf->uri_shctx->uri_shpool->data; /to do //return NGX_OK;}//到这里呢我们就可以开始使用者片共享内存了,//要注意的是所有的内存分配都要是在这片共享内存上//这里只看到在shpool上分配内存了,实际shpool还要调用ngx_slab_init初始化,//这个过程应该是系统里帮我们做了//比如下面的代码,就是分配一个红黑树的东西并做初始化,jtxy_cf->uri_shctx->rbtree = (ngx_http_jtxy_shctx_rbtree_t*)ngx_slab_alloc(jtxy_cf->uri_shctx->uri_shpool, sizeof(ngx_http_jtxy_shctx_rbtree_t));if (jtxy_cf->uri_shctx->rbtree == NULL) {ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,"ngx_slab_alloc %V, ngx_http_sean_shctx_t failed");return NGX_ERROR;}//把分配出来的值存到uri_shpool得data上下文中,因为如果shm_zone->shm.exists存在,还要用的jtxy_cf->uri_shctx->uri_shpool->data = jtxy_cf->uri_shctx->rbtree;ngx_rbtree_init(&jtxy_cf->uri_shctx->rbtree->uri_rbtree, &jtxy_cf->uri_shctx->rbtree->uri_rbtree_sentinel, ngx_str_rbtree_insert_value);return NGX_OK;
}

我们看看这句代码

jtxy_cf->uri_shctx->uri_shpool = (ngx_slab_pool_t *)shm_zone->shm.addr;

可以看到,这里并没有初始化shpool的ngx_slab_pool_t结构体,只是简单的把shm_zone->shm.addr赋值给了shpool,难道不用初始化shpool吗,其实初始化ngx_slab_pool_t的过程nginx已经做了。在ngx_init_zone_pool函数里ngx_init_cycle会调用ngx_init_zone_pool来完成。这里我们就只是保存一下,拿到他的指针而已。

if (shm_zone->shm.exists)这句判断是什么意思呢?

如果这片共享内存池之前已经存在了,拿一下之前的共享内存上的shctx,为什么会有这段代码呢,因为我们创建的是有名的共享内存,即使nginx停掉了,这块内存还是存在的,等nginx重新启动后,拿到这片共享内存后就会在这里得到之前的上下文。

为什么会重用这个共享内存呢?

因为shm_zone->noreuse被设置为了0,这也nginx是默认设置(ngx_shared_memory_add里),它表明是否复用之前的共享内存。我们这里设置的是

shm_zone->noreuse = 1;  //每次重启都新建共享内存

可以不管if内的处理。

接下来就是调用ngx_slab_alloc和ngx_rbtree_init来创建一个在共享内存上的红黑树了。这个看看上面的代码就明白了。

因为是多进程共享这一棵树,那么使用时就需要加锁。这里没有slab上的锁操作,是因为目前还在初始化阶段,不涉及资源竞争的问题。

当需要使用这棵树时是这样的,这时就需要加锁了

ngx_shmtx_lock(&jtxy_cf->uri_shctx->uri_shpool->mutex);uriinfo_node = ngx_str_rbtree_lookup(&jtxy_cf->uri_shctx->rbtree->uri_rbtree, token, hash);
...
...
...
ngx_shmtx_unlock(&jtxy_cf->uri_shctx->uri_shpool->mutex);
ngx_shmtx_lock(&jtxy_cf->uri_shctx->uri_shpool->mutex);ngx_rbtree_insert(&jtxy_cf->uri_shctx->rbtree->uri_rbtree, &jtxyreqinfo->key_node.node);
...
...
...
ngx_shmtx_unlock(&jtxy_cf->uri_shctx->uri_shpool->mutex);

最后,我们可以通过ngx_slab_stat模块开查看slab内存的使用情况,如下,ngx_slab_stat是Tengine提供的一个模块,我们可以把他编译进nginx里使用,方便了我们查看slab的内容。

Hello World!root@DESKTOP-EMC868D:~# curl -v --noproxy "*" http://127.0.0.1:8881/slab_stat
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8881 (#0)
> GET /slab_stat HTTP/1.1
> Host: 127.0.0.1:8881
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.20.0
< Date: Thu, 13 Jan 2022 08:02:09 GMT
< Content-Length: 2120
< Connection: keep-alive
<
* shared memory: sean_share_data
total:          16(KB) free:           8(KB) size:           4(KB)
pages:           8(KB) start:00007FFFF7FF4000 end:00007FFFF7FF7000
slot:           8(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:          16(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:          32(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:          64(Bytes) total:          64 used:           1 reqs:           1 fails:           0
slot:         128(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:         256(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:         512(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:        1024(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:        2048(Bytes) total:           0 used:           0 reqs:           0 fails:           0
* shared memory: jtxy_share_uri_data
total:          64(KB) free:          56(KB) size:           4(KB)
pages:          56(KB) start:00007FFFF7FE4000 end:00007FFFF7FF3000
slot:           8(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:          16(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:          32(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:          64(Bytes) total:          64 used:           1 reqs:           1 fails:           0
slot:         128(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:         256(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:         512(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:        1024(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:        2048(Bytes) total:           0 used:           0 reqs:           0 fails:           0

我们看

slot:          64(Bytes) total:          64 used:           1 reqs:           1 fails:           0

表示slot为64bytes的块有64个,使用了1个,请求了1次,失败0次

如果64个都使用完了呢,就会在分配一页出来给64bytes的slot使用。

total:          64(KB) free:          56(KB) size:           4(KB)
pages:          56(KB) start:00007FFFF7FE9000 end:00007FFFF7FF8000
slot:           8(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:          16(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:          32(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:          64(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:         128(Bytes) total:          32 used:          32 reqs:          32 fails:           0
slot:         256(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:         512(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:        1024(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:        2048(Bytes) total:           0 used:           0 reqs:           0 fails:           0

比如上面,128bytes的32个slot都使用完了,我们再请求分配会如何呢,如下

slot:           8(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:          16(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:          32(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:          64(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:         128(Bytes) total:          64 used:          33 reqs:          33 fails:           0
slot:         256(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:         512(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:        1024(Bytes) total:           0 used:           0 reqs:           0 fails:           0
slot:        2048(Bytes) total:           0 used:           0 reqs:           0 fails:           0

128bytes的slot变为了64个,使用了33个。可见系统又拿出一页来给128bytes的slot使用

nginx使用ngx_shared_memory_add和ngx_slab_alloc等在共享内存里创建一棵红黑树相关推荐

  1. 共享内存(一)—— 共享内存的创建与释放

    匿名管道和命名管道都是基于文件的进程间通信,SystemV方案是在OS层面专门为进程间通信设计的一个方案,然后通过系统调用(system call)给用户提供通信接口 SystemV方案包含三种:共享 ...

  2. SystemV 共享内存(一)—— 共享内存的创建与释放(shmget / shmctl)

    匿名管道和命名管道都是基于文件的进程间通信,SystemV方案是在OS内核层面专门为进程间通信设计的一个方案,然后通过系统调用(system call)给用户提供通信接口 SystemV方案包含三种: ...

  3. 共享内存的创建和映射shmget()和shmat()

    共享内存的实现分为两步: 1.创建共享内存,用到的函数是shmget(); 2.映射共享内存,就是把这段创建的共享内存映射到具体的进程空间去,使用的函数是:shmat(); 这样就可以使用不带缓冲的I ...

  4. VC共享内存的创建和权限问题

    最近做的项目中,由用户双击启动的应用创建了共享内存,调用CreateFileMapping,按百度到的方式,发现在PHP以服务启动(运行在SYSTEM用户下)无法访问到该共享内存的内容,数值都是0. ...

  5. 高通平台smd分析及smem共享内存的创建笔记

    http://blog.csdn.net/whshiyun/article/details/79447959 转载于:https://www.cnblogs.com/RunnigGift/p/8581 ...

  6. 第三十六讲:用好共享内存工具:Slab管理器

    刚刚我们谈到nginx不同的worker进程间需要共享信息的时候,需要通过共享内存:我们也谈到了共享内存上可以使用链表或者红黑树这样的数据结构;但是每一个红黑树上有许多节点:每一个节点你需要分配内存去 ...

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

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

  8. mmap和shm共享内存的区别和联系

    共享内存的创建 根据理论: 1. 共享内存允许两个或多个进程共享一给定的存储区,因为数据不需要来回复制,所以是最快的一种进程间通信机制.共享内存可以通过mmap()映射普通文件(特殊情况下还可以采用匿 ...

  9. ngx_lua模块中的共享内存字典项API

    在ngx_lua模块中使用共享内存字典项相关API的前提条件是已经使用lua_shared_dict命令定义了一个字典项对象,该命令的具体用法为: 语法:lua_shared_dict <nam ...

最新文章

  1. 2021China SAFe Day中国规模化敏捷会
  2. 国内ntp服务器ip地址
  3. 文档管理套包Aspose.Total 5月更新 | 附下载
  4. 安装完python怎么打开安装步骤-Python安装与运行测试详细教程,带你攻克第一个学习难关...
  5. java 创建日程到期提醒_苹果“快捷指令”日程播报完美版
  6. 20201014 《计算感知》第2节课 笔记
  7. android studio插入数据表中没有_学Java能拿高薪吗 Java中常见排序算法有哪些
  8. VGA光纤收发器特点及参数
  9. linux查看历史的所有命令,linux查看历史命令history
  10. ASP.NET MVC实践系列11-FCKEditor和CKEditor的使用
  11. iterator adapter reverse_iterator
  12. SQL Serve——版本
  13. SCVMM2012 SP1 之添加非受信任Hyper-V
  14. TP5代码一键生成(萤火小程序新增功能开发辅助)及开发记录文档
  15. SAP 荣获「中国好公司」头衔
  16. 教你仿写 Flipboard 的翻页效果
  17. 微信公众号开发系列-12、微信前端开发利器:WeUI
  18. 最完美安装amd显卡驱动方法
  19. 远程连接服务器---SSH详解
  20. 7-152 百钱百鸡

热门文章

  1. 故事:两只老虎的悲惨结局
  2. PG+POSTGIS地图空间位置网格聚合算法
  3. python英语词汇读音_40行Python代码区分英语单词和汉语拼音
  4. 空空老师向青海地震捐款~~把我感动毁了
  5. Nacos注册中心8-Server端(处理注册请求)
  6. 基于卷积神经网络和小波变换的视频监控中的火灾探测
  7. 2019年51CTO学院发布课程回顾总结-引莫(孙忠)
  8. 坦克大战(Tank Battalion)------Java代码实现
  9. 彻底弄懂JS的事件冒泡和事件捕获
  10. Darknet - 模型 (.weights) 重命名