共享内存

     我们知道,如果我们模块需要一块共享的内存,需要调用ngx_share_memory_add来创建共享内存,而该函数不会马上创建一个共享内存,它是先登记一下共享内存的信息,例如名称,大小,然后在进程初始化时再进行共享内存的创建和初始化.

     ngx_shared_memory_add这个函数是将共享内存的分配登记在哪里,在ngx_cycle_s这个结构体中有一个成员,即ngx_cycle_s->shared_memory,它是个list,用来保存所有登记的共享内存,这个list中保存的ngx_shm_zone_t类型的数据,

      ngx_shm_zone_t的结构体:

struct ngx_shm_zone_s {  
    // 这里可以指向自定义的一个数据结构,主要是为了在数据初始化的时候使用到,或通过共享内存直接拿到与共享内存相关的数据,它不一定指向共享内存中的地址  
    void                     *data;  
    // 实际的共享内存  
    ngx_shm_t                 shm;  
    // 共享内存的初始化函数  
    ngx_shm_zone_init_pt      init;  
    // 标记  
    void                     *tag;  
};  

  研究一下ngx_shared_memory_add这个函数的实现,该函数先检查要添加的共享内存是否存在,如果存在,直接返回.如果不存在,创建一个新的.

1. 两个相同名字的共享内存大小要一样。
2. 两个相同名字的共享内存tag要一样。
3. 如果当前共享内存已经存在,则不需要再次添加。会返回同一个共享内存
4. 如果此共享内存不存在,则添加一个新的ngx_shm_zone_t
添加完后,会返回ngx_shm_zone_t,然后再设置init函数与data数据。

ngx_shm_zone_t *
ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag)//tag为某一模块
{
    ngx_uint_t        i;
    ngx_shm_zone_t   *shm_zone;
    ngx_list_part_t  *part;

//先查找是否共享内存是否存在,存在 返回,不存在 创建 part
= &cf->cycle->shared_memory.part; shm_zone = part->elts; for (i = 0; /* void */ ; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; shm_zone = part->elts; i = 0; } if (name->len != shm_zone[i].shm.name.len) { continue; } if (ngx_strncmp(name->data, shm_zone[i].shm.name.data, name->len) != 0) { continue; } if (tag != shm_zone[i].tag) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the shared memory zone \"%V\" is " "already declared for a different use", &shm_zone[i].shm.name); return NULL; } if (size && size != shm_zone[i].shm.size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the size %uz of shared memory zone \"%V\" " "conflicts with already declared size %uz", size, &shm_zone[i].shm.name, shm_zone[i].shm.size); return NULL; } //如果共享内存已经存在,则直接返回共享内存 return &shm_zone[i]; } shm_zone = ngx_list_push(&cf->cycle->shared_memory); if (shm_zone == NULL) { return NULL; } shm_zone->data = NULL; shm_zone->shm.log = cf->cycle->log; shm_zone->shm.size = size; shm_zone->shm.name = *name; shm_zone->shm.exists = 0; shm_zone->init = NULL; shm_zone->tag = tag; //返回一个新的共享内存 return shm_zone; }

     一般,在我们的模块中,我们会保存ngx_shared_memory_add所创建的ngx_shm_zone_t,然后设置我们自己的初始化函数,ngx_shm_zone_t->init,这样,在进程初始化的时候,会创建这个共享内存,然后会调用我们的init函数。这个init函数的原型是:

    typedef ngx_int_t (*ngx_shm_zone_init_pt) (ngx_shm_zone_t *zone, void *data);

    添加共享内存代码实例:

hm_zone = ngx_shared_memory_add(cf, &name, size,  
                                 &ngx_http_limit_req_module);  
if (shm_zone == NULL) {  
    return NGX_CONF_ERROR;  
}  
  
shm_zone->init = ngx_http_limit_req_init_zone;  
shm_zone->data = ctx;  

    在进行初始化时,就会创建共享内存了,我们来看看它是如何做的,初始化工作是在ngx_init_cycle这个函数里面做的,这里我们只看进行共享内存初始化的代码.

    

