nginx 进程通信--共享内存

共享内存是Linux下进程之间进行数据通信的最有效方式之一,而nginx就为我们提供了统一的操作接口来使用共享内存。

在nginx里,一块完整的内存以结构体ngx_shm_zone_s封装.其中包括是共享内存的名字(shm_zone[i].shm.name),大小(shm_zone[i].shm.size),标签(shm_zone[i].tag),      ngx_shm_zone_init_pt      init;  (初始化共享内存时的回调函数)

 一些共享内存的结构体:

struct ngx_shm_zone_s
    void                     *data;       
    ngx_shm_t                 shm;
    ngx_shm_zone_init_pt      init;
    void                     *tag;
};
typedef struct {
    u_char      *addr;
    size_t       size;   
    ngx_str_t    name; //名字
    ngx_log_t   *log;
    ngx_uint_t   exists;   /* unsigned  exists:1;  */
} ngx_shm_t;

      这些字段大都容易理解,只有tag字段需要解释一下,因为看上去它和name字段有点重复,而事实上,name字段主要用作共享内存的唯一标识,它能让nginx知道我想使用哪个共享内存,但它没法让nginx区分我到底是想新创建一个共享内存,还是使用那个已存在的旧的共享内存。举个例子,模块A创建了共享内存sa,模块A或另外一个模块B再以同样的名称sa去获取共享内存,那么此时nginx是返回模块A已创建的那个共享内存sa给模块A/模块B,还是直接以共享内存名重复提示模块A/模块B出错呢?不管nginx采用哪种做法都有另外一种情况出错,所以新增一个tag字段做冲突标识,该字段一般也就指向当前模块的ngx_module_t变量即可。这样在上面的例子中,通过tag字段的帮助,如果模块A/模块B再以同样的名称sa去获取模块A已创建的共享内存sa,模块A将获得它之前创建的共享内存的引用(因为模块A前后两次请求的tag相同),而模块B则将获得共享内存已做它用的错误提示(因为模块B请求的tag与之前模块A请求时的tag不同)。

     当我们要使用一个共享内存时,总会在配置文件里加上该共享内存的相关配置信息,而nginx在进行配置解析的过程中,根据这些配置信息就会创建相应的共享内存,不过此时的创建只是代表共享内存的结构体变量的ngx_shm_zone_t的创建,具体实现在share_memory_add()中.nginx中所有共享内存都是以list链表的形式组织在全局变量cf->cycle->shared_memory下,在创建新的共享内存之前会先对该链表进行遍历查找以及冲突检测,对于已经存在且不存在冲突的共享内存可直接返回引用。

以nginx中ngx_http_limit_req_module模块为例://nginx限制链接

limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;  

rate=1r/s 的意思是每个地址每秒只能请求一次,原理 burst=120 一共有120块令牌,并且每秒钟只新增1块令牌,120块令牌发完后 多出来的那些请求就会返回503

nginx在进行配置解析的时,遇到limit_req_zone配置项时,调用ngx_http_limit_req_zone(),而在该函数中继续调用share_memory_add()创建ngx_shm_zone_t结构体变量并加入到全局链表中:ngx_http_limit_req_zone() -> ngx_shared_memory_add() -> ngx_list_push()。

共享内存的真正创建是在配置文件全部解析完后,所有代表共享内存的结构体ngx_shm_zone_t变量以链表的形式挂接在全局变量cf->cycle->shared_memory下,nginx此时遍历该链表并逐个进行实际创建,即分配内存、管理机制(比如锁、slab)初始化等:

ngx_cycle_t *
ngx_init_cycle(ngx_cycle_t *old_cycle)
{ 
        part = &cycle->shared_memory.part;
        shm_zone = part->elts;
        ...............
        for (i = 0; /* void */ ; i++) {
              if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) {
            goto failed;
        }

        if (ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK) {
            goto failed;
        }

        if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) {
            goto failed;
        }
} 

     其中函数ngx_shm_alloc()时共享内存的实际分配,针对当前系统可提供的接口,可以时mmap,shmget等,而ngx_init_zone_pool()函数是共享内存管理机制的初始化,共享内存涉及2个主题:(1)多进程共同使用共享内存块,必须考虑互斥问题 (2)nginx以性能著称,那么对于共享内存自然也有其独特的使用方式,虽然我们可以不用,但在这里也默认都会以这种slab的高效访问机制进行初始化.

     回调函数shm_zone[i].init()是各个共享内存所特定的,根据使用方的自身需求不同而不同,这也是我们在使用共享内存时需特别注意的函数.

     

      函数ngx_http_limit_req_init_zone()的第二个参数data表示‘旧’数据,在进行重新加载配置时(即nginx收到SIGHUP信号)该值将不为空,如果旧数据可继续使用,那么可直接返回NGX_OK;否则,需根据自身模块逻辑对共享内存的使用做相关初始化,比如ngx_http_limit_req_module模块,在第634、642行直接使用默认已初始化好的slab机制,进行内存的分配等。当函数ngx_http_limit_req_init_zone()正确执行结束,一个完整的共享内存就已创建并初始完成,接着要做的就是共享内存的使用,这即回到前面提到的两个主题:互斥与slab。

函数

含义

ngx_shmtx_create()

创建

ngx_shmtx_destory()

销毁

ngx_shmtx_trylock()

尝试加锁(加锁失败则直接返回,不等待)

ngx_shmtx_lock()

加锁(持续等待,直到加锁成功)

ngx_shmtx_unlock()

解锁

ngx_shmtx_force_unlock()

强制解锁(可对其它进程进行解锁)

ngx_shmtx_wakeup()

唤醒等待加锁进程(系统支持信号量的情况下才可用)

 

 

 

 

 

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