初始化监听端口

Nginx端口监听机制

微信公众号:郑尔多斯
关注可了解更多的Nginx知识。任何问题或建议,请公众号留言;
关注公众号,有趣有内涵的文章第一时间送达!

初始化监听端口

前言

上文介绍了ngx_http_optimize_servers()函数的一部分内容,下面继续介绍剩下的重头戏。

初始化端口

for (p = 0; p < ports->nelts; p++) {
// 前面的内容已经介绍完了
        if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) {
            return NGX_ERROR;
        }
}

源码分析

下面我们详细介绍ngx_http_init_listening()函数,源码如下:

static ngx_int_t
ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port)
{
    ngx_uint_t                 i, last, bind_wildcard;
    ngx_listening_t           *ls;
    ngx_http_port_t           *hport;
    ngx_http_conf_addr_t      *addr;

    addr = port->addrs.elts;
    last = port->addrs.nelts;

    /*
     * If there is a binding to an "*:port" then we need to bind() to
     * the "*:port" only and ignore other implicit bindings.  The bindings
     * have been already sorted: explicit bindings are on the start, then
     * implicit bindings go, and wildcard binding is in the end.
     */
 // addr 是排过序的,放在最前面的是需要bind的
    // addr 数组最后一个元素是宽绑定。即:*:port
    // 就是监听最前面的元素的端口地址和最后一个元素的端口。
    if (addr[last - 1].opt.wildcard) {
        addr[last - 1].opt.bind = 1;
        bind_wildcard = 1;

    } else {
        bind_wildcard = 0;
    }

    i = 0;

    while (i < last) {

        if (bind_wildcard && !addr[i].opt.bind) {
        // 仔细分析一下,i的值就是那些没有显式的指定bind,将要被包含在wildcard中addr的数量
// 因为排序之后的bind在最前面,所以当出现addr[i].opt.bind=0开始,那么后面的addr.opt.bind都为0,
// 因为这是排序之后的数组。最后的一个元素的addr.opt.bind被nginx设置为了1,参考上面的代码
            i++;
            continue;
        }
 // 如果能执行到这里,那么有两个条件 
// ① bind_wildcard=0,即不存在类似 listen *:80 的这种wildcard情况
// ② bind_wildcard = 1, 即存在wildcard,但是当前的listen指令显式的指定了bind属性
        ls = ngx_http_add_listening(cf, &addr[i]);
        
        hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t));
      
        ls->servers = hport;

        if (i == last - 1) {
            hport->naddrs = last;

        } else {
            hport->naddrs = 1;
            i = 0;
        }

        switch (ls->sockaddr->sa_family) {
        default: /* AF_INET */
            if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) {
                return NGX_ERROR;
            }
            break;
        }

        addr++;
        last--;
    }

    return NGX_OK;
}

监听端口

上面的源码中有一个非常重要的函数ngx_http_add_listening(),这个函数实现了对端口的监听。

static ngx_listening_t *
ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr)
{
    ngx_listening_t           *ls;
    ngx_http_core_loc_conf_t  *clcf;
    ngx_http_core_srv_conf_t  *cscf;
// 创建一个新的ngx_listening_t结构体,表示监听的端口
    ls = ngx_create_listening(cf, &addr->opt.u.sockaddr, addr->opt.socklen);
    
    ls->addr_ntop = 1;
//下面的一行代码很重要。在accept成功之后会调用ls->handler函数
    ls->handler = ngx_http_init_connection;

    cscf = addr->default_server;
    ls->pool_size = cscf->connection_pool_size;
    ls->post_accept_timeout = cscf->client_header_timeout;

    clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index];

    ls->logp = clcf->error_log;
    ls->log.data = &ls->addr_text;
    ls->log.handler = ngx_accept_log_error;

    ls->backlog = addr->opt.backlog;
    ls->rcvbuf = addr->opt.rcvbuf;
    ls->sndbuf = addr->opt.sndbuf;

#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
    ls->accept_filter = addr->opt.accept_filter;
#endif

#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
    ls->deferred_accept = addr->opt.deferred_accept;
#endif

#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
    ls->ipv6only = addr->opt.ipv6only;
#endif

#if (NGX_HAVE_SETFIB)
    ls->setfib = addr->opt.setfib;
#endif

    return ls;
}

上面的函数又调用了ngx_create_listening(),源码如下:

