8.3 Nginx框架中的核心结构体ngx_cycle_t

Nginx核心的框架代码一直在围绕着一个结构体展开,它就是ngx_cycle_t。无论是master管理进程、worker工作进程还是cache manager(loader)进程,每一个进程都毫无例外地拥有唯一一个ngx_cycle_t结构体。服务在初始化时就以ngx_cycle_t对象为中心来提供服务,在正常运行时仍然会以ngx_cycle_t对象为中心。本节将围绕着ngx_cycle_t结构体的定义、ngx_cycle_t结构体所支持的方法来介绍Nginx框架代码,其中8.4节中的Nginx的启动流程、8.5节和8.6节中Nginx各进程的主要工作流程都是以ngx_cycle_t结构体作为基础的。下面我们来看一下ngx_cycle_t究竟有哪些成员维持了Nginx的基本框架。

8.3.1 ngx_listening_t结构体

作为一个Web服务器,Nginx首先需要监听端口并处理其中的网络事件。这本来应该属于第9章所介绍的事件模块要处理的内容,但由于监听端口这项工作是在Nginx的启动框架代码中完成的,所以暂时把它放到本章中介绍。ngx_cycle_t对象中有一个动态数组成员叫做listening,它的每个数组元素都是ngx_listening_t结构体,而每个ngx_listening_t结构体又代表着Nginx服务器监听的一个端口。在8.3.2节中的一些方法会使用ngx_listening_t结构体来处理要监听的端口,在8.4节中我们也会看到master进程、worker进程等许多进程如何监听同一个TCP端口(fork出的子进程自然共享打开的端口)。更多关于ngx_listening_t的介绍将在第9章中介绍。本节我们仅仅介绍ngx_listening_t的成员,对于它会引用到的其他对象,如ngx_connection_t等,将在第9章中介绍。下面来看一下ngx_listening_t的成员,代码如下所示。


typedef struct ngx_listening_s ngx_listening_t;

struct ngx_listening_s{

//socket套接字句柄

ngx_socket_t fd;

//监听sockaddr地址

struct sockaddr*sockaddr;

//sockaddr地址长度

socklen_t socklen;

/存储IP地址的字符串addr_text最大长度,即它指定了addr_text所分配的内存大小/

size_t addr_text_max_len;

//以字符串形式存储IP地址

ngx_str_t addr_text;

//套接字类型。例如,当type是SOCK_STREAM时,表示TCP

int type;

/TCP实现监听时的backlog队列,它表示允许正在通过三次握手建立TCP连接但还没有任何进程开始处理的连接最大个数/

int backlog;

//内核中对于这个套接字的接收缓冲区大小

int rcvbuf;

//内核中对于这个套接字的发送缓冲区大小

int sndbuf;

//当新的TCP连接成功建立后的处理方法

ngx_connection_handler_pt handler;

/实际上框架并不使用servers指针,它更多是作为一个保留指针,目前主要用于HTTP或者mail等模块,用于保存当前监听端口对应着的所有主机名/

void*servers;

//log和logp都是可用的日志对象的指针

ngx_log_t log;

ngx_log_t*logp;

//如果为新的TCP连接创建内存池,则内存池的初始大小应该是pool_size

size_t pool_size;

/TCP_DEFER_ACCEPT选项将在建立TCP连接成功且接收到用户的请求数据后,才向对监听套接字感兴趣的进程发送事件通知,而连接建立成功后,如果post_accept_timeout秒后仍然没有收到的用户数据,则内核直接丢弃连接/

ngx_msec_t post_accept_timeout;

/前一个ngx_listening_t结构,多个ngx_listening_t结构体之间由previous指针组成单链表/

ngx_listening_t*previous;

//当前监听句柄对应着的ngx_connection_t结构体

ngx_connection_t*connection;

/标志位,为1则表示在当前监听句柄有效,且执行ngx_init_cycle时不关闭监听端口,为0时则正常关闭。该标志位框架代码会自动设置/

unsigned open:1;

/*标志位,为1表示使用已有的ngx_cycle_t来初始化新的ngx_cycle_t结构体时,不关闭原先打开的监听端口,这对运行中升级程序很有用,remain为0时,表示正常关闭曾经打开的监听端口。该标志位框架代

码会自动设置,参见ngx_init_cycle方法*/

unsigned remain:1;

/标志位,为1时表示跳过设置当前ngx_listening_t结构体中的套接字,为0时正常初始化套接字。该标志位框架代码会自动设置/

unsigned ignore:1;

//表示是否已经绑定。实际上目前该标志位没有使用

unsigned bound:1;

/已经绑定/

/表示当前监听句柄是否来自前一个进程(如升级Nginx程序),如果为1,则表示来自前一个进程。一般会保留之前已经设置好的套接字,不做改变/

unsigned inherited:1;/来自前一个进程/

//目前未使用

unsigned nonblocking_accept:1;

//标志位,为1时表示当前结构体对应的套接字已经监听

unsigned listen:1;

//表示套接字是否阻塞,目前该标志位没有意义

unsigned nonblocking:1;

//目前该标志位没有意义

unsigned shared:1;

//标志位,为1时表示Nginx会将网络地址转变为字符串形式的地址

unsigned addr_ntop:1;

};


ngx_connection_handler_pt类型的handler成员表示在这个监听端口上成功建立新的TCP连接后,就会回调handler方法,它的定义很简单,如下所示。


typedef void(ngx_connection_handler_pt)(ngx_connection_tc);


它接收一个ngx_connection_t连接参数。许多事件消费模块(如HTTP框架、mail框架)都会自定义上面的handler方法。