January 20th Wednesday 2010

本文详细介绍了Nginx事件模块的工作原理,包括ngx_events_block()函数的作用、event模块的初始化过程、ngx_event_process_init()函数的具体实现,以及ngx_event_accept()函数如何处理连接请求。还探讨了不同操作系统下的I/O接口实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Nginx (十二)  事件

 

 

ngx_events_block()函数。这个函数在nginx系统起动后解析配置文件中的events{}块时调用的。是event模块的中第一个被调用的函数。

 

1.  NGX_EVENT_MODULE类型的模块进行点数,并给这些模板建立上下文的访问索引;

2.  event模块创建上下文数组空间;

3.  调用每个event模块中的create_conf()钩子函数,其返回值放在上下文数组中模块相对槽中;

4.  解析event{}块中的配置项参数;

cf->ctx = ctx;

cf->module_type = NGX_EVENT_MODULE;

cf->cmd_type = NGX_EVENT_CONF;

 

rv = ngx_conf_parse(cf, NULL);

5.  调用event模块中的init_conf()钩子函数。

 

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;

 

ngx_event_module_init()函数。在event模块初始化时调用,来做一些初始化操作。具体内容暂时没有搞清楚,先放一下。

ngx_event_process_init()函数。这个函数间接地在ngx_single_process_cycle()函数或ngx_worker_process_init()函数中调用;也就是在工作进程启动时调用。

1.  获取event模块中对应的存放配置信息的上下文内存;

2.  ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex时,

    ngx_use_accept_mutex = 1;

    ngx_accept_mutex_held = 0;

    ngx_accept_mutex_delay = ecf->accept_mutex_delay;

    否则,ngx_use_accept_mutex = 0;

3.  调用ngx_event_timer_init()函数,初始化event要用到的timer

    ngx_event_timer_init()函数中调用ngx_rbtree_init()这个宏来初始化ngx_event_timer_rbtree树。

ngx_rbtree_init(&ngx_event_timer_rbtree, &ngx_event_timer_sentinel,

                    ngx_rbtree_insert_timer_value);

 

ngx_rbtree_init()宏定义如下:

#define ngx_rbtree_init(tree, s, i)                                           /

    ngx_rbtree_sentinel_init(s);                                              /

    (tree)->root = s;                                                         /

    (tree)->sentinel = s;                                                     /

    (tree)->insert = i

 

ngx_event_timer_rbtree 是用来排序timer的红黑树的root

ngx_event_timer_sentinel 是哨兵节点;

ngx_rbtree_insert_timer_value()是红黑树节点上的insert钩子函数。这个函数的逻辑是对timer值在红黑树上进行排序。

a. 将待插入节点值与红黑树上的节点进行对比。

若树上节点值比待插入节点值小,沿着其左子节点向下,否则沿着其右子节点向下;直至找到与哨兵节点相同值的节点;

b.  找到插入处后,插入该节点,同时设定其对应的父节点;

c.  将其节点的左右子节点都设为哨兵节点;

d.  最后将这个插入的节点标为红色。

在支持线程的情况下,暂不做说明。

 

4. 遍历event模块,调用其中的actions.init()钩子函数。

   这里要说明的是在遍历时做了下面的检查。

   if (ngx_modules[m]->ctx_index != ecf->use) {

            continue;

   }

  

下面是event{}配置块中use参数的说明。

use

Syntax: use [ kqueue | rtsig | epoll | /dev/poll | select | poll | eventport ]

Default:

 

If you have more than one event-model specified at the ./configure script, then you can tell nginx which one do you want to use. By default nginx looks for the most suitable method for your OS at ./configure time.

 

You can see the available event-models and how you can activate it at the ./configure state here.

 

也就是说,这个检查是过滤掉不使用的event模块。例如在linux下,不用kqueue等。

 

5. ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)情况下,建立SIGALRM信号处理函数(ngx_timer_signal_handler()),并调用setitimer(ITIMER_REAL, &itv, NULL)来启动一个实时的计时器;

下面是ngx_timer_signal_handler() SIGALRM信号处理函数的定义:

void ngx_timer_signal_handler(int signo)

{

    ngx_event_timer_alarm = 1;

    ngx_time_update(0, 0);

}

 