ngx_listening_t *
ngx_create_listening(ngx_conf_t *cf, void *sockaddr, socklen_t socklen)
{
    size_t            len;
    ngx_listening_t  *ls;
    struct sockaddr  *sa;
    u_char            text[NGX_SOCKADDR_STRLEN];
 // cycle->listening 数组中保存了监听的端口的信息
    ls = ngx_array_push(&cf->cycle->listening);

    ngx_memzero(ls, sizeof(ngx_listening_t));
// ngx_listening_t结构的sockaddr表示的是监听的端口的结构
    sa = ngx_palloc(cf->pool, socklen);
    ngx_memcpy(sa, sockaddr, socklen);
    ls->sockaddr = sa;
    ls->socklen = socklen;

//ngx_listening_t结构的addr_text就是监听的地址信息的字符串,包括端口
    len = ngx_sock_ntop(sa, text, NGX_SOCKADDR_STRLEN, 1);
    ls->addr_text.len = len;

    switch (ls->sockaddr->sa_family) {

    case AF_INET:
         ls->addr_text_max_len = NGX_INET_ADDRSTRLEN;
         break;
     }
// ngx_listening_t结构的addr_text就是监听的地址信息的字符串,包括端口
    ls->addr_text.data = ngx_pnalloc(cf->pool, len);
    ngx_memcpy(ls->addr_text.data, text, len);

    ls->fd = (ngx_socket_t) -1;
    ls->type = SOCK_STREAM;// 表示监听的TCP

    ls->backlog = NGX_LISTEN_BACKLOG;
    ls->rcvbuf = -1;
    ls->sndbuf = -1;

#if (NGX_HAVE_SETFIB)
    ls->setfib = -1;
#endif

    return ls;
}

上面还牵涉到一个比较简单的函数ngx_sock_ntop(),这个函数的功能很简单,就是把sockaddr格式的ip地址转换为一个字符串

/*
 *参数的含义:
 * sa : 要转换为字符串的ip地址
 * text: 保存转换之后的字符串
 * len: sa参数的长度
 * port: 0或者1,表示是否将端口也转换为字符串,如果为0则不转换,若为1则转换
 * 返回值:转换的字符串的长度
*/
size_t
ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len, ngx_uint_t port)
{
    u_char               *p;
    struct sockaddr_in   *sin;
 
    switch (sa->sa_family) {
 
    case AF_INET:
 
        sin = (struct sockaddr_in *) sa;
        p = (u_char *) &sin->sin_addr;
 
        if (port) {
            p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud:%d",
                             p[0], p[1], p[2], p[3], ntohs(sin->sin_port));
        } else {
            p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud",
                             p[0], p[1], p[2], p[3]);
        }
 
        return (p - text);
    }
}

数据结构

本文牵涉到的数据结构比较多,整理如下:

typedef struct {
    /* ngx_http_in_addr_t or ngx_http_in6_addr_t */
    void        *addrs;  // ngx_http_in_addr_t 数组
    ngx_uint_t  naddrs; // 表示 addrs 数组元素的个数
} ngx_http_port_t;


typedef struct {
    in_addr_t                  addr; // 当前监听的地址
    ngx_http_addr_conf_t       conf; //参考下面的数据结构
} ngx_http_in_addr_t;


typedef struct {
    /* the default server configuration for this address:port */
    ngx_http_core_srv_conf_t  *default_server; // 当前address:port的default server
    ngx_http_virtual_names_t  *virtual_names; // 参考下面的数据结构

#if (NGX_HTTP_SSL)
    ngx_uint_t                 ssl;   /* unsigned  ssl:1; */ // 表示listen指令是否使用了ssl
#endif
} ngx_http_addr_conf_t;


typedef struct {
// 参考下面的数据结构
// name的hash指向address:port的hash
// name的wc_head指向address:port的wc_head
// name的wc_tail指向address:port的wc_tail
// 也就是说把 address:port 的 hash,wc_head,wc_tail组合成一个ngx_hash_combined_t类型的数据结构,为后面做准备.
// 因为后面使用到的都是 ngx_http_port_t 结构体,不再使用address:port对应的ngx_http_conf_addr_t结构体
     ngx_hash_combined_t              names; 
 
     ngx_uint_t                       nregex;
     ngx_http_server_name_t          *regex;
} ngx_http_virtual_names_t;


typedef struct {
    ngx_hash_t            hash;
    ngx_hash_wildcard_t  *wc_head;
    ngx_hash_wildcard_t  *wc_tail;
} ngx_hash_combined_t;

喜欢本文的朋友们,欢迎长按下图关注订阅号郑尔多斯,更多精彩内容第一时间送达
郑尔多斯

