這篇blog是專門解決前篇[openVswitch(OVS)源代碼分析之工作流程(哈希桶結構體的疑惑)](http://blog.csdn.net/yuzhihui_no1/article/details/39558815)中提到的哈希桶結構flex_array結構體成員變量含義的問題。
引用下前篇blog中分析討論得到的flex_array結構體成員變量的含義結論:
~~~
struct {
int element_size; // 這是flex_array_part結構體存放的哈希頭指針的大小
int total_nr_elements; // 這是所有flex_array_part結構體中的哈希頭指針的總個數
int elems_per_part; // 這是每個part指針指向的空間能存儲多少個哈希頭指針
u32 reciprocal_elems;
struct flex_array_part *parts[]; // 結構體指針數組,里面存放的是struct flex_array_part結構的指針
};
~~~
其實這個結論是正確的,這些結構體成員的含義就是這些意思。但前篇分析中這個結論和static inline int elements_fit_in_base(struct flex_array *fa)函數產生矛盾,這里也看下該函數的具體實現:
~~~
static inline int elements_fit_in_base(struct flex_array *fa)
{
// fa->element_size 根據上面的結論應該是哈希頭的大小,flex_array_part結構體中存放的哈希頭大小
// fa->total_nr_elements 根據上面的結論應該是所有哈希頭的總數
// 那么data_size 就是所有存儲哈希頭的空間大小了,矛盾來了
int data_size = fa->element_size * fa->total_nr_elements;
// FLEX_ARRAY_BASE_BYTES_LEFT是什么意思呢?
// #define FLEX_ARRAY_BASE_BYTES_LEFT (FLEX_ARRAY_BASE_SIZE - offsetof(struct flex_array, parts))
// offsetof()宏用來求一個成員在結構體中的偏移量
// 所以所有存儲哈希頭空間的大小和 FLEX_ARRAY_BASE_BYTES_LEFT 比較是什么意思呢?
// 我當時的判斷就是element_size和total_nr_elements這兩個成員變量理解錯了。
if (data_size <= FLEX_ARRAY_BASE_BYTES_LEFT)
return 1;
return 0;
}
~~~
如果按照一般的思想來分析這個源代碼真的有問題了,至少這個函數分析不下了。那么真正的原因是什么呢?
首先來看下哈希桶內存申請函數(在上篇中有分析)其中傳過來的分別為:elements = sizeof(struct hlist*)和total = 1024(宏定義而來);
再看看上面這個函數的實現:data_size = element_size * total_nr_elements; 也即是 data_size = elements * total;帶入數據得:data_size = 4 * 1024 = 4096(因為兩個參數一個是宏定義的,對整個項目來說是不變的;另外一個也一樣是不會變的。所以可以當做常量帶入去應驗下);
那么現在來看看if判斷語句:data_size <= (4096 - 4*4);因為根據上面的flex_array結構體成員變量可以知道:有3個int型成員和一個u32類型的成員。所以得到parts前有 4*4個字節。用一個頁的大小減去到parts成員前的字節為:4096 - 4*4;
最后把所有數據帶入可以得到:4096 <= (4096 - 4*4);那么這個條件肯定是恒不成立的。所以這個函數就是多余的了,因為data_size的值是一定為4096的,不管flex_array結構中成員變量代表什么意思。而FLEX_ARRAY_BASE_BYTES_LEFT也是一定不變的。
得到上面的結論其實離真相就比較接近了,可以想象得到一個由這么多頂尖的程序員設計出來的項目,不太可能會出現一個冗余的函數,而且在flex_array.c中大量的使用。那么這個函數一定有其他用處,我想了很多種可能,也反復的分析flex_array.c和flex_array.h中的源代碼,最后我得到一種猜想:就是當這個項目中所要的最大元素數非常小,就是說根據需求total不需要1024,不要那么大呢??
猜想:需要的流表項鏈表頭結點比較少(total_nr_elements < 1024),那么不需要分配一個parts指針(一個parts數組指針元素有一個頁大小的空間)來存儲,如果total_nr_elements不大于1020,就沒必要分配parts指針了,直接在flex_array結構體(該結構體的大小為一個頁,有3個int型和1個u32成員,所以剩下的就是1020 * 4個字節了)中存儲就得了。
下面來驗證下這個猜想,來分析調用了static inline int elements_fit_in_base(struct flex_array *fa)函數的各個代碼:
~~~
if (elements_fit_in_base(fa))
part = (struct flex_array_part *)&fa->parts[0];
else {
part_nr = fa_element_to_part_nr(fa, element_nr);
part = __fa_get_part(fa, part_nr, flags);
if (!part)
return -ENOMEM;
}
~~~
這段代碼在很多函數中都有,可以看int flex_array_put(struct flex_array *fa, unsigned int element_nr, void *src,gfp_t flags);數據拷貝函數的具體實現。該代碼中調用了elements_fit_in_base(fa)來判斷,如果成立,也就是說total_nr_elements不大于1020;那么直接用數組頭元素的地址來強轉為需要的結構體,即是直接在數組頭元素存儲的地方開始操作,而不是數組頭元素指向的地方開始操作。說明了數據就是存儲在flex_array結構體中。
下面來看另外一段代碼:
~~~
void flex_array_free_parts(struct flex_array *fa)
{
int part_nr;
if (elements_fit_in_base(fa))
return;
for (part_nr = 0; part_nr < FLEX_ARRAY_NR_BASE_PTRS; part_nr++)
kfree(fa->parts[part_nr]);
}
~~~
看這段代碼大概就知道是用來釋放parts內存的。該代碼中調用了elements_fit_in_base(fa),如果成立,也就是說total_nr_elements不大于1020;那么就直接返回,什么都不執行。這就暗示了這個項目中根本就沒有申請parts內存,所有的流表項鏈表頭結點都是存放在flex_array結構體中的。再看下行的for循環,是從0開始的,更能說明如果total_nr_elements大于1020就一定得申請parts內存。
還有其他代碼中調用了該函數,就不一一列證了。就目前為止來說這個猜想還是比較符合源代碼的,我不能百分百的說這個猜想是正確的,希望有興趣的朋友可以分析下。當然我也在找各種途徑去分析這個矛盾和猜想。?
轉載請注明作者和原文出處,原文地址:[http://blog.csdn.net/yuzhihui_no1/article/details/39939241](http://blog.csdn.net/yuzhihui_no1/article/details/39939241)
分析得比較匆促,若有不正確之處,望大家指正,共同學習!謝謝!!!
- 前言
- OVS datapath模塊分析:packet處理流程
- openVswitch(OVS)源代碼分析之簡介
- openVswitch(OVS)源代碼分析之數據結構
- openVswitch(OVS)源代碼分析之工作流程(收發數據包)
- openVswitch(OVS)源代碼分析之工作流程(數據包處理)
- openVswitch(OVS)源代碼分析之工作流程(key值得提取)
- openVswitch(OVS)源代碼分析之工作流程(flow流表查詢)
- openVswitch(OVS)源代碼的分析技巧(哈希桶結構體為例)
- openVswitch(OVS)源代碼分析之工作流程(哈希桶結構體的解釋)
- openVswitch(OVS)源代碼之linux RCU鎖機制分析
- openVswitch(OVS)源代碼分析 upcall調用(之linux中的NetLink通信機制)
- openVswitch(OVS)源代碼分析 upcall調用(一)