欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

Nginx事件處理模塊怎么理解

本篇內(nèi)容主要講解“Nginx事件處理模塊怎么理解”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Nginx事件處理模塊怎么理解”吧!

站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到?jīng)芸h網(wǎng)站設(shè)計(jì)與涇縣網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都網(wǎng)站建設(shè)、成都網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、空間域名、虛擬空間、企業(yè)郵箱。業(yè)務(wù)覆蓋涇縣地區(qū)。

一、事件模塊主流程講解

事件模塊主要處理兩類事件:① 定時(shí)任務(wù) ② I/O事件。其中定時(shí)任務(wù)是指nginx通過(guò)ngx_add_timer添加的事件,原型函數(shù)如下:

ngx_event_timer.h

static ngx_inline void
ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer)
{
	// 計(jì)算過(guò)期時(shí)間點(diǎn)
    // 過(guò)期時(shí)間 = 現(xiàn)在時(shí)間 + 過(guò)期時(shí)間
    key = ngx_current_msec + timer;
	
	// 設(shè)置超時(shí)時(shí)間到
	ev->timer.key = key;
	
	// 添加的紅黑樹(shù)
	ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer);
}


// 并設(shè)置了全局變量方便在任何地方調(diào)用
#define ngx_add_timer        ngx_event_add_timer

在此列舉一個(gè)本文涉及的超時(shí)管理:

① 工作進(jìn)程在爭(zhēng)搶互斥鎖時(shí),因?yàn)橹挥幸粋€(gè)進(jìn)程能夠獲取鎖,如果其他進(jìn)程無(wú)法獲取鎖,不可能一直等待,所以設(shè)置accept_mutex_delay,超過(guò)了設(shè)定的事件就會(huì)發(fā)生超時(shí)處理,也就放棄爭(zhēng)搶鎖二選擇去釋放資源。

ngx_event_accept.c

void
ngx_event_accept(ngx_event_t *ev)
{
	if (ngx_use_accept_mutex) {
	// xxx
	}else{
	ngx_add_timer(ev, ecf->accept_mutex_delay);
	}
}

其次就是I/O事件了,很容易理解就是處理讀寫事件,那么I/O事件以epoll為例,先來(lái)看下nginx處理事件的主流程:

ngx_process_cycle.c

static void
ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
	for ( ;; ) {
		// 不斷循環(huán)處理事件
		ngx_process_events_and_timers(cycle);
	}
}

事件處理的入口在nginx進(jìn)程模型的代碼里面,工作進(jìn)程會(huì)一直循環(huán)取處理事件,其中的阻塞點(diǎn)就是epoll_wait,處理完一輪事件后又循環(huán)這個(gè)過(guò)程,直到master進(jìn)程發(fā)來(lái)關(guān)閉的信號(hào)。接下來(lái)就是真正的開(kāi)始處理事件的地方:

