nginx 模块解析

      nginx的模块非常之多,可以认为所有代码都是以模块的形式组织,这包括核心模块和功能模块,针对不同的应用场合,并非所有的功能模块都要被用到,附录A给出的是默认configure(即简单的http服务器应用)下被连接的模块,这里虽说是模块连接,但nginx不会像apache或lighttpd那样在编译时生成so动态库而在程序执行时再进行动态加载,nginx模块源文件会在生成nginx时就直接被编译到其二进制执行文件中,所以如果要选用不同的功能模块,必须对nginx做重新配置和编译。对于功能模块的选择,如果要修改默认值,需要在进行configure时进行指定,比如新增http_flv功能模块(默认是没有这个功能的,各个选项的默认值可以在文件auto/options内看到)

    [root@localhost nginx-1.2.0]# ./configure --with-http_flv_module

执行后,生成的objs/ngx_modules.c文件内就包含有对ngx_http_flv_module模块的引用了,要再去掉http_flv功能模块,则需要重新configure,即不带--with-http_flv_module配置后再编译生成新的nginx执行程序。通过执行./configure –help,我们可以看到更多的配置选项。

   虽然nginx的模块的很多,并且某个模块的功能各不相同,但是可以根据功能特性,我们大致可以分为四类:

   1) handlers : 处理客户端请求并产生响应内容,比如ngx_http_static_moudle模块,负责客户端的静态页面请求,并将对应的静态磁盘文件作为响应内容输出.

   2)filters : 对handlers产生的响应内容做各种过滤处理(即增,删,改),比如 ngx_http_not_modify_filter_moudle,如果通过时间判断前后2次请求的响应内容没有发生任何改变,那么可以直接响应"304 Not Modified"状态标识,让客户端使用缓存即可,而原本发送的响应内容将被清除掉.

  3)upstream : 如果存在后端真实的服务器,nginx 可以利用upstream模块充当反向代理的角色,对客户端的请求只负责转发到后端的真实服务器,如ngx_http_proxy_moudle模块.

  4)load-balance : 在nginx充当中间代理时,由于后端真实服务器往往多于一个,对于某一次客户端的请求,如何选择对应的后端真实服务器来进行处理,这就有类似于ngx_http_upstream_ip_hash_module这样的模块来实现不同的负载均衡算法(Load Balance)。

 

在此,我们先来了解一些数据结构:

struct ngx_module_s {ngx_uint_t            ctx_index; //在同类模块中的序号ngx_uint_t            index;     //在所有模块中序号ngx_uint_t            spare0;ngx_uint_t            spare1;ngx_uint_t            spare2;ngx_uint_t            spare3;ngx_uint_t            version;  //当前模块的版本号void                 *ctx;     //指向当前模块特有的数据ngx_command_t        *commands;  //指向当前模块配置项解析数组ngx_uint_t            type;   //模块的类型//回调函数ngx_int_t           (*init_master)(ngx_log_t *log);ngx_int_t           (*init_module)(ngx_cycle_t *cycle);ngx_int_t           (*init_process)(ngx_cycle_t *cycle);ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);void                (*exit_thread)(ngx_cycle_t *cycle);void                (*exit_process)(ngx_cycle_t *cycle);void                (*exit_master)(ngx_cycle_t *cycle);//保留字uintptr_t             spare_hook0;uintptr_t             spare_hook1;uintptr_t             spare_hook2;uintptr_t             spare_hook3;uintptr_t             spare_hook4;uintptr_t             spare_hook5;uintptr_t             spare_hook6;uintptr_t             spare_hook7;
};

    结构体ngx_module_s中值得注意的几个字段 ctx , commands, type,其中commands字段表示当前模块的可以解析的配置项目,表示模块类型的type值只有5种,而同一类型的模块的ctx数据类型都是相同的。

序号

type值

ctx指向数据类型

1

NGX_CORE_MODULE

ngx_core_module_t

2

NGX_EVENT_MODULE

ngx_event_module_t

3

NGX_CONF_MODULE

NULL

4

NGX_HTTP_MODULE

ngx_http_module_t

5

NGX_MAIL_MODULE