1.       ngx_event_flags & NGX_USE_FD_EVENT情况下,先取得系统中进程可打开文件数,根据这个最大打开文件数,在cycle中创建一个files数组。这个files数组是ngx_connection_t类型。

2.       cycle中创建一个connections数组。connection_n表示connections数组中的元素数目。

3.       cycle中创建read_events数组,并将其中每个read event对象,按下面方式设置;

        rev[i].closed = 1;

        rev[i].instance = 1;

#if (NGX_THREADS)

        rev[i].lock = &c[i].lock;

        rev[i].own_lock = &c[i].lock;

#endif

9.在cycle中创建write_events数组,并将其中每个write event对象,按下面方式设置;

        wev[i].closed = 1;

#if (NGX_THREADS)

        wev[i].lock = &c[i].lock;

        wev[i].own_lock = &c[i].lock;

#endif

   注:每个ngx_connection_t对象都对应有一个read和一个write事件对象。所以cycle中的read_evnetswrite_events数组大小与connection_n一样。

10. cycle中的connection数组中每个ngx_connection_t对象的readwrite对象设为对应与read_evnetswrite_events数组中的成员;同时将这个ngx_connection_t对象用data指针串起来(参见图)。

11. free_connections指向刚创建的connections数组。

12. cycle中的listening数组中的ngx_listening_t对象创建对应的ngx_connection_t对象。

a) 在这个循环中,先调用ngx_get_connection()函数,从free_connections中取得一个空闲未用的ngx_connection_t对象,做一些相关初始化工作和设置工作,将connection对象的write事件对象的write标志设为1;同时ngx_cycle中的files存在时,ngx_cycle->files[s] = c;

s ngx_listening_t对象中的fd

b) 然后将取得的ngx_connection_t对象与该ngx_listening_t对象相关联;

c->listening = &ls[i];

   ls[i].connection = c;

c) 将该ngx_connection_t对象的read事件对象的accept标志设为1

   支持deferred_accept时,从对应的ngx_listening_t对象中获取deferred_accept设定值;

d) Win32系统下,将这个ngx_listening_t对象之前的对象中的ngx_connection_t对象中的read事件中删掉NGX_READ_EVENT类型事件,并将其fd设为-1

e) 一个关键设置。

   rev->handler = ngx_event_accept;

f)  ngx_use_accept_mutex有效时,进行下一个ngx_listening_t对象的处理。

g) 否则接着是下面工作:

        if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {

            if (ngx_add_conn(c) == NGX_ERROR) {

                return NGX_ERROR;

            }

 

        } else {

            if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {

                return NGX_ERROR;

            }

        }

 

注:ngx_event_module_init()ngx_event_process_init()函数设置在ngx_event_core_module模块中。如下:

 

ngx_module_t  ngx_event_core_module = {

    NGX_MODULE_V1,

    &ngx_event_core_module_ctx,             /* module context */

    ngx_event_core_commands,               /* module directives */

    NGX_EVENT_MODULE,                    /* module type */

    NULL,                                  /* init master */

    ngx_event_module_init,                   /* init module */

    ngx_event_process_init,                   /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

 

ngx_event_accept()函数。

1. 先也是从ngx_cycle->conf_ctx中取得ngx_event_core_module对象对应的ngx_event_cont_t对象;

2. 检查ngx_event_flags标志中若设定了NGX_USE_RTSIG_EVENTev实参事件对象中的available设为1;若未设定NGX_USE_KQUEUE_EVENT标志,available标志取ngx_event_cont_t对象中multi_accept的值;

注:ngx_event_t对象中available成员是个计数器。个人猜测是用来表示一个ngx_event_t对象对应的有效的ngx_connection_t对象有多少。目前似乎只有在FreeBSD系统中的KQUEUE架构中才会使available的值大于1

3. 取出ev对象关联的ngx_connection_t对象和ngx_listening_t对象,并将ev对象中ready设为0

4. 使用do-while循环,处理ev对象中所有的有效的accept连接。

   a)  调用accept()函数,接受一个新的连接,同时创建一个新的socket

   b)  ngx_accept_disabled = ngx_cycle->connection_n / 8

                              - ngx_cycle->free_connection_n;

   c)  调用ngx_get_connection()为新建的socket获取一个空闲的ngx_connection_t对象;同时新建的socket与获取的ngx_connection_t对象关联起来。

   d)  给这个ngx_connection_t对象创建新的pool,创建sockaddr,并将接受到的地址拷贝到sockaddr中。

   e)  ngx_inherited_nonblocking有效时,且ngx_event_flags中设有NGX_USE_AIO_EVENT标志的情况下,调用ngx_blocking()函数将新建的socket设为阻塞模式;