// 開(kāi)始處理事件
void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
    ngx_uint_t  flags;
    ngx_msec_t  timer, delta;

    // ngx_timer_resolution 用于決定使用何種超時(shí)策略
    // 如果ngx_timer_resolution非零0,則設(shè)置timer未無(wú)限大
    // 另外ngx_timer_resolution還有作用就是控制gettimeofday調(diào)用的頻率,不過(guò)在x86_64系統(tǒng)中影響已經(jīng)可忽略了
    // 此時(shí)采用定時(shí)方案,在規(guī)定的時(shí)間,默認(rèn)500ms,對(duì)紅黑樹(shù)中的元素進(jìn)行一次掃描
    // 并處理超時(shí)的節(jié)點(diǎn)
    if (ngx_timer_resolution) {
        // 先設(shè)置時(shí)間為無(wú)限待,后面會(huì)設(shè)置未500
        timer = NGX_TIMER_INFINITE;
        flags = 0;

    } else {
        // 如果ngx_timer_resolution為0
        // 采用超時(shí)的方案,首先計(jì)算出最快超時(shí)的時(shí)間,然后等待這個(gè)時(shí)間段取處理超時(shí)的事件,
        // 處理完超時(shí)任務(wù),再次計(jì)算下一次的超時(shí)時(shí)間,不斷地循環(huán)處理。

        // 查詢紅黑樹(shù),返回超時(shí)時(shí)間最小的節(jié)點(diǎn),即最左邊的元素
        /**
         * while (node->left != sentinel) {
              node = node->left;
           }
        */
        // nginx通過(guò)紅黑樹(shù)來(lái)維護(hù)所有的time節(jié)點(diǎn)
        // 將超時(shí)檢測(cè)時(shí)間設(shè)置未最快發(fā)生超時(shí)的事件對(duì)象的超時(shí)時(shí)刻和當(dāng)前時(shí)刻的差
        // timer = (ngx_msec_int_t) (node->key - ngx_current_msec);
        timer = ngx_event_find_timer();
        flags = NGX_UPDATE_TIME;

#if (NGX_WIN32)

        /* handle signals from master in case of network inactivity */
        // 如果timer是無(wú)限或者大于500,則設(shè)置未500
        // 即如果選擇定時(shí)方案,會(huì)設(shè)置定時(shí)時(shí)間為500ms
        if (timer == NGX_TIMER_INFINITE || timer > 500) {
            timer = 500;
        }

#endif
    }
    // 處理驚群現(xiàn)象(nginx是一個(gè)master,多個(gè)work競(jìng)爭(zhēng)請(qǐng)求)
    // 驚群現(xiàn)象是指多個(gè)線程/進(jìn)程同時(shí)監(jiān)聽(tīng)一個(gè)socket事件(nginx是多進(jìn)程程序,共享80端口),
    // 當(dāng)事件發(fā)生時(shí),會(huì)喚醒所有等待的線程/進(jìn)程,爭(zhēng)搶事件
    // 但是最后只有一個(gè)線程/進(jìn)程可以讀取事件成功
    // 其他進(jìn)程/線程爭(zhēng)搶失敗后重新等待或者其他操作,這種浪費(fèi)資源的現(xiàn)象叫驚群

    // 設(shè)置通過(guò)Accept互斥鎖來(lái)解決驚群現(xiàn)象
    // 當(dāng)nginx配置文件中worker的數(shù)量大于1時(shí)
    // 且配置文件打開(kāi)了accept_mutex時(shí),ngx_use_accept_mutex會(huì)設(shè)為1
    /*
    * nginx配置
    events {
        accept_mutex on;   #設(shè)置網(wǎng)路連接序列化,防止驚群現(xiàn)象發(fā)生,默認(rèn)為on
        multi_accept on;  #設(shè)置一個(gè)進(jìn)程是否同時(shí)接受多個(gè)網(wǎng)絡(luò)連接,默認(rèn)為off
        #use epoll;      #事件驅(qū)動(dòng)模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport
        worker_connections  1024;    #最大連接數(shù),默認(rèn)為512
    }
    */
    if (ngx_use_accept_mutex) {
        // ngx_accept_disabled = ngx_cycle->connection_n / 8 - ngx_cycle->free_connection_n;
        // 其中connection_n表示當(dāng)前工作進(jìn)程最大可承受連接數(shù),free_connection_n表現(xiàn)當(dāng)前空閑連接數(shù)
        // 假設(shè)當(dāng)前連接數(shù)為x,那么ngx_cycle->connection_n / 8 - connection_n + x => x - 7/8 * connection_n
        // 即當(dāng)連接數(shù)超過(guò)最大數(shù)的7/8時(shí),ngx_accept_disabled的值將大于0時(shí),當(dāng)前work處理的連接已經(jīng)達(dá)到飽和
        // 此時(shí)work不會(huì)競(jìng)爭(zhēng)新的連接,nginx會(huì)任務(wù)當(dāng)前函數(shù)已經(jīng)經(jīng)歷了一輪事件處理
        // 即相應(yīng)的負(fù)載減少了一點(diǎn),并對(duì)ngx_accept_disabled自減
        if (ngx_accept_disabled > 0) {
            ngx_accept_disabled--;

        } else {
            // 如果活動(dòng)連接數(shù)還沒(méi)有達(dá)到飽和,則獲取accept鎖
            // 多個(gè)work,只有一個(gè)可以得到鎖,
            // 且獲取鎖的過(guò)程不是阻塞,獲取成功后,ngx_accept_mutex_held會(huì)設(shè)為1
            if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
                return;
            }
            // 如果ngx_accept_mutex_held = 1表示成功獲取到鎖
            if (ngx_accept_mutex_held) {
                // 搶到了accept鎖的進(jìn)程會(huì)被加上了NGX_POST_EVENTS標(biāo)志
                // 并加入到post_events隊(duì)列里
                // 設(shè)置標(biāo)志,延遲處理事件
                flags |= NGX_POST_EVENTS;

            } else {
                if (timer == NGX_TIMER_INFINITE
                    || timer > ngx_accept_mutex_delay)
                {
                    timer = ngx_accept_mutex_delay;
                }
            }
        }
    }

    delta = ngx_current_msec;
    // 獲得互斥鎖的進(jìn)程開(kāi)始處理請(qǐng)求
    // 主要調(diào)用epoll_wait等待事件
    // 并根據(jù)事件類型加入到隊(duì)列ngx_posted_accept_events或者ngx_posted_events
    (void) ngx_process_events(cycle, timer, flags);

    // delta就是調(diào)用ngx_process_events消耗的時(shí)間
    // ngx_current_msec的值是通過(guò)ngx_time_update函數(shù)得到的
    // 如果不執(zhí)行ngx_time_update,則delta依然為0
    delta = ngx_current_msec - delta;

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                   "timer delta: %M", delta);

    // 處理新建連接事件
    ngx_event_process_posted(cycle, &ngx_posted_accept_events);

    // accept事件一旦處理完成,當(dāng)前進(jìn)程就會(huì)釋放互斥鎖
    if (ngx_accept_mutex_held) {
        // 釋放鎖
        ngx_shmtx_unlock(&ngx_accept_mutex);
    }
    
    if (delta) {
        // 不斷的從紅黑樹(shù)的節(jié)點(diǎn)中取出時(shí)間值最小的,判斷是否超時(shí)
        // 如果超時(shí)就執(zhí)行他們的事件函數(shù),直到最小的時(shí)間不超時(shí)
        // 處理超時(shí)事件
        ngx_event_expire_timers();
    }
    // 釋放鎖后開(kāi)始處理ngx_posted_events隊(duì)列中的普通讀寫事件
    ngx_event_process_posted(cycle, &ngx_posted_events);
}