ngx_mail_module_t

typedef struct {ngx_str_t             name;void               *(*create_conf)(ngx_cycle_t *cycle);char               *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;
............................
typedef struct {ngx_str_t              *name;void                 *(*create_conf)(ngx_cycle_t *cycle);char                 *(*init_conf)(ngx_cycle_t *cycle, void *conf);ngx_event_actions_t     actions;
} ngx_event_module_t;
........................typedef struct {ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);void       *(*create_main_conf)(ngx_conf_t *cf);char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);void       *(*create_srv_conf)(ngx_conf_t *cf);char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);void       *(*create_loc_conf)(ngx_conf_t *cf);char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;

    上面表中的第3列的数据类型比较重要,它的字段基本上都是一些回调函数,这些回调函数会在其模块对应的配置文件解析过程 前/中/后 会适时被调用,做一些内存准备,初始化,配置值检查,配置值填充,合并,回调函数挂载等初始工作.

    下面我们以ngx_http_core_moudle模块为例,type 为 NGX_HTTP_MOUDLE, ctx 指向ngx_http_moudle_t结构体变量ngx_http_core_module_ctx.

static ngx_http_module_t  ngx_http_core_module_ctx = {ngx_http_core_preconfiguration,        /* preconfiguration */NULL,                                  /* postconfiguration */ngx_http_core_create_main_conf,        /* create main configuration */ngx_http_core_init_main_conf,          /* init main configuration */ngx_http_core_create_srv_conf,         /* create server configuration */ngx_http_core_merge_srv_conf,          /* merge server configuration */ngx_http_core_create_loc_conf,         /* create location configuration */ngx_http_core_merge_loc_conf           /* merge location configuration */
};

     根据上面的代码,我们可以很明显看到各个回调函数的回调时机,例如:ngx_http_core_preconfiguration将在进行http块配置解析前被调用,所以内在ngx_http_block()函数里看到这样的代码:

static char *
ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{for (m = 0; ngx_modules[m]; m++) {if (ngx_modules[m]->type != NGX_HTTP_MODULE) {continue;}module = ngx_modules[m]->ctx;if (module->preconfiguration) {if (module->preconfiguration(cf) != NGX_OK) {return NGX_CONF_ERROR;}}}
}

至于这些回调函数内的具体逻辑,如前所述一般是一些初始或默认值填充工作,但也有回调函数挂载的设置,比如ngx_http_static_module模块的postconfiguration字段回调函数ngx_http_static_init()就是将自己的处理函数ngx_http_static_handler()挂载在http处理状态机上,但总体来看这毕竟都只是一些简单的初始准备工作.

   handlers模块

      对于客户端http请求过程,为了获得更强的控制力,nginx将其细分成多个阶段处理,每个阶段都有零个或多个回调函数专门处理,当我们编写handler模块的时候,必须把模块功能挂载在正确的阶段点。如前面所描述的ngx_http_static_moudle 将自己的功能模块处理函数ngx_http_static_handler()挂载在NGX_HTTP_CONTENT_PHASE阶段.

     Http请求处理过程一共分为11个阶段,每一个阶段对应的处理功能都比较单一,这样能尽量让nginx模块代码更为内聚:

    

序号

阶段宏名

阶段描述

0

NGX_HTTP_POST_READ_PHASE

读取请求内容阶段

1

NGX_HTTP_SERVER_REWRITE_PHASE

Server请求地址重写阶段

2

NGX_HTTP_FIND_CONFIG_PHASE

配置查找阶段

3

NGX_HTTP_REWRITE_PHASE

Location请求地址重写阶段

4

NGX_HTTP_POST_REWRITE_PHASE

请求地址重写提交阶段

5

NGX_HTTP_PREACCESS_PHASE

访问权限检查准备阶段

6

NGX_HTTP_ACCESS_PHASE

访问权限检查阶段

7

NGX_HTTP_POST_ACCESS_PHASE

访问权限检查提交阶段

8

NGX_HTTP_TRY_FILES_PHASE

配置项try_files处理阶段 

9

NGX_HTTP_CONTENT_PHASE

内容产生阶段