<think>好的,我现在要处理用户关于Android 12中RKNN开发配置和使用教程的问题。首先,我需要回顾用户提供的引用内容,看看是否有相关的信息。 用户提供了三个引用。引用1提到了RKNN Model Zoo和RKNN Toolkit2的环境配置,包括安装Miniconda3。引用2是关于系统镜像下载,特别是Ubuntu 22.04版本和导出ONNX模型。引用3讨论了RV1808/1126使用RKNN作为MediaPipe推理后端的步骤,包括配置参数如use_rga、use_mmap和data_type的设置,以及反量化的处理。 用户的问题集中在Android 12上,所以需要将引用中的信息与Android系统关联起来。引用3提到了MediaPipe和RKNN的集成,这可能适用于Android开发,但需要确认是否支持Android 12。此外,RKNN Toolkit2的环境配置可能在不同平台上有所不同,用户可能需要针对Android进行调整。 接下来,我需要考虑Android开发中RKNN的一般步骤,比如环境准备、模型转换、集成到Android应用、配置参数和性能优化。结合引用中的信息,模型转换部分可以参考引用2提到的导出ONNX模型,然后使用RKNN Toolkit转换为RKNN格式。引用3中的配置参数(如use_rga、use_mmap、data_type)可能在Android中同样适用,但需要查阅相关文档确认。 可能还需要指导用户如何将RKNN库集成到Android项目中,比如添加依赖项或NDK配置。此外,性能优化方面,引用3提到的反量化处理可能需要特别注意,尤其是在Android设备上的实现。 最后,确保回答结构清晰,分步骤说明,并引用提供的资料。需要验证引用中的信息是否适用于Android 12,例如系统镜像是否支持,或者是否有特定版本的要求。如果某些步骤在引用中没有明确说明,可能需要提示用户参考官方文档或社区资源。</think>### Android 12 RKNN 开发配置与使用教程 #### 1. **环境准备** - **系统镜像与工具链**: 根据引用[2],建议使用Ubuntu 22.04作为开发环境,并下载Orange Pi 5 Pro等支持RKNN的硬件镜像。对于Android 12开发,需确保RKNN Toolkit2适配Android NDK版本(推荐NDK 21+)[^2]。 - **安装RKNN Toolkit2**: 参考引用[1],通过Miniconda3配置Python环境(建议Python 3.8),并安装RKNN Toolkit2以支持模型转换与推理。命令示例: ```bash pip install rknn_toolkit2 ``` #### 2. **模型转换(ONNX转RKNN)** - **导出ONNX模型**: 使用PyTorch或TensorFlow等框架导出模型为ONNX格式(引用[2])。 - **转换为RKNN格式**: 编写RKNN配置文件(`config.yml`),指定输入尺寸、量化方式等参数。示例代码: ```python from rknn.api import RKNN rknn = RKNN() rknn.config(target_platform='rk3568', mean_values=[[127.5]], std_values=[[127.5]]) rknn.load_onnx(model="model.onnx") rknn.build(do_quantization=True, dataset="dataset.txt") rknn.export_rknn("model.rknn") ``` #### 3. **Android项目集成** - **添加RKNN库依赖**: 将编译好的RKNN动态库(`librknnrt.so`)和模型文件(`model.rknn`)放入Android项目的`jniLibs`目录。 - **JNI接口开发**: 通过JNI调用RKNN的C/C++ API实现推理。关键配置参考引用[3]: ```cpp #include <rknn/rknn_runtime.h> rknn_context ctx; int ret = rknn_init(&ctx, model_path, 0, 0, nullptr); ret = rknn_inputs_set(ctx, input_num, input_attrs); ret = rknn_run(ctx, nullptr); ret = rknn_outputs_get(ctx, output_num, output_attrs, nullptr); ``` #### 4. **关键配置参数** - **use_rga与use_mmap**: 根据引用[3],若Android设备不支持硬件加速(如RGA),需将`use_rga`和`use_mmap`设为`false`。 - **数据量化类型**: `data_type`可选`uint8`或`fp32`。若选`uint8`,需手动实现反量化;若选`fp32`,RKNN会自动处理[^3]。 #### 5. **性能优化** - **多线程推理**: 利用Android的`ThreadPoolExecutor`并行处理输入数据。 - **内存优化**: 启用`use_mmap`(若支持)减少内存拷贝开销,或通过零拷贝技术直接操作物理内存。 #### 6. **调试与验证** - **日志输出**: 启用RKNN的调试模式(`RKNN_LOG_LEVEL=DEBUG`)查看详细运行信息。 - **性能分析**: 使用`rknn.query(RKNN_QUERY_PERF_DETAIL)`获取各层耗时,针对性优化模型结构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值