上一段代碼較長(zhǎng),這里解讀下多做了什么操作:

1.  首先是根據(jù)ngx_timer_resolution的值判斷當(dāng)前定時(shí)策略,這個(gè)值是在nginx.conf文件可選配置:

timer_resolution 10ms

 如果timer_resolution 非0,則選擇定時(shí)檢查方案,即設(shè)置定時(shí)時(shí)間,500ms,隔這個(gè)時(shí)間去紅黑樹(shù)中檢查時(shí)候有事件超時(shí),如果有則處理超時(shí)事件。如果ngx_timer_resolution為0,則采用超時(shí)檢測(cè)方案,首先先計(jì)算最快超時(shí)的時(shí)間 timer = ngx_event_find_timer(),然后到了timer這個(gè)時(shí)間去處理超時(shí)事件,接著在計(jì)算一次timer,依次循環(huán)處理超時(shí)事件。

2.  根據(jù)ngx_use_accept_mutex判斷是否打開(kāi)了互斥鎖配置,如果打開(kāi)了互斥鎖配置則開(kāi)始爭(zhēng)搶鎖,否則的話直接處理事件。首先先了解下?tīng)?zhēng)搶鎖的過(guò)程,其核心就是函數(shù)ngx_trylock_accept_mutex:

// 各個(gè)工作線程會(huì)嘗試去獲取鎖
ngx_int_t
ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
{
    // 非阻塞的方式獲取鎖,返回1表示獲取成功,0表示獲取失敗
    if (ngx_shmtx_trylock(&ngx_accept_mutex)) {

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                       "accept mutex locked");

        if (ngx_accept_mutex_held && ngx_accept_events == 0) {
            return NGX_OK;
        }
        // 將cycle->listening中的端口信息添加到epoll事件中
        // 即把監(jiān)聽(tīng)socket加入到epoll中進(jìn)行監(jiān)聽(tīng)
        if (ngx_enable_accept_events(cycle) == NGX_ERROR) {
            ngx_shmtx_unlock(&ngx_accept_mutex);
            return NGX_ERROR;
        }

        // 監(jiān)聽(tīng)完成后會(huì)獲取到
        ngx_accept_events = 0;
        ngx_accept_mutex_held = 1;

        return NGX_OK;
    }

    // 獲取鎖失敗的情況
    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                   "accept mutex lock failed: %ui", ngx_accept_mutex_held);
    // 獲取鎖失敗:本來(lái)就擁有鎖的情況
    if (ngx_accept_mutex_held) {
        // 本來(lái)?yè)碛墟i就直接將監(jiān)聽(tīng)套接口從自身的事件監(jiān)聽(tīng)機(jī)制刪除
        if (ngx_disable_accept_events(cycle, 0) == NGX_ERROR) {
            return NGX_ERROR;
        }
        // 擁有鎖標(biāo)志置位0
        ngx_accept_mutex_held = 0;
    }

    return NGX_OK;
}