10

NGX_HTTP_LOG_PHASE

日志模块处理阶段

    并非某个阶段都能挂载自定义的回调函数,比如NGX_HTTP_TRY_FILE_PHASE阶段就是针对配置项try_files的特定处理阶段段。NGX_HTTP_FIND_CONFIG_PHASE、NGX_HTTP_POST_ACCESS_PHASE与NGX_HTTP_POST_REWRITE_PHASE这三个阶段也是为了完成nginx特定的功能,就算给这几个阶段加上回调函数,也永远不会被调用。我们的自定义模块回调函数挂载在NGX_HTTP_CONTENT_PHASE阶段的情况比较多,毕竟大部分情况下的业务需求是修改HTTP响应数据,nginx自身的产生响应内容的模块,像ngx_http_static_module、ngx_http_random_index_module、ngx_http_index_module、ngx_http_gzip_static_module、ngx_http_dav_module等都是挂载在这个阶段。

 

   大多数情况下,功能模块会在其对应配置解析完后的回调函数,也就是ngx_http_moudle_t结构体的postconfiguration字段指向的函数内将当前模块的回调功能函数挂载到这11个阶段其中一个上.

以ngx_http_static_module为例:

ngx_http_module_t  ngx_http_static_module_ctx = {NULL,                                  /* preconfiguration */ngx_http_static_init,                  /* postconfiguration */NULL,                                  /* create main configuration */NULL,                                  /* init main configuration */NULL,                                  /* create server configuration */NULL,                                  /* merge server configuration */NULL,                                  /* create location configuration */NULL                                   /* merge location configuration */
};static ngx_int_t
ngx_http_static_init(ngx_conf_t *cf)
{ngx_http_handler_pt        *h;ngx_http_core_main_conf_t  *cmcf;cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);if (h == NULL) {return NGX_ERROR;}*h = ngx_http_static_handler;  return NGX_OK;

    在模块ngx_http_static_module的postconfiguration回调函数ngx_http_static_init()内,将ngx_http_static_module模块的核心功能函数ngx_http_static_handler()挂载在Http请求处理流程中的NGX_HTTP_CONTENT_PHASE阶段。这样,当一个客户端的http静态页面请求发送到nginx服务器,nginx就能够调用到我们这里注册的ngx_http_static_handler()函数,

   

各个功能模块将其自身的功能函数挂载在cmcf->phases后,内部的情况如下图所示: 

回调函数会根据模块的不同而不同.这些回调函数的调用都时有条件的,调用后也要做一些根据返回值的结果处理.比如某次处理是否进入到阶段NGX_HTTP_CONTENT_PARSE的回调函数的处理,这需要一个事前判断.所以在函数ngx_http_init_phase_handlers()里对所有这个回调函数进行一次重组.

struct ngx_http_phase_handler_s {ngx_http_phase_handler_pt checker;  //阶段检查函数ngx_http_handler_pt handler;ngx_uint_t next;
};

     但从上图中可以看到,该函数只把有回调函数的处理阶段给提取了出来,同时利用ngx_http_phase_handler_t结构体数组对这些回调函数进行重组,不仅加上了进入回调函数的条件判断checker函数,而且通过next字段的使用,把原本的二维数组实现转化为可直接在一维函数数组内部跳动;一般来讲,二维数组的遍历需要两层循环,而遍历一维函数数组就只需一层循环。

     再来看对http请求进行分段处理的核心函数ngx_http_core_run_phase:

void
ngx_http_core_run_phases(ngx_http_request_t *r)
{ngx_int_t                   rc;ngx_http_phase_handler_t   *ph;ngx_http_core_main_conf_t  *cmcf;cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);ph = cmcf->phase_engine.handlers;while (ph[r->phase_handler].checker) {rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);if (rc == NGX_OK) {return;}}
}
ngx_http_core_run_phases函数中r->phase_handler标志当前处理的序号,对于一个客户端的最开始的请求的时刻, 该值当然就是0了,while循环判断如果存在checker函数(末尾数组元素的checker函数为null),那就调用该checker函数并有可能调用相应的回调函数,以NGX_HTTP_ACCESS_PHASE阶段的ngx_http_core_access_phase()函数为例:
ngx_int_t
ngx_http_core_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
{if (r != r->main) {r->phase_handler = ph->next;return NGX_AGAIN;}rc = ph->handler(r);if (rc == NGX_DECLINED) {r->phase_handler++;return NGX_AGAIN;}if (rc == NGX_AGAIN || rc == NGX_DONE) {return NGX_OK;} ngx_http_finalize_request(r, rc);return NGX_OK;
}