ngx_inherited_nonblocking无效时,若ngx_event_flags标志没有NGX_USE_AIO_EVENTNGX_USE_RTSIG_EVENT标志时,调用ngx_nonblocking()函数将新建的socket设为非阻塞模式;

f)  设置recv, send, recv_chain, send_chain钩子函数;

c->recv = ngx_recv;

  c->send = ngx_send;

   c->recv_chain = ngx_recv_chain;

   c->send_chain = ngx_send_chain;

ngx_recvngx_sendngx_recv_chainngx_send_chain都是宏。下面是它们的定义:

#define ngx_recv             ngx_io.recv

#define ngx_recv_chain       ngx_io.recv_chain

#define ngx_udp_recv         ngx_io.udp_recv

#define ngx_send             ngx_io.send

#define ngx_send_chain       ngx_io.send_chain

 

还记得在epoll_init()函数中有下面这个语句吗?

ngx_io = ngx_os_io;

也就是说ngx_io作为nginx纯一的io接口对象,而ngx_os_io则是针对不同操作系统定制的特定的io对象。它们的结构如下:

 

typedef struct {

    ngx_recv_pt        recv;

    ngx_recv_chain_pt  recv_chain;

    ngx_recv_pt        udp_recv;

    ngx_send_pt        send;

    ngx_send_chain_pt  send_chain;

    ngx_uint_t         flags;

} ngx_os_io_t;

 

继续做ngx_os_io相关搜索会发现ngx_os_io = ngx_linux_io; ngx_os_io = ngx_solaris_io; ngx_os_io = ngx_freebsd_io; ngx_os_io = ngx_darwin_io; …

 

我们查找ngx_linux_io这个对象,最后在ngx_linux_init.c文件中有下面的定义:

static ngx_os_io_t ngx_linux_io = {

    ngx_unix_recv,

    ngx_readv_chain,

    ngx_udp_unix_recv,

    ngx_unix_send,

#if (NGX_HAVE_SENDFILE)

    ngx_linux_sendfile_chain,

    NGX_IO_SENDFILE

#else

    ngx_writev_chain,

    0

#endif

};

 

最后在ngx_recv.c文件中找到ngx_unix_recv()这个函数的实现。如下,其逻辑不难看懂。

ssize_t  ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)

{

    ssize_t       n;

    ngx_err_t     err;

    ngx_event_t  *rev;

 

    rev = c->read;

 

    do {

        n = recv(c->fd, buf, size, 0);

 

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "recv: fd:%d %d of %d", c->fd, n, size);

 

        if (n == 0) {

            rev->ready = 0;

            rev->eof = 1;

            return n;

 

        } else if (n > 0) {

 

            if ((size_t) n < size

                && !(ngx_event_flags & NGX_USE_GREEDY_EVENT))

            {

                rev->ready = 0;

            }

 

            return n;

        }

 

        err = ngx_socket_errno;

 

        if (err == NGX_EAGAIN || err == NGX_EINTR) {

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,

                           "recv() not ready");

            n = NGX_AGAIN;

 

        } else {

            n = ngx_connection_error(c, err, "recv() failed");

            break;

        }

 

    } while (err == NGX_EINTR);

 

    rev->ready = 0;

 

    if (n == NGX_ERROR){

        rev->error = 1;

    }

 

    return n;

}

 

注:在KQUEUE支持的情况下, 有另一个ngx_unix_recv()函数的实现。

 

ngx_unix_send()函数的实现如下。它只有一个版本。

ssize_t

ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size)