ngx_cycle_t *  
ngx_init_cycle(ngx_cycle_t *old_cycle)  
{  
 ........  
   /* create shared memory */  
  
    part = &cycle->shared_memory.part;  
    shm_zone = part->elts;  
    for (i = 0; /* void */ ; i++) {  
        if (i >= part->nelts) {  
            if (part->next == NULL) {  
                break;  
            }  
            part = part->next;  
            shm_zone = part->elts;  
            i = 0;  
        }  
        if (shm_zone[i].shm.size == 0) {  
            ngx_log_error(NGX_LOG_EMERG, log, 0,  
                          "zero size shared memory zone "%V"",  
                          &shm_zone[i].shm.name);  
            goto failed;  
        }  
  
        // 跳过未使用的共享内存  
        if (shm_zone[i].init == NULL) {  
            /* unused shared zone */  
            continue;  
        }  
  
        shm_zone[i].shm.log = cycle->log;  
  
        // 这里主要是考虑到在做reload等操作时,应该如何做  
        opart = &old_cycle->shared_memory.part;  
        oshm_zone = opart->elts;  
        for (n = 0; /* void */ ; n++) {  
  
            if (n >= opart->nelts) {  
                if (opart->next == NULL) {  
                    break;  
                }  
                opart = opart->next;  
                oshm_zone = opart->elts;  
                n = 0;  
            }  
            if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) {  
                continue;  
            }  
            if (ngx_strncmp(shm_zone[i].shm.name.data,  
                            oshm_zone[n].shm.name.data,  
                            shm_zone[i].shm.name.len)  
                != 0)  
            {  
                continue;  
            }  
            // 如果新的共享内存的大小与原有的共享内存大小相同,就不需要重新添加了  
            if (shm_zone[i].shm.size == oshm_zone[n].shm.size) {  
                // 新的共享内存直接指向已经存在的共享内存的地址  
                shm_zone[i].shm.addr = oshm_zone[n].shm.addr;  
                // 当然,还是需要重新初始化一下的,因为重新初始化的函数中,可能会有一些对本地内存的操作,比如在某个本地内存结构体中保存共享内存地址等,所以在我们的初始化函数中,要小心处理  
                if (shm_zone[i].init(&shm_zone[i], oshm_zone[n].data)  
                    != NGX_OK)  
                {  
                    goto failed;  
                }  
  
                goto shm_zone_found;  
            }  
  
            // 如果不存在,则释放掉老的共享内存  
            // 注意,如果新配置的共享内存大小与老的共享内存大小不一样,那老的共享内存大小就被释放掉了,所以这点我们要特别注意  
            ngx_shm_free(&oshm_zone[n].shm);  
  
            break;  
        }  
  
        // 创建共享内存  
        if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) {  
            goto failed;  
        }  
  
        // 初始化共享内存池,这里主要是初始化为slab来管理内存池  
        if (ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK) {  
            goto failed;  
        }  
  
        // 这里调用我们自己的初始化函数,注意第二个参数是NULL的  
        if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) {  
            goto failed;  
        }  
  
    shm_zone_found:  
  
        continue;  
    }  
 ........  

     我们看到,在对每一个共享内存,先调用ngx_shm_alloc创建共享内存,然后调用ngx_init_zone_pool对共享内存块进行初始化,然后调用我们自己添加的共享内存init函数。ngx_init_zone_pool函数是对共享内存进行slab的初始化,之后,我们就可以通过slab进行共享内存的管理.接下来,在我们的init函数里面,将共享内存强制转化成slab。以后我们对共享内存的分配和释放 ,就可以通过slab实现.

  ngx_init_zone_pool函数实现:

static ngx_int_t  
ngx_init_zone_pool(ngx_cycle_t *cycle, ngx_shm_zone_t *zn)  
{  
    u_char           *file;  
    ngx_slab_pool_t  *sp;  
  
    // 使用slab来进行共享内存的管理  
    sp = (ngx_slab_pool_t *) zn->shm.addr;  
  
    if (zn->shm.exists) {  
  
        if (sp == sp->addr) {  
            return NGX_OK;  
        }  
  
        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,  
                      "shared zone "%V" has no equal addresses: %p vs %p",  
                      &zn->shm.name, sp->addr, sp);  
        return NGX_ERROR;  
    }  
  
    // 初始化slab分配器  
    sp->end = zn->shm.addr + zn->shm.size;  
    sp->min_shift = 3;  
    sp->addr = zn->shm.addr;  
  
#if (NGX_HAVE_ATOMIC_OPS)  
  
    file = NULL;  
  
#else  
  
    file = ngx_pnalloc(cycle->pool, cycle->lock_file.len + zn->shm.name.len);  
    if (file == NULL) {  
        return NGX_ERROR;  
    }  
  
    (void) ngx_sprintf(file, "%V%V%Z", &cycle->lock_file, &zn->shm.name);  
  
#endif  
  
    // 创建此共享内存的锁  
    if (ngx_shmtx_create(&sp->mutex, (void *) &sp->lock, file) != NGX_OK) {  
        return NGX_ERROR;  
    }  
  
    // 初始化slab  
    ngx_slab_init(sp);  
  
    return NGX_OK;  
}  

在nginx_init_zone_pool之后,我们在共享内存里面分配的工作,就需要slab.

另外,共享内存实际的分配函数是ngx_shm_alloc(),nginx里面包含了共享内存的多种实现方式,lunix中默认使用mmap实现.

ngx_int_t  
ngx_shm_alloc(ngx_shm_t *shm)  
{  
    shm->addr = (u_char *) mmap(NULL, shm->size,  
                                PROT_READ|PROT_WRITE,  
                                MAP_ANON|MAP_SHARED, -1, 0);  
  
    if (shm->addr == MAP_FAILED) {  
        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,  
                      "mmap(MAP_ANON|MAP_SHARED, %uz) failed", shm->size);  
        return NGX_ERROR;  
    }  
  
    return NGX_OK;  
}  

 

 

   

posted @ 2012-11-26 17:49  风去无痕  阅读(1636)  评论(0编辑  收藏  举报