可以看到,一个功能模块的handler函数可以返回多种类型的值,并且这些值有其固有的含义:

序号

返回值

含义

1

NGX_OK

当前阶段已经被成功处理,必须进入到下一个阶段

2

NGX_DECLINED

当前回调不处理当前情况,进入到下一个回调处理

3

NGX_AGAIN

当前处理所需资源不足,需要等待所依赖事件发生

4

NGX_DONE

当前处理结束,仍需等待进一步事件发生后做处理

5

NGX_ERROR, NGX_HTTP_…

当前回调处理发生错误,需要进入到异常处理流程



Filter模块:
对于http请求处理handlers产生的响应内容,在输出客户端之前需要做过滤处理,这些过滤处理对于完整功能的增强实现和性能的提升是非常有必要的,比如过滤模块ngx_http_chunked_filter_moudle,那么就无法完整支持http中chunk的功能。如果没有ngx_http_not_modified_filter_module过滤模块,那么就无法让客户端使用本地缓存来提高性能;诸如这些都需要过滤模块的支持。由于响应数据包括响应头和响应体,所以以此对应,任一filter模块必须提供处理响应头的header过滤函数(比如ngx_http_not_modified_filter_module模块提供的ngx_http_not_modified_header_filter()函数)或处理响应体的body过滤功能函数(比如ngx_http_copy_filter_module模块提供的ngx_http_copy_filter()函数)或两者皆有(比如ngx_http_chunked_filter_module模块提供的ngx_http_chunked_header_filter()函数和ngx_http_chunked_body_filter()函数)。

  所有的header过滤功能函数和body过滤功能函数会分别组成各自的两条过滤链,如下图所示(使用附录A所列模块):

     这2条过滤链怎么形成的呢?在源文件ngx_http.c中,可以看到有2个函数指针变量:

   ngx_int_t  (*ngx_http_top_header_filter) (ngx_http_request_t *r);

   ngx_int_t  (*ngx_http_top_body_filter) (ngx_http_request_t *r, ngx_chain_t *ch);

    这是整个nginx范围内可见的全局变量;然后在每一个filter模块内,我们还会看到类似于这样的定义(如果当前模块只有header过滤功能函数或只有body过滤功能函数,那么如下定义也就只有相应的那个变量):

         static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

         static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

        注意到static修饰符,也就是说这两个变量是属于模块范围内可见的局部变量。有了这些函数指针变量,再在各个filter模块的postconfiguration回调函数(该函数会在其对应配置解析完后被调用做一些设置工作,前面已经描述过)内,全局变量与局部变量的巧妙赋值使得最终行成了两条过滤链。以header过滤链为例,通过附录A的模块列表ngx_modules变量,可以看到ngx_http_header_filter_module是具有header过滤功能函数的序号最小的过滤模块,其postconfiguration回调函数如下:

618:    ngx_http_header_filter_init(ngx_conf_t *cf)
619:    {
620:        ngx_http_top_header_filter = ngx_http_header_filter;
621:    
622:        return NGX_OK;
623:    }
232:    static ngx_int_t
233:    ngx_http_chunked_filter_init(ngx_conf_t *cf)
234:    {
235:        ngx_http_next_header_filter  = ngx_http_top_header_filter;
236:        ngx_http_top_header_filter   = ngx_http_chunked_header_filter;}

        其它过滤模块的类此加入,逐步形成最终的完整header过滤链;当然,body过滤链的形成过程也与此类似。两条过滤链形成后,其对应的调用入口分别在函数ngx_http_send_header()和函数ngx_http_output_filter()内:

1889:    ngx_int_t
1890:    ngx_http_send_header(ngx_http_request_t *r)
1891:    {
1892:    …
1897:        return ngx_http_top_header_filter(r);
1898:    }
1899:    
1901:    ngx_int_t
1902:    ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
1903:    {
1904:    …
1912:        rc = ngx_http_top_body_filter(r, in);
1913:    …
1919:        return rc;
1920:    }

      这两个函数非常简单,主要是通过过滤链的链头函数指针全局变量进入到两条过滤链内,进而依次执行链上的各个函数。比如这里ngx_http_top_header_filter指向的是ngx_http_not_modified_header_filter()函数,因此进入到该函数内执行,而在该函数的执行过程中又会根据情况,继续通过当前模块内的函数指针局部变量ngx_http_next_header_filter间接的调用到header过滤链的下一个过滤函数,这对保证过滤链的前后承接是非常必要的,除非我们遇到无法继续处理的错误,此时只有返回NGX_ERROR这样的值:   

52:    static ngx_int_t
53:    ngx_http_not_modified_header_filter(ngx_http_request_t *r)
54:    {
55:    …
70:        return ngx_http_next_header_filter(r);
71:    }

        根据HTTP协议具备的响应头影响或决定响应体内容的特点,所以一般是先对响应头进行过滤,根据头过滤处理返回值再对响应体进行过滤处理,如果在响应头过滤处理中出错或某些特定情况下,响应体过滤处理可以不用再进行。

         

转载于:https://www.cnblogs.com/fll369/archive/2012/11/28/2793417.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/377055.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《LeetcodeHot100非困难题补录》

最近比较闲,也比较焦虑,刷刷题吧 目录11. 盛最多水的容器22. 括号生成31. 下一个排列48. 旋转图像49. 字母异位词分组56. 合并区间75. 颜色分类79. 单词搜索114. 二叉树展开为链表141. 环形链表148. 排序链表152. 乘积最大子数组169. 多数元素207. 课程表…

《MySQL8.0.22:Lock(锁)知识总结以及源码分析》

目录1、关于锁的一些零碎知识,需要熟知事务加锁方式:Innodb事务隔离MVCC多版本并发控制常用语句 与 锁的关系意向锁行级锁2、锁的内存结构以及一些解释3、InnoDB的锁代码实现锁系统结构lock_sys_tlock_t 、lock_rec_t 、lock_table_tbitmap锁的基本模式的…

《Linux杂记:一》

目录CPU负载和CPU利用率CPU负载很高,利用率却很低的情况负载很低,利用率却很高常用linux命令常用的文件、目录命令常用的权限命令常用的压缩命令CPU负载和CPU利用率 可以通过 uptime , w 或者 top 命令看到CPU的平均负载。 Load Average :负载的3个数字,比如上图的0.57、0.4…

Iptables入门教程

转自:http://drops.wooyun.org/tips/1424 linux的包过滤功能,即linux防火墙,它由netfilter 和 iptables 两个组件组成。 netfilter 组件也称为内核空间,是内核的一部分,由一些信息包过滤表组成,这些表包含内…

No identities are available for signing 的解决办法

今天重新上传做好的app提交到app store,结果就出现标题上的错误。“No identities are available for signing”。 以后碰到这样的问题按照下面几个步骤来做: 进入Distribution -----下载发布证书 -----双击安装-----重启Xcode就能上传了 其他细节 如果再…

匿名方法和Lambda表达式

出于MVVM学习的需要,复习下匿名方法和Lambda表达式,因为之前用的也比较少,所以用的也不是很熟练,Baidu下相关的知识,写了这个Demo,目标是用简单的方法展示这个怎么用。 这里偏重的和LINQ中的Lambda表达式 …

烂橘子

Problem Statement: 问题陈述: Given a matrix of dimension r*c where each cell in the matrix can have values 0, 1 or 2 which has the following meaning: 给定尺寸r * C的矩阵,其中矩阵中的每个单元可以具有其具有以下含义的值0,1或2…

别人的算法学习之路

