Nginx源码分析 ——Nginx的进程通信方式


在之前分析Nginx的进程模型中我们看到,Nginx使用Master-Worker模式工作时,会有多个进程启动。那在Nginx的多进程之间采用怎样的通信方式呢?本节,我们将就Nginx的进程间通信进行分析。

一、UNIX域套接字通信

在开始介绍Nginx如何使用UNIX域套接字之前,我们讲讲UNIX域套接字通信方式,它是用于同一台机器上运行的进程之间的一种常用通信方式,它实质上是一种应用于客户端和服务端之间的API。虽然因特网域套接字也可用于同一目的,但是UNIX域套接字的效率更高。UNIX域套接字仅仅复制数据,并不执行协议处理,不需要添加或删除网络报头,无需计算校验和,不需产生顺序号,没有确认报文需要发送。UNIX域套接字提供字节流和数据报两种方式,其中数据报是提供可靠服务。

由于UNIX域套接字的高效性和稳定性,Nginx也采用了该方式来进行Master进程和Worker进程之间的通信。Nginx在创建Worker进程时调用socketpair()来创建一对未命名的UNIX域套接字,并将创建的channel[0]分配给Master进行,将channel[1]分配给Worker进程。 由于子进程(Woker进程)继承了父进程(Master进程)的资源,所以这样Master进程和Worker进程知道彼此channel端口,因而他们之间可以相互进行通信。


代码实现如下:

Master进程:

ngx_spawn_process(){

         …

         if(socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1){

         }

         ngx_channel =ngx_processes[s].channel[1];//为子进程记录Channel1

         fork();

}

Worker进程:

ngx_worker_process_cycle(){

         …

         ngx_worker_process_init(){

                   …

                   if(ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,

                              ngx_channel_handler)== NGX_ERROR){//将Channel1分配给子进程

                   }

         }

}

另外,各Worker进程之间也可以通过获取其他进程的channel端口,进而进行通信。但是就目前看到的代码而言,还没有看到Nginx的Master进程和各Worker进程间,及各Worker进程间有相互通信的内容。

二、内存共享

共享内存,永远是C/C++语言系应用下最高效快捷的通信方式,没有之一。Nginx为了提高自身的处理效率,肯定也需要使用到共享内存机制。

为了方便共享和使用,Nginx特意自定义了内存共享结构体ngx_shm_zone_t,其中包含了共享内存的名称,标签,内存的起始地址,大小,以及初始化回调函数。

typedef struct ngx_shm_zone_s 

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

struct ngx_shm_zone_s {

    void                     *data;

    ngx_shm_t                 shm;

    ngx_shm_zone_init_pt      init;

    void                     *tag;

    ngx_uint_t                noreuse;  /* unsigned noreuse:1; */

};

要启用Nginx的共享内存,需要在配置文件中进行相关配置。每个模块都可以设置自己的共享内存,Nginx在解析完配置文件的过程中,遇到有模块设置了共享内存,则新建一个ngx_shm_zone_t实例(此时仅仅分配了结构体,并未分配实际的data空间),并调用ngx_shared_memory_add()该实例以链表的形式放在cf->cycle->shared_memory下。当Nginx解析完了所有配置文件,所有的共享内存结构体都挂在了cf->cycle->shared_memory下,此时,Nginx再对链表下的结构体分配内存,以及初始化管理机制(比如锁,或者slab机制)等。

代码片段如下:

ngx_init_cycle(){

         …

         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;

        }

         }

}

为了提供内存的分配和管理效率,Nginx使用了slab机制对内存(包括共享内存)进行管理。

三、Nginx的Slab机制

为了提高内存分配效率,减少碎片的产生,Linux系统通常使用伙伴系统解决外部碎片,使用slab机制解决内部碎片。Slab机制的核心思想就是预分配,在原理上讲即是系统按照SIZE对内存进行分类管理的,当申请一块大小为SIZE的内存时,分配器就从SIZE集合中分配一个内存块(BLOCK)出去,当释放一个大小为SIZE的内存时,则将该内存块放回到原有集合,而不是释放给操作系统。当又要申请相同大小的内存时,可以复用之前被回收的内存块(BLOCK),从而避免了内存碎片的产生。这里的SIZE始终是2的幂次方,如8,16,32,64等等,即使用户申请的内存大小小于2的幂次方是,也为用户分配一个大于用户申请值的最小适合单元。这样做主要是为了提高内存分配,回收,以及寻址的速度,我们称这种特性叫做对齐。所以,Slab机制总结成两个特点,即是预分配和对齐。

Nginx的Slab机制在原理上和Linux的Slab机制是一致的。在Nginx中,slab机制和共享内存机制一起使用。前面提到,Nginx在解析完配置文件之后,将为cf->cycle->shared_memory中的所有ngx_shm_zone_t分配实际内存。而Nginx将使用slab机制对每块共享内存再进行内部划分和管理。实现是通过在共享内存初始化函数中ngx_init_zone_pool()->ngx_slab_init()实现。

代码片段如下:

ngx_init_cycle(){

           …

           if(ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK){

           }

           …

}

ngx_init_zone_pool(ngx_cycle_t *cycle, ngx_shm_zone_t *zn){

           …

           sp =(ngx_slab_pool_t *) zn->shm.addr;

           sp->end =zn->shm.addr + zn->shm.size;

    sp->min_shift = 3;

    sp->addr =zn->shm.addr;

           …

ngx_slab_init(sp);

}

ngx_slab_init(ngx_slab_pool_t *pool){

           …

           n =ngx_pagesize_shift - pool->min_shift;

 

    for (i = 0; i < n; i++){

        slots[i].slab =0;        slots[i].next =&slots[i];        slots[i].prev = 0;

    }

           …

           pages = (ngx_uint_t)(size / (ngx_pagesize + sizeof(ngx_slab_page_t)));

           …

}


由于代码细节过于繁杂,就不再对细节进行分解。我们这里归纳一下Nginx的slab机制主要方式:对共享内存进行两级管理,现将内存分为page,page的大小ngx_pagesize是操作系统定义的size (ngx_pagesize= getpagesize()),即Nginxd定义的page和操作系统内存块大小一直;再将page分为slot块进行管理,每个slot的大小由ngx_slab_exact_size (ngx_slab_exact_size = ngx_pagesize / (8 *sizeof(uintptr_t));)决定。

四、总结

通过本节的学习,我们了解了Nginx的进程通信机制,包括SocketPair方式,Linux典型的共享内存和Slab机制等内容。在后面的分享中,我们将继续分析Nginx的配置解析过程。

敬请期待。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值