{

    ssize_t       n;

    ngx_err_t     err;

    ngx_event_t  *wev;

 

    wev = c->write;

 

#if (NGX_HAVE_KQUEUE)

 

    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {

        (void) ngx_connection_error(c, wev->kq_errno,

                               "kevent() reported about an closed connection");

        wev->error = 1;

        return NGX_ERROR;

    }

 

#endif

 

    for ( ;; ) {

        n = send(c->fd, buf, size, 0);

 

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "send: fd:%d %d of %d", c->fd, n, size);

 

        if (n > 0) {

            if (n < (ssize_t) size) {

                wev->ready = 0;

            }

 

            c->sent += n;

 

            return n;

        }

 

        err = ngx_socket_errno;

 

        if (n == 0) {

            ngx_log_error(NGX_LOG_ALERT, c->log, err, "send() returned zero");

            wev->ready = 0;

            return n;

        }

 

        if (err == NGX_EAGAIN || err == NGX_EINTR) {

            wev->ready = 0;

 

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,

                           "send() not ready");

 

            if (err == NGX_EAGAIN) {

                return NGX_AGAIN;

            }

 

        } else {

            wev->error = 1;

            (void) ngx_connection_error(c, err, "send() failed");

            return NGX_ERROR;

        }

    }

g) 接下来是一些设置;

   c->socklen = socklen;

   c->listening = ls;

   c->local_sockaddr = ls->sockaddr;

   c->local_socklen = ls->socklen;

 

   c->unexpected_eof = 1;

h) c中的write事件的ready设为1

i)  ngx_event_flags标志中没有NGX_USE_AIO_EVENTNGX_USE_RTSIG_EVENT时,将c中的read事件的ready也设为1

j)  ev对象中deferred_accept有效时,将c中的read事件的ready也设为1;在KQUEUE环境下,rev->available = 1;

k) 支持线程时,按下面在read, write事件对象中设定锁。

   rev->lock = &c->lock;

   wev->lock = &c->lock;

   rev->own_lock = &c->lock;

   wev->own_lock = &c->lock;

l)  ev对象中的ngx_listening_t对象的addr_ntop标志有效时,为ev对象中ngx_connection_t对象中的addr_text分配空间,将接受地址转为文本格式。

m)  ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0时,调用ngx_add_conn(c)函数,为这个ngx_connection_t对象激活read, write事件对象。

n)  调用ngx_listening_t 对象中的handler()钩子函数来处理ev对象中的ngx_connection_t对象。

    注:ngx_listening_t对象中的handler()钩子函数在不同模块中有不同制定。如在http模块中ls->handler = ngx_http_init_connection;, 在mail模块中是ls->handler = ngx_mail_init_connection;。也就是说真正httpmail协议的逻辑处理从这儿开始。

如果在nginx中要开发另一种协议支持的模块时,在解析完自己的配置块时,在监听的ngx_listening_t对象中设置handler()钩子函数。(详情可以参考httpmail模块。)

o)  最后ngx_event_flags标志中NGX_USE_KQUEUE_EVENT有效时,将ev对象中的available计数器减一。