http://www.cnblogs.com/figure9/p/3708351.html 我的算法学习之路 关于 严格来说,本文题目应该是我的数据结构和算法学习之路,但这个写法实在太绕口——况且CS中的算法往往暗指数据结构和算法(例如算法导论指的实际上是数据结构和算法导论&a…

git config命令使用第二篇——section操作,多个key值操作,使用正则

接上一篇,git config命令使用第一篇——介绍,基本操作,增删改查:http://blog.csdn.net/hutaoer06051/article/details/8275069 1. 删除一个section 命令参数 --remove-section 格式:git config [--local|--global|--system] --rem…

MySQL面试准备——64页pdf

本笔记为以前整理的零碎的关于Mysql的知识点,有深入源码的也有浅层的八股。已经被我整理成了一个pdf。 实习岗位正好也是和数据库内核有关的,之后应该还会更新。做个整理,方便秋招的时候快速回顾吧。 链接:链接 提取码&#xff1a…

python点图_Python | 点图

python点图The dot plot is a type of data representation in which each data-point in the figure is represented as a dot. Dot plot underlies discrete functions unlike a continuous function in a line plot. Each value could be correlated but cannot be connecte…

SAP-MM:发票、贷方凭证、事后借记、后续贷记

发票和事后借记 相同点:增加对供应商的应付款 不同点:针对同一订单收货,发票要先于事后借记(事后借记是对供应商后期发票金额的补充);发票和金额、订单数量有关系,而事后借记只是订单金额调整的…

nios pio interrupt 的使能

关于nios 中的中断,因为要16c550中需要nios的中断环境去测试,所以就用到了中断。 硬件:在nios中添加硬件PIO,但是要使能中断功能。如下图所示: 系统列化,PIO的连接就不说了。但是要注意两地方:edge type&am…

《单线程的build hash table、write rows to chunks、hash join的步骤以及流程图》

Build Hash Table流程 1、初始化row buffer2、从build input table中读一行3、若读完build input table所有row,返回状态READING_ROW_FROM_PROBE_item4、否则,向hash map中写入一条row5、如果hash map 写入成功,返回2,继续执行6、…

适合高速驱动电路的推挽电路

http://www.dzsc.com/data/html/2008-9-10/69023.html 图1是使用NPN/PNP型晶体管的互补推挽电路,适于驱动功率MOSFET的门极。此电路虽然具有门极电流的驱动能力,但射极输出波形不能比输人信号快。 图2是此电路的开关波形。它表示出tf、tr都快&#xff0c…

socket编程常见函数使用方法

socket知识 有了IP地址,socket可知道是与哪一台主机的哪一个进程通信 有了端口号,就知道是这个进程的哪一个套接字进行传输 应用进程使用描述符与它的套接字进行通信,也就是说一个进程创建一个套接字时就会返回一个套接字描述符 socket的…

html 表格套表格_HTML表格

html 表格套表格A table is a set of rows and columns, which could be created on a webpage in HTML, by <table> tag. The tabular representation of complex data makes it readable. 表格是一组行和列&#xff0c;可以通过<table>标签在HTML网页上创建。 复…

HDU计算机网络系统2021复习提纲

目录计算机网络系统的主要功能TCP/IP模型与OSI模型的层次结构及各层功能。&#xff08;掌握&#xff09;TCP/IP参考模型各层次所对应的主要设备局域网的体系结构与IEEE.802标准数据链路层的编址方式和主要设备原理数据链路层CSMA/CD的技术原理交换机VLAN原理与划分方法数据链路…

掷骰子

Description: 描述&#xff1a; In this article, we are going to see a dynamic programing problem which can be featured in any interview rounds. 在本文中&#xff0c;我们将看到一个动态的编程问题&#xff0c;该问题可以在任何采访回合中体现。 Problem statement:…

《YOLO算法笔记》(草稿)

检测算法回顾 5、6年前的检测算法大体如下&#xff1a; 手动涉及特征时应该考虑的因素&#xff1a; 1、尺度不变性 2、光照不变性 3、旋转不变性 这一步骤称为特征工程&#xff0c;最重要的一个算法称为sift&#xff0c;(回顾SIFT讲解)体现了上述所有的观点。 在分类的过程中…