代碼較累贅,還是畫個(gè)圖說(shuō)明下流程:

       Nginx事件處理模塊怎么理解

拿到鎖后就開(kāi)始處理事件了。開(kāi)始處理之前先 flags |= NGX_POST_EVENTS延遲事件實(shí)際處理的事件,那么是如何延遲的呢?實(shí)際是在函數(shù)ngx_epoll_process_events中:

static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
	if (flags & NGX_POST_EVENTS) {
		queue = rev->accept ? &ngx_posted_accept_events
							: &ngx_posted_events;

		ngx_post_event(rev, queue);

	} else {
		rev->handler(rev);
	}
}

我們已經(jīng)直到影響flags的值是變量ngx_timer_resolution,其中有如下這一段代碼:

void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
    if (ngx_timer_resolution) {
        flags = 0;
    } else {
        flags = NGX_UPDATE_TIME;
	}	
}

flags被設(shè)置為0或者1,所以執(zhí)行 flags |= NGX_POST_EVENTS會(huì)導(dǎo)致flags & NGX_POST_EVENTS為非零,所以來(lái)自epoll_wait的事件會(huì)被緩存在隊(duì)列里面。

3.  接下來(lái)的操作就是要處理事件了,按照2中爭(zhēng)搶鎖的過(guò)程,搶到鎖的進(jìn)程會(huì)用于事件的監(jiān)聽(tīng)套接口,調(diào)用ngx_process_events方法處理事件,實(shí)際上是調(diào)用了ngx_epoll_module.c中的ngx_epoll_process_events方法:

ngx_epoll_module.c

static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
	events = epoll_wait(ep, event_list, (int) nevents, timer);
	// 定時(shí)方案:回調(diào)函數(shù)會(huì)調(diào)用ngx_timer_signal_handler方法,并設(shè)置ngx_event_timer_alarm = 1,如果沒(méi)有執(zhí)行回
    //         調(diào)函數(shù),則不會(huì)執(zhí)行ngx_time_update。
	// 超時(shí)方案:flags = 1,則flags & NGX_UPDATE_TIME一定為真,所以每次會(huì)調(diào)用該方法
	if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
		// 更新ngx_current_msec的值
		ngx_time_update();
	}
	// 緩存事件到隊(duì)列
	if (flags & NGX_POST_EVENTS) {
		queue = rev->accept ? &ngx_posted_accept_events
							: &ngx_posted_events;

		ngx_post_event(rev, queue);

	} else {
		rev->handler(rev);
	}
}

