摘要:請注意這里的和是全局變量,而和是模塊的靜態變量是模塊級的全局變量,這一點很重要,后面會詳細分析。當編譯進一個模塊的時候,就被賦值為當前模塊的處理函數。所以整體看來,就像用全局變量組成的一條單向鏈表。
最近開始使用Nginx的第三方擴展解決實際的問題,對Nginx的擴展開發產生了一些興趣,在閱讀第三方代碼時產生了一些心得和體會。本文詳細分析了進行Nginx過濾器開發的時候,Nginx提供的注冊過濾器的精妙機制。參考Nginx開發從入門到精通-過濾模塊
原文:Nginx源碼:利用C語言tricky構建函數鏈
過濾模塊簡介Nginx本身就是模塊化的設計,在處理HTTP請求的過程中,就是由各種不同的模塊在不同的時機參與處理請求和回發響應。模塊就像流水線上的工人一樣,在特定的位置做特定的事情,如果想要對請求做新的處理,只需要添加新的工人。工人處理完自己的工作后,就交給下一個工人處理,直到全部處理完。過濾模塊是一類模塊,它們即可以處理請求頭部,也可以處理請求體。
Nginx的另一個特點是,所有的模塊都是通過編譯,直接生成在Nginx的可執行文件中的,并不是動態加載的,這也是Nginx維持高性能的原因之一。
開發一個過濾模塊注冊一個過濾模塊時,通常都需要執行類似下面的初始化代碼:
static ngx_int_t ngx_http_zip_header_filter(ngx_http_request_t *r); static ngx_int_t ngx_http_zip_body_filter(ngx_http_request_t *r, ngx_chain_t *in); 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 ngx_int_t ngx_http_zip_init(ngx_conf_t *cf) { ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_zip_header_filter; ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_zip_body_filter; return NGX_OK; }
任何過濾模塊的初始化代碼都會被Nginx在初始化時調用。請注意:這里的ngx_http_top_header_filter和ngx_http_top_body_filter是全局變量,而ngx_http_next_header_filter和ngx_http_next_body_filter是模塊的靜態變量(是模塊級的全局變量),這一點很重要,后面會詳細分析。
通過如下調用,將請求交由下一個過濾模塊處理:
return ngx_http_next_body_filter(r, in);
表面上看這里的ngx_http_next_body_filter似乎就是本模塊的ngx_http_zip_body_filter啊,怎么是調用其他模塊的處理函數呢?
ngx_http_top_header_filter是一個全局變量。當編譯進一個filter模塊的時候,就被賦值為當前filter模塊的處理函數。而ngx_http_next_header_filter是一個局部全局變量,它保存了編譯前上一個filter模塊的處理函數。所以整體看來,就像用全局變量組成的一條單向鏈表。
上面對這個單向鏈表的解釋有些籠統,對于我這種業余選手,理解起來有些困難。下面從C編譯器的工作原理角度詳細分析一下
詳細分析模塊的編譯為了簡化描述,我們只考慮header過濾器,而且用top表示ngx_http_top_header_filter,用next表示ngx_http_next_header_filter。
假設我們有3個模塊a.c,b.c,c.c,大致都是按照上面初始化代碼編寫的,比如a.c模塊偽代碼如下(忽略各種函數傳參):
static function a(); static function next(); static ngx_int_t init() { next = top; top = a; return NGX_OK; }
b.c和c.c的代碼也是如此,只是將a函數分別變成b和c。
編譯由于top是nginx定義的全局函數指針變量,屬于unsolved symbol,next是靜態變量,只在c語言模塊中有效,所以c編譯器在完成模塊編譯后,生成的a.o大致是這樣的:
如上圖:top在未解決符號表,等待鏈接器處理,next在模塊變量部分,next靜態變量在編譯時默認值為0,假設a函數在a.o模塊中的地址(偏移量)是0xaaa;init函數中,將top所在的內存中的值賦值給next的值簡化為next.value=top.value,將a函數賦值給top,等同于將a函數在編譯時的地址值0xaaa寫入top所在的內存。
相應的b.o和c.o大致是這樣的:
鏈接鏈接就是把.o文件拼接在一起,在拼接過程中需要做兩件重要的事情:一個是地址偏移重定向,這個過程可以確定全局變量在代碼段中的位置。二是將各個模塊中所有的未解決符號引用改成實際的地址。在這個例子中top作為全局變量,在鏈接的時候ngx_xxx.o被鏈接進來,并確定了其在最后可執行文件中的位置,我們假設是0x111,然后各個模塊對top的引用都將修改成這個地址,最后在可執行文件中是這樣的(忽略地址偏移重定向):
初始化上面說過Nginx會在初始化的時候,執行各個模塊的init,假設這里依次執行a.init、b.init、c.init,內存如下:
注意圖中紅色的變量的變化。最后,top這個函數指針指向了c模塊的c函數(c函數的偏移地址為0xccc),而c模塊的next這個函數指針指向了b模塊的b函數(b函數的偏移地址為0xbbb),而b模塊的next這個函數指針指向了a模塊的a函數(a函數的偏移地址為0xaaa),a模塊的next指針為0。這樣在b函數中調用本模塊b的next,卻執行了a模塊的a函數,而這里的a,b,c函數都是過濾器的實際處理函數,因此,過濾器處理函數如同一條鏈一樣通過各自模塊的next彼此相連。Nginx只需要調用top,就可以按照c()->b()—>a()將所有的處理函數都執行一遍(當然前提是處理函數都會調用next)
不得不承認此種方法的精妙。
問:32位的內存表示不應該是0xaaaaaaaa,怎么只有0xaaa??
答:好吧,css看多了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/39076.html
摘要:而對于堆內存,通常需要程序員進行管理。我們通常說的內存管理亦是只堆空間內存管理。內存管理整體可以分為個部分,第一部分是常規的內存池,用于進程平時所需的內存管理第二部分是共享內存的管理。將內存塊按照的整數次冪進行劃分最小為最大為。 施洪寶 一. 概述 應用程序的內存可以簡單分為堆內存,棧內存。對于棧內存而言,在函數編譯時,編譯器會插入移動棧當前指針位置的代碼,實現棧空間的自管理。而對于...
摘要:而對于堆內存,通常需要程序員進行管理。二內存池管理說明本部分使用的版本為具體源碼參見文件實現使用流程內存池的使用較為簡單可以分為步,調用函數獲取指針。將內存塊按照的整數次冪進行劃分最小為最大為。 運營研發團隊 施洪寶 一. 概述 應用程序的內存可以簡單分為堆內存,棧內存。對于棧內存而言,在函數編譯時,編譯器會插入移動棧當前指針位置的代碼,實現棧空間的自管理。而對于堆內存,通常需要程序...
摘要:源代碼路徑版本主要作用分析提供了一種機制,幫助進行資源管理內存文件。用來標記該使用時分配失敗次數。根據以上思路,可以很容易明白源碼里關于創建鏈表的代碼函數聲明說明輸入要分配的節點大小,返回一個的指針。 源代碼路徑 版本:1.8.0 srccoreNgx_palloc.h srccoreNgx_palloc.c 主要作用分析 提供了一種機制,幫助進行資源管理(內存、文件)。可以...
摘要:關于是自身實現的一個內存池模塊,其遍及整個的源碼之中,也是能簡潔高效處理各個請求的基礎所在。它本身是一個記錄表,其中記錄了整個內內存池的內存分配信息鏈的頭指針。 關于 palloc是nginx自身實現的一個內存池模塊,其遍及整個nginx的源碼之中,也是nginx能簡潔高效處理各個請求的基礎所在。本文先從ngx_alloc和ngx_palloc2個文件來解讀內存模塊。 ngx_all...
摘要:現在使用的各種哈希函數基本上只能保證較小概率出現兩個不同的其相同的情況。而出現兩個值對應的相同的情況,稱為哈希沖突。中的哈希表需要指出的是,中自造的哈希表屬于內部使用的數據結構,因此,并不是一個通用的哈希表。 源文件路徑 版本:1.8.0 csrccoreNgx_hash.h srccoreNgx_hash.c 關于hash表 Nginx實現的hash表和常見的hash表大體...
閱讀 3949·2021-11-22 13:53
閱讀 1676·2021-08-25 09:39
閱讀 2410·2019-08-29 18:36
閱讀 1469·2019-08-26 13:35
閱讀 1215·2019-08-26 11:57
閱讀 1678·2019-08-23 15:57
閱讀 803·2019-08-23 14:55
閱讀 1163·2019-08-23 14:51