nginx:“端口”之行

缘由

今天我又读了一遍 深入理解nginx的第八章。觉得又有了更深理解。不过由此带来了一个问题,书上既然说了ngx_listening_s的结构体是用于代表一个监听端口的。那么里面怎么没有一个int类型的成员代表端口呢?由这个问题,引来了我一系列探索和思考。

基础知识

刚开始的时候还有点忘了,就是端口不是单独列出来的,是和结构体
sockaddr和sockaddr_in有着密切的关系。请看下图:


其中参数sin_port就是代表端口。
那么服务器端的bind的函数是要用到这个sockaddr结构体的,bind之后就是可以listen了。

从配置文件中取值赋给ngx_cycle_t的成员

http指令

ngx_cycle_t这个结构体的位置非常特殊,每一个进程都有一个。
 经过的探索,发现是在ngx_create_listening()函数内,将端口等信息赋值了给了ngx_cycle_t结构体内的   
  • ngx_array_t  listening;
成员。
listening这个动态数组,就是专门用来保存一系列监听端口的信息。
listening的结构体是ngx_listening_t,除了很多信息意外,与端口有关的就是:

struct ngx_listening_s {
    ngx_socket_t        fd;//文件描述符

    struct sockaddr    *sockaddr;
    socklen_t           socklen;    /* size of sockaddr */
    ...
    
};

代码如下:
ngx_listening_t *
ngx_create_listening(ngx_conf_t *cf, void *sockaddr, socklen_t socklen)
{
  	...
    ls = ngx_array_push(&cf->cycle->listening);
    if (ls == NULL) {
        return NULL;
    }
    ngx_memzero(ls, sizeof(ngx_listening_t));
    sa = ngx_palloc(cf->pool, socklen);
    if (sa == NULL) {
        return NULL;
    }
    ngx_memcpy(sa, sockaddr, socklen);
    ls->sockaddr = sa;
    ls->socklen = socklen;
	...
}

这段代码还有一个值得注意的就是我刚是还在想是怎么拿到ngx_cycle_t结构体的呢?原来是在每一个ngx_conf_t 对象里面都保留了一个ngx_cycle_t的引用,所以拿到了ngx_conf_t就可以拿到ngx_cycle_t。
这段代码是如何被调用的呢?如下图所示:


可以看到,调用到这个ngx_create_listening的原因是因为解析了配置文件。代码如下所示:
static ngx_command_t  ngx_http_commands[] = {
    { ngx_string("http"),
      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
      ngx_http_block,
      0,
      0,
      NULL },


      ngx_null_command
};
就是因为解析到了http模块才会逐渐调用了ngx_create_listening这个函数。如果从运行的代码里来看的话。如下图所示:

所以,总结一下,正是因为在配置文件的http模块里写了linsten这个配置指令。那么在逐步解析的过程中,将listen里面值赋值给ngx_cycle_t的成员listening。这样该进程就可以使用在配置文件中写的值了。

lisnten指令

后来才想起来解析到listen的指令的时候也会单独调用一个方法:
    { ngx_string("listen"),
      NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
      ngx_http_core_listen,
      NGX_HTTP_SRV_CONF_OFFSET,
      0,
      NULL },
ngx_http_core_listen方法才真正去拿到配置项中
  • listen 8080;
中的8080的函数。所以我推断,ngx_http_core_listen函数内确实是拿到8080这个值,如下图所示:


我的推断

我确实还没有完全搞清楚。但是我的推断是这样:
  • 那就是ngx_cycle_t的listening动态数组,是一个存放了所有的sockaddr(里面有结构体)的地方。
  • 但是单个读取还是要靠ngx_http_core_listen的函数。
  • 那么ngx_http_core_listen先读取好了之后,就放在一个地方。
  • 在合适的时候和地方把它给ngx_cycle_t的listening动态数组。
  • 这样,才能方便之后的代码使用。
至于什么时候和什么地方。我还要再探索一下。


打开socket并监听

main函数会调用ngx_init_cycle,ngx_init_cycle非常复杂,我们先看看与监听窗口相关的函数,如下所示:
ngx_cycle_t *
ngx_init_cycle(ngx_cycle_t *old_cycle)
{
	...
	//先获得内存空间
    cycle->listening.nelts = 0;
    cycle->listening.size = sizeof(ngx_listening_t);
    cycle->listening.nalloc = n;
    cycle->listening.pool = pool;
	...
	//解析配置文件、赋值
    if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
        environ = senv;
        ngx_destroy_cycle_pools(&conf);
        return NULL;
    }
	...
	//打开监听窗口
    if (ngx_open_listening_sockets(cycle) != NGX_OK) {
        goto failed;
    }
	...
}
接着看看ngx_open_listening_sockets函数,都一些关键的信息。除去一些不重要的内容,那么我们可以看到socket、bind、listen等服务器端的socket的函数的流程。
ngx_int_t
ngx_open_listening_sockets(ngx_cycle_t *cycle)
{	
		...
        for (i = 0; i < cycle->listening.nelts; i++) {
            
            s = ngx_socket(ls[i].sockaddr->sa_family, ls[i].type, 0);
            if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) {
            }
            if (listen(s, ls[i].backlog) == -1) {


            }
        }
        ...
    return NGX_OK;
}

额外问题

因为不涉及多进程,所以我把配置文件设置为master进程和worker进程是同一个进程的单进程模式。
主要是
  • master_process off;
  • daemon off;
这两句配置指令,如此就可以用

总结

虽然博客比较短,但是有我使用了ddd(gdb)不断的在源代码的基础上调试,所以耗费比较久的时间,但是这使得我非常熟悉如何调试nginx,非常有成就感。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值