代碼中可以看出,其中accept事件(即監(jiān)聽(tīng)端口上的可讀事件)會(huì)被緩存到隊(duì)列ngx_posted_accept_events,普通事件會(huì)被緩存到隊(duì)列ngx_posted_events。

4. 緩存完事件,接下來(lái)就是處理新建連接事件(accept事件),因?yàn)楫?dāng)前進(jìn)程已經(jīng)監(jiān)聽(tīng)了某個(gè)客戶端的端口,該端口的請(qǐng)求中的可讀事件先要處理下,該讀的數(shù)據(jù)讀完,即處理隊(duì)列ngx_posted_accept_events中的新建連接事件,如果在處理新建連接期間還有新的請(qǐng)求連接事件,會(huì)阻塞,等待下次進(jìn)程獲取鎖后讀取。讀完可讀事件后就執(zhí)行解鎖操作ngx_shmtx_unlock。

5. 鎖釋放完之后就處理連接套接口之后的連接事件了,即保存在隊(duì)列ngx_posted_events中的事件。

void
ngx_event_process_posted(ngx_cycle_t *cycle, ngx_queue_t *posted)
{
    ngx_queue_t  *q;
    ngx_event_t  *ev;

    while (!ngx_queue_empty(posted)) {

        q = ngx_queue_head(posted);
        ev = ngx_queue_data(q, ngx_event_t, queue);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                      "posted event %p", ev);

        ngx_delete_posted_event(ev);

        ev->handler(ev);
    }
}

可以看出,就是不斷遍歷隊(duì)列,調(diào)用對(duì)應(yīng)的handler處理事件。

二、介紹事件處理模型的初始化

因?yàn)槲覀兩厦娴闹v解都是以epoll為例的,接下來(lái)就解釋下ngnix如何來(lái)選擇事件處理機(jī)制,首先先看下nginx.conf中的events模塊配置:

events
{
	// 選擇使用的事件處理機(jī)制,這里使用epoll
    use epoll;
	
	// 是否同時(shí)接受多個(gè)網(wǎng)絡(luò)請(qǐng)求
	multi_accept on; 
	
	// 是否激活互斥鎖
	accept_mutex on; 
	
	// 設(shè)置最大可用連接數(shù)
	worker_connections 65535;
	
	// 配置http連接的超時(shí)時(shí)間
    keepalive_timeout 60;
	
	// 客戶端request請(qǐng)求中header的緩存大小
    client_header_buffer_size 4k;
	
	// 靜態(tài)文件的緩存大小和緩存時(shí)間,比如html/css/image
    open_file_cache max=65535 inactive=60s;
	
	// 設(shè)置每次檢查緩存有效性的時(shí)間間隔
    open_file_cache_valid 80s;
	
	// 靜態(tài)文件有效緩存時(shí)間內(nèi)最少使用次數(shù)
    open_file_cache_min_uses 1;
    
	// 設(shè)置是否允許緩存錯(cuò)誤信息
    open_file_cache_errors on;
}

配置比較詳細(xì),其實(shí)相關(guān)的也就是 use epoll配置。

首先nginx定義了統(tǒng)一的事件處理接口,封裝了各種事件處理機(jī)制(epoll/poll/select等)的執(zhí)行函數(shù):

ngx_event.h