基于数据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用python语言,django框架,mysql数据库开发 编程环境:pycharm,mysql8.0 系统分为前台+后台模式开发 网站前台: 用户注册, 登录 搜索音乐,音乐欣赏(可以在线进行播放) 用户登陆时选择相关感兴趣的音乐风格 音乐收藏 音乐推荐算法:(重点) 本课题需要大量用户行为(如播放记录、收藏列表)、音乐特征(如音频特征、歌曲元数据)等数据 (1)根据用户之间相似性或关联性,给一个用户推荐与其相似或有关联的其他用户所感兴趣的音乐; (2)根据音乐之间的相似性或关联性,给一个用户推荐与其感兴趣的音乐相似或有关联的其他音乐。 基于用户的推荐和基于物品的推荐 其中基于用户的推荐是基于用户的相似度找出相似相似用户,然后向目标用户推荐其相似用户喜欢的东西(和你类似的人也喜欢**东西); 而基于物品的推荐是基于物品的相似度找出相似的物品做推荐(喜欢该音乐的人还喜欢了**音乐); 管理员 管理员信息管理 注册用户管理,审核 音乐爬虫(爬虫方式爬取网站音乐数据) 音乐信息管理(上传歌曲MP3,以便前台播放) 音乐收藏管理 用户 用户资料修改 我的音乐收藏 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:pycharm
MPU6050是一款广泛应用在无人机、机器人和运动设备中的六轴姿态传感器,它集成了三轴陀螺仪和三轴加速度计。这款传感器能够实时监测并提供设备的角速度和线性加速度数据,对于理解物体的动态运动状态至关重要。在Arduino平台上,通过特定的库文件可以方便地与MPU6050进行通信,获取并解析传感器数据。 `MPU6050.cpp`和`MPU6050.h`是Arduino库的关键组成部分。`MPU6050.h`是头文件,包含了定义传感器接口和函数声明。它定义了类`MPU6050`,该类包含了初始化传感器、读取数据等方法。例如,`begin()`函数用于设置传感器的工作模式和I2C地址,`getAcceleration()`和`getGyroscope()`则分别用于获取加速度和角速度数据。 在Arduino项目中,首先需要包含`MPU6050.h`头文件,然后创建`MPU6050`对象,并调用`begin()`函数初始化传感器。之后,可以通过循环调用`getAcceleration()`和`getGyroscope()`来不断更新传感器读数。为了处理这些原始数据,通常还需要进行校准和滤波,以消除噪声和漂移。 I2C通信协议是MPU6050与Arduino交互的基础,它是一种低引脚数的串行通信协议,允许多个设备共享一对数据线。Arduino板上的Wire库提供了I2C通信的底层支持,使得用户无需深入了解通信细节,就能方便地与MPU6050交互。 MPU6050传感器的数据包括加速度(X、Y、Z轴)和角速度(同样为X、Y、Z轴)。加速度数据可以用来计算物体的静态位置和动态运动,而角速度数据则能反映物体转动的速度。结合这两个数据,可以进一步计算出物体的姿态(如角度和角速度变化)。 在嵌入式开发领域,特别是使用STM32微控制器时,也可以找到类似的库来驱动MPU6050。STM32通常具有更强大的处理能力和更多的GPIO口,可以实现更复杂的控制算法。然而,基本的传感器操作流程和数据处理原理与Arduino平台相似。 在实际应用中,除了基本的传感器读取,还可能涉及到温度补偿、低功耗模式设置、DMP(数字运动处理器)功能的利用等高级特性。DMP可以帮助处理传感器数据,实现更高级的运动估计,减轻主控制器的计算负担。 MPU6050是一个强大的六轴传感器,广泛应用于各种需要实时运动追踪的项目中。通过 Arduino 或 STM32 的库文件,开发者可以轻松地与传感器交互,获取并处理数据,实现各种创新应用。博客和其他开源资源是学习和解决问题的重要途径,通过这些资源,开发者可以获得关于MPU6050的详细信息和实践指南
### USACO 2025 January Contest Information and Problems For individuals interested in participating or learning about the USA Computing Olympiad (USACO) contests, it's important to understand that specific details regarding future events like the USACO 2025 January contest are not yet available as of now. However, based on previous patterns observed in past competitions such as those mentioned in earlier years[^1], some general expectations can be outlined. #### General Structure of USACO Contests Contests typically consist of several problems categorized by difficulty levels—Bronze, Silver, Gold, and Platinum. Participants solve algorithmic challenges within a limited timeframe using programming languages including C++, Java, Python among others. The exact nature of these questions varies widely but often involves complex algorithms and data structures designed to test both coding skills and problem-solving abilities. #### Preparing for Future Competitions To prepare effectively for upcoming contests similar to what might appear in 2025: - **Practice Regularly**: Engage actively with practice problems from previous contests which cover various topics ranging from basic sorting techniques to advanced graph theory concepts. - **Understand Algorithms & Data Structures**: Deepen knowledge around key areas frequently tested during these exams; this includes dynamic programming, greedy strategies, depth-first search (DFS), breadth-first search (BFS), etc.[^3] - **Participate in Mock Tests**: Join mock testing sessions offered through official platforms or community-driven initiatives where participants attempt solving under exam conditions. Here’s an example code snippet demonstrating how one could approach implementing DFS—a common technique used across many competitive programming tasks: ```cpp void dfs(int node, vector<bool>& visited, const vector<vector<int>>& adjList){ cout << "Visiting Node: " << node << endl; visited[node] = true; // Traverse all adjacent nodes recursively if they haven't been visited already for(auto neighbor : adjList[node]){ if(!visited[neighbor]) dfs(neighbor, visited, adjList); } } ``` --related questions-- 1. What resources exist specifically tailored towards preparing for USACO? 2. How does understanding different types of graphs benefit performance in computational olympiads? 3. Can you provide examples of other international computing olympiads comparable to USACO? 4. In terms of time management, what tips would help maximize efficiency while attempting multiple challenging problems simultaneously?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值