菜鳥nginx源碼剖析數據結構篇(三) 單向鏈表 ngx_list_t[轉]

菜鳥nginx源碼剖析數據結構篇(三) 單向鏈表 ngx_list_tnginx

 

  • Author:Echo Chen(陳斌)數組

  • Email:chenb19870707@gmail.com數據結構

  • Blog:Blog.csdn.net/chen19870707app

    Date:October 23h, 2014學習

     

    1.ngx_list優點和特色

     

    ngx_list _t是一個順序容器,它其實是動態數組和單向鏈表的結合體,擴容起來比動態數組簡單的多,能夠一次擴容一個數組,因此說它結合了 鏈表插入刪除不須要移動的 和 數組下標快速索引 的優點,設計很是讓人叫絕,此外它還有如下特色:ui

    • 鏈表中存儲的元素是靈活的,能夠是任何一種數據結構。spa

    • 鏈表元素須要佔用的內存由ngx_list_t管理,它已經經過數組分配好了。
    • 小塊的內存使用鏈表訪問O(n)效率是低下的,可使用數組經過直接經過偏移量來直接訪問O(1)。

    2.源代碼位置

     

    頭文件:http://trac.nginx.org/nginx/browser/nginx/src/core/ngx_list.h.net

    源文件:http://trac.nginx.org/nginx/browser/nginx/src/core/ngx_list.c設計

     

    3.數據結構定義

     

    前面說到ngx_list_t是一個數組鏈表,鏈表中的每一個結點都是一個數組,ngx_list_part_t 描述的是鏈表中的一個結點,這個結點又是一個數組,elts爲數組首地址,nelts爲該數組已經使用的個數,*next爲下一個鏈表結點的指針,定義以下:指針

     

       1: typedef struct ngx_list_part_s  ngx_list_part_t;
       2:  
       3: //描述鏈表中的一個結點,這個結點又是一個數組
       4: struct ngx_list_part_s {
       5:     void             *elts;     //首地址
       6:     ngx_uint_t        nelts;    //已經使用的個數
       7:     ngx_list_part_t  *next;     //下一個鏈表節點的指針
       8: };

     

    ngx_list_t是描述整個鏈表,其中last 爲鏈表中最後一個數組,part爲鏈表中首個數組,size爲數組每一個元素佔用空間小,nalloc爲每一個數組能夠容納的元素個數,pool爲鏈表內存池對象,定義以下:

     

     

       1: typedef struct {
       2:     ngx_list_part_t  *last;        //鏈表中最後一個數組元素
       3:     ngx_list_part_t   part;        //鏈表中的首個數組元素
       4:     size_t            size;        //每一個數組元素佔用的空間大小
       5:     ngx_uint_t        nalloc;      //每一個數組結點的容量,即每一個數組最多能夠存放多少個元素
       6:     ngx_pool_t       *pool;        //鏈表中的內存池對象指針
       7: } ngx_list_t;

     

    其結構以下圖所示,最下面一行爲內存映像,能夠看到整個內存中僅僅多了 一個ngx_list_t 和 幾個ngx_list_part_s結果,內存並無太多的浪費,nginx在內存方面的苛刻確實值得咱們學習

     

     

    4.鏈表建立ngx_list_create和初始化ngx_list_init

     

       1: //鏈表建立,pool爲內存池對象,size爲每一個數組元素的大小,n爲每一個數組能夠容納的元素個數
       2: ngx_list_t *ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size)
       3: {
       4:     ngx_list_t  *list;
       5:     
       6:     //分配ngx_list_t結構
       7:     list = ngx_palloc(pool, sizeof(ngx_list_t));
       8:     if (list == NULL) {
       9:         return NULL;
      10:     }
      11:  
      12:     //初始化
      13:     if (ngx_list_init(list, pool, n, size) != NGX_OK) {
      14:         return NULL;
      15:     }
      16:  
      17:     return list;
      18: }
      19:  
      20: //鏈表初始化,list爲鏈表結構(在create中建立),pool爲內存池,size爲每一個數組元素的大小,n爲每一個數組能夠容納的元素個數
      21: static ngx_inline ngx_int_t ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size)
      22: {
      23:     //分配一個數組,並用鏈表首結點指向它
      24:     list->part.elts = ngx_palloc(pool, n * size);
      25:     if (list->part.elts == NULL) {
      26:         return NGX_ERROR;
      27:     }
      28:  
      29:     //已使用個數爲0
      30:     list->part.nelts = 0;
      31:     //沒有下一個結點,next指爲NULL
      32:     list->part.next = NULL;
      33:     //最後一個結點也指向剛剛分配的數組
      34:     list->last = &list->part;
      35:     //數組每一個元素大小爲size
      36:     list->size = size;
      37:     //數組能容納元素個數爲n
      38:     list->nalloc = n;
      39:     //內存池爲pool
      40:     list->pool = pool;
      41:  
      42:     return NGX_OK;
      43: }

     

    能夠看到,ngx_list_init調用成功後,會建立一個數組結點。

     

    5.鏈表添加元素操做ngx_list_push

     

       1: //鏈表添加元素,l爲鏈表結構
       2: void *ngx_list_push(ngx_list_t *l)
       3: {
       4:     void             *elt;
       5:     ngx_list_part_t  *last;
       6:  
       7:     //last指向鏈表最後一個結點
       8:     last = l->last;
       9:  
      10:     //若是最後一個結點的數組已經滿了
      11:     if (last->nelts == l->nalloc) {
      12:  
      13:         /* the last part is full, allocate a new list part */
      14:         
      15:         //從內存池中分配一個ngx_list_part_t數組對象
      16:         last = ngx_palloc(l->pool, sizeof(ngx_list_part_t));
      17:         if (last == NULL) {
      18:             return NULL;
      19:         }
      20:         
      21:         //從內存池中給數組對象元素分配空間,每一個元素大小爲size,元素數目爲nalloc
      22:         last->elts = ngx_palloc(l->pool, l->nalloc * l->size);
      23:         if (last->elts == NULL) {
      24:             return NULL;
      25:         }
      26:  
      27:         //新分配的數組已使用結點個數爲0,下一個鏈表結點爲NULL
      28:         last->nelts = 0;
      29:         last->next = NULL;
      30:  
      31:         //將新分配的數組鏈到鏈表,並把鏈表尾指針指向新分配數組
      32:         l->last->next = last;
      33:         l->last = last;
      34:     }
      35:     
      36:     //elts爲數組首地址,elts + size * nelts 即位爲分配數組元素的位置
      37:     elt = (char *) last->elts + l->size * last->nelts;
      38:     
      39:     //分配個數+1
      40:     last->nelts++;
      41:  
      42:     return elt;
      43: }
     
    能夠看到,push的思想很簡單,每次都往鏈表最後一個數組添加,數組滿後就再增長一個數組結點,這裏push的時間複雜度爲O(1),沒有任何遍歷操做,充分利用了 「鏈表尾插」  和 「數組下標索引」的優點。 
     

    6.疑問

    源代碼中只給了鏈表的插入操做,沒有刪除操做,看起來不完整,這裏猜想這個鏈表結構應該不會直接使用,而是被再次封裝。

     

    7.實戰

     

    看完源代碼,這個list設計仍是很巧妙的,結合了數組和鏈表各自的優點,確實不得不讓人拍手叫絕。下面嘗試給出ngx_list_t的使用例子:

     

     

       1: int main()
       2: {
       3:     ngx_list_t *pList = ngx_list_create( r->pool,4,sizeof( ngx_str_t));
       4:     if( NULL == pList)
       5:     {
       6:         return -1;
       7:     } 
       8:     
       9:     ngx_str_t *str = ngx_list_push(pList);
      10:     if( NULL == str)
      11:     {
      12:         return -1;
      13:     }
      14:  
      15:     str->len = sizeof("This is a test for ngx_list_t");
      16:     str->value = "This is a test for ngx_list_t";
      17:  
      18:     //遍歷
      19:      ngx_list_part_t *part = &pList->part;
      20:     ngx_str_t *str = part->elts;
      21:     
      22:     for(int i = 0;;i++)
      23:     {
      24:         if( i >= part->nelts)
      25:         {   
      26:             //已經遍歷完最後一個數組,沒有下一個結點了
      27:             if( NULL == part->next)
      28:             {
      29:                 break;
      30:             }
      31:             
      32:             //還有下一個數組,part指到下一個數組,並把str指到新數組的地址
      33:             part = part->next;
      34:             str = part->elts;
      35:            
      36:             //重置計數器
      37:             i = 0;
      38:          }
      39:  
      40:         //輸出結點
      41:         cout << "list elemts :" << str[i] << endl;
      42:         
      43:     }
相關文章
相關標籤/搜索