typedef struct {

    // 將一個(gè)事件(讀事件/寫事件)添加到事件驅(qū)動(dòng)機(jī)制
    ngx_int_t  (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    // 將一個(gè)事件(讀事件/寫事件)從事件驅(qū)動(dòng)中上刪除
    ngx_int_t  (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    // 啟用一個(gè)已經(jīng)添加的事件(代碼暫時(shí)未使用)
    ngx_int_t  (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    // 禁用一個(gè)已經(jīng)添加的事件(代碼暫時(shí)未使用)
    ngx_int_t  (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    // 將指定連接關(guān)聯(lián)的描述符加入到多路復(fù)用監(jiān)控里
    ngx_int_t  (*add_conn)(ngx_connection_t *c);

    // 從多路復(fù)用監(jiān)控里刪除指定連接關(guān)聯(lián)的描述符
    ngx_int_t  (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);

    // 僅在多線程環(huán)境下調(diào)用,目前未使用
    ngx_int_t  (*notify)(ngx_event_handler_pt handler);

    // 阻塞等待發(fā)生,并對(duì)發(fā)生的事件逐個(gè)處理
    ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,ngx_uint_t flags);

    // 初始化事件驅(qū)動(dòng)模塊
    ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);

    // 回收資源
    void       (*done)(ngx_cycle_t *cycle);

} ngx_event_actions_t;

對(duì)封裝的接口定義全局的變量,以及該接口函數(shù)也定義相應(yīng)的全局函數(shù)

ngx_event_actions_t   ngx_event_actions

ngx_process_events   ngx_event_actions.process_events
ngx_done_events      ngx_event_actions.done
ngx_add_event        ngx_event_actions.add
ngx_del_event        ngx_event_actions.del
ngx_add_conn         ngx_event_actions.add_conn
ngx_del_conn         ngx_event_actions.del_conn
ngx_notify           ngx_event_actions.notify

所以添加一個(gè)事件(讀/寫)只需要調(diào)用ngx_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)。那么重點(diǎn)就是如何對(duì)ngx_event_actions進(jìn)行賦值?搜索event/modules會(huì)發(fā)現(xiàn)在各種事件處理機(jī)制的模塊里面都存在對(duì)ngx_event_actions的賦值:

D:\mycode\nginx\src\event\modules\ngx_devpoll_module.c:
  186:     ngx_event_actions = ngx_devpoll_module_ctx.actions;

D:\mycode\nginx\src\event\modules\ngx_epoll_module.c:
  189: ngx_event_actions = ngx_epoll_module_ctx.actions

D:\mycode\nginx\src\event\modules\ngx_eventport_module.c:
  279:     ngx_event_actions = ngx_eventport_module_ctx.actions;

D:\mycode\nginx\src\event\modules\ngx_kqueue_module.c:
  224:     ngx_event_actions = ngx_kqueue_module_ctx.actions;

D:\mycode\nginx\src\event\modules\ngx_poll_module.c:
   96:     ngx_event_actions = ngx_poll_module_ctx.actions;

D:\mycode\nginx\src\event\modules\ngx_select_module.c:
  105:     ngx_event_actions = ngx_select_module_ctx.actions;

D:\mycode\nginx\src\event\modules\ngx_win32_select_module.c:
  106:     ngx_event_actions = ngx_select_module_ctx.actions;

而系統(tǒng)采用哪個(gè)賦值語(yǔ)句取決于用戶在events模塊的配置中的use epoll

具體初始化的過(guò)程:

static char *
ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf)
{
	ngx_conf_init_uint_value(ecf->use, module->ctx_index);
}


#define ngx_conf_init_size_value(conf, default)                             
// 用戶沒(méi)有指定則設(shè)置默認(rèn)的值
if (conf == NGX_CONF_UNSET_SIZE) {                                       
    conf = default;                                                      
}

以上配置項(xiàng)通過(guò)函數(shù)ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module)獲取到,即ecf->use的值是epoll值的序號(hào),而序號(hào)的設(shè)置在ngx_module.c中:

ngx_int_t
ngx_preinit_modules(void)
{
    ngx_uint_t  i;
    for (i = 0; ngx_modules[i]; i++) {
        ngx_modules[i]->index = i;
        ngx_modules[i]->name = ngx_module_names[i];
    }
    ngx_modules_n = i;
    ngx_max_module = ngx_modules_n + NGX_MAX_DYNAMIC_MODULES;
    return NGX_OK;
}

在ngx_preinit_modules會(huì)循環(huán)遍歷所有ngx_modules中的值,并對(duì)模塊的index值賦值,用于標(biāo)識(shí)這個(gè)模塊,所以這里存在疑問(wèn)的是ngx_modules的值來(lái)自于哪里?查看源碼,發(fā)現(xiàn)在nginx.c中存在賦值語(yǔ)句 modules = ngx_dlsym(handle, "ngx_modules"):

ngx_event.c:
ngx_event_process_init(ngx_cycle_t *cycle)
{
	// 返回events模塊的配置集合
	ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
	// 遍歷所有模塊
	for (m = 0; cycle->modules[m]; m++) {
        if (cycle->modules[m]->type != NGX_EVENT_MODULE) {
            continue;
        }
		// 如果不等于ecf->use的值就繼續(xù)遍歷
        if (cycle->modules[m]->ctx_index != ecf->use) {
            continue;
        }
		//找到用戶指定的事件處理機(jī)制
        module = cycle->modules[m]->ctx;
		// 執(zhí)行該模塊的初始化函數(shù)
        if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
            /* fatal */
            exit(2);
        }
        break;
    }
}

所以我們可以知道nginx對(duì)事件具體的處理邏輯定義在event/modules各個(gè)處理機(jī)制的模塊里,我們?nèi)∑渲幸粋€(gè)為例(ngx_epoll_module.c),在上面的賦值語(yǔ)句ngx_event_actions = ngx_epoll_module_ctx.actions,其中ngx_event_actions封裝了nginx統(tǒng)一的事件處理調(diào)用函數(shù),而ngx_epoll_module_ctx則定義了epoll模塊的上下文信息,是ngx_event_module_t類型的靜態(tài)變量,ngx_epoll_module.c中有如下定義:

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;

具體的賦值語(yǔ)句就是定義靜態(tài)變量ngx_epoll_module_ctx:

static ngx_event_module_t  ngx_epoll_module_ctx = {
    &epoll_name,
    ngx_epoll_create_conf,               /* create configuration */
    ngx_epoll_init_conf,                 /* init configuration */
	// 對(duì)actions的賦值
    {
        // 添加事件(讀/寫事件)
        ngx_epoll_add_event,             /* add an event */
        ngx_epoll_del_event,             /* delete an event */
        ngx_epoll_add_event,             /* enable an event */
        ngx_epoll_del_event,             /* disable an event */
        ngx_epoll_add_connection,        /* add an connection */
        ngx_epoll_del_connection,        /* delete an connection */
#if (NGX_HAVE_EVENTFD)
        ngx_epoll_notify,                /* trigger a notify */
#else
        NULL,                            /* trigger a notify */
#endif
        ngx_epoll_process_events,        /* process the events */
        ngx_epoll_init,                  /* init the events */
        ngx_epoll_done,                  /* done the events */
    }
};

到這里,就已經(jīng)通過(guò)ngx_event_actions = ngx_epoll_module_ctx.actions賦值語(yǔ)句將epoll的處理函數(shù)賦值給了nginx的全局變量ngx_event_actions,所以當(dāng)我們調(diào)用全局函數(shù)ngx_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)實(shí)際上就是調(diào)用ngx_epoll_add_event。

到此,相信大家對(duì)“Nginx事件處理模塊怎么理解”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

分享文章:Nginx事件處理模塊怎么理解
本文URL:http://www.chinadenli.net/article36/geicsg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供營(yíng)銷型網(wǎng)站建設(shè)建站公司品牌網(wǎng)站建設(shè)網(wǎng)站收錄微信小程序網(wǎng)站策劃

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

小程序開(kāi)發(fā)