1. 程式人生 > >菜鳥學習Nginx之ngx_list_t

菜鳥學習Nginx之ngx_list_t

上一篇介紹的是ngx_buf_t,本篇介紹ngx_list_t,幾乎在Nginx中無處不在,出現頻率非常之高。

Nginx中ngx_list_t在名字是連結串列的含義,但是實際可以理解成是陣列形式單鏈表,比一般的連結串列要複雜一些,而ngx_queue_t是我們常說的雙向連結串列。這一點需要澄清。

一、資料結構

1.1、資料結構

typedef struct ngx_list_part_s  ngx_list_part_t;
/**
 * 連結串列資料節點
 */
struct ngx_list_part_s {
    void             *elts;//儲存空間 相當於陣列首地址
    ngx_uint_t        nelts;//已經使用節點數目 當nelts==nalloc表示已經存滿
    ngx_list_part_t  *next;//指向下一個節點
};

/**
 * 連結串列
 */
typedef struct {
    ngx_list_part_t  *last; //儲存最後一個節點 定義成指標方便修改指向
    ngx_list_part_t   part; //連結串列首節點
    size_t            size; //每個節點的大小 ngx_list_part_s.elts陣列中一個元素大小
    ngx_uint_t        nalloc; //分配了n個節點,相當於n元素的陣列
    ngx_pool_t       *pool;
} ngx_list_t;

1.2、記憶體中組織結構

灰色代表,其他記憶體空間

藍色代表,申請的記憶體已經被使用

白色代表,當前連結串列節點中還有兩個剩餘空間可用

需要說明一下,last始終指向最後一個節點,最後一個節點之前的節點可用記憶體空間均為0。

二、相關介面

/**
 * 建立ngx_list_t
 * @param pool  記憶體池
 * @param n     陣列大小
 * @param size  陣列中每個元素大小
 */
ngx_list_t *
ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size)
{
    ngx_list_t  *list;

    list = ngx_palloc(pool, sizeof(ngx_list_t));//分配ngx_list_t頭部資訊
    if (list == NULL) {
        return NULL;
    }

    if (ngx_list_init(list, pool, n, size) != NGX_OK) {//初始化ngx_list_t
        return NULL;
    }

    return list;
}

/**
 * 初始化ngx_list_t
 * @param list  待初始化的ngx_list_t
 * @param n     每個陣列包含的元素個數
 * @param size  陣列元素大小
 */
static ngx_inline ngx_int_t
ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size)
{
    list->part.elts = ngx_palloc(pool, n * size);//分配陣列空間
    if (list->part.elts == NULL) {
        return NGX_ERROR;
    }
    //設定各類指標
    list->part.nelts = 0;
    list->part.next = NULL;
    list->last = &list->part;
    list->size = size;
    list->nalloc = n;
    list->pool = pool;

    return NGX_OK;
}
/**
 * 新增元素
 * @param l 待操作的連結串列
 * @return  返回值可用地址,外部呼叫者進行賦值操作
 * @Describe 
 * 和以往使用連結串列方式不太一樣
 * 入參:傳入一個連結串列結構,然後計算該連結串列節點數目以及可寫入地址,返回可用起始地址。
 * 具體賦值操作,在函式返回後,在呼叫的地方進行復制。
 */
void *
ngx_list_push(ngx_list_t *l)
{
    void             *elt;
    ngx_list_part_t  *last;

    last = l->last;
    //最後一個節點沒有可用空間則進行重新分配
    if (last->nelts == l->nalloc) {//表示當前陣列中可用空間為0,需要重新分配

        /* the last part is full, allocate a new list part */

        last = ngx_palloc(l->pool, sizeof(ngx_list_part_t));
        if (last == NULL) {
            return NULL;
        }

        last->elts = ngx_palloc(l->pool, l->nalloc * l->size);
        if (last->elts == NULL) {
            return NULL;
        }

        last->nelts = 0;
        last->next = NULL;

        l->last->next = last;
        l->last = last;
    }
    //偏移指標 返回可用空間
    elt = (char *) last->elts + l->size * last->nelts;
    last->nelts++;

    return elt;
}

對於ngx_list_t有且只有這三個介面,為什麼沒有遍歷iteration介面呢?不需要,因為非常簡單,在Nginx原始碼中,也有一段偽碼:

/*
 *
 *  the iteration through the list:
 *
 *  part = &list.part;//獲取連結串列頭
 *  data = part->elts;//獲取陣列首地址
 *
 *  for (i = 0 ;; i++) {//進行遍歷
 *
 *      if (i >= part->nelts) {//判斷,如i==nelts表示陣列遍歷完畢
 *          if (part->next == NULL) {//判斷是否為最後一個節點
 *              break;
 *          }
 *          //不是最後一個節點 修改指標 重置i
 *          part = part->next;
 *          data = part->elts;
 *          i = 0;
 *      }
 *
 *      ...  data[i] ...//進行資料訪問
 *
 *  }
 */

不知道大家這個ngx_list_t有沒有這樣一個疑問,為什麼刪除節點的介面?不需要,因為這個結構只用於新增操作,不能用於刪除操作。主要原因是內部使用的是陣列結構。如果想支援刪除操作,請使用ngx_queue_t。

三、例子

若想真正執行起來,需要把nginx中.o檔案一起連結起來。具體步驟如下:

1、修改nginx.c中main函式名,例如修改成main_old,因為一個程式只能有一個main函式,並且執行configure && make操作,最後會報錯,原因是缺少main函式,我們直接忽略。

2、在Nginx根目錄建立一個新的目錄test_hello且建立一個檔案hello.c檔案,檔案內容如下:

#include <stdio.h>
#include <string.h>
#include <ngx_config.h>
#include <ngx_core.h>

typedef struct 
{
    ngx_uint_t  id;
    ngx_uint_t  age;
    u_char name[32];
}student_t;


int main()
{
    ngx_pool_t *pool;
    student_t *s1;
    int i = 0;

    pool = ngx_create_pool(1024,NULL); 
    if(pool == NULL)
        return -1;

    ngx_list_t *std_list = ngx_list_create(pool, 2, sizeof(student_t));
    if(std_list == NULL)
        return -1;

    for(i = 0; i < 5; i++) {
        s1 = ngx_list_push(std_list);
        sprintf(s1->name, "Student-%d", i);
        s1->id = i;
        s1->age = 20+i;
    }

    printf("iteration list ele info:\n");
    ngx_list_part_t *part = &std_list->part;
    s1 = (student_t *)part->elts;
    for (i = 0; ; i++) {
        if (i >= part->nelts) {
            if (part->next == NULL)
                break;

            part = part->next;
            s1 = part->elts;
            i = 0;
        }

        printf("ele name:%s  id:%d  age:%d\n", s1[i].name, s1[i].id, s1[i].age);
    }

    ngx_destroy_pool(pool);
    return 0;
}

3、在test_hello目錄中建立一個Makefile檔案,內容如下:

TestHello: hello.o libnginx.a
        gcc hello.o libnginx.a -o TestHello -ldl -lpthread -lcrypt -lpcre -lz -Wl,-E
hello.o:
        gcc -I../src/core -I../objs -I../src/os/unix  -c hello.c -o hello.o
libnginx.a:
        ar rv libnginx.a \
        ../objs//src/core/nginx.o \
        ../objs//src/core/ngx_log.o \
        ../objs//src/core/ngx_palloc.o \
        ../objs//src/core/ngx_array.o \
        ../objs//src/core/ngx_list.o \
        ../objs//src/core/ngx_hash.o \
        ../objs//src/core/ngx_buf.o \
        ../objs//src/core/ngx_queue.o \
        ../objs//src/core/ngx_output_chain.o \
        ../objs//src/core/ngx_string.o \
        ../objs//src/core/ngx_parse.o \
        ../objs//src/core/ngx_parse_time.o \
        ../objs//src/core/ngx_inet.o \
        ../objs//src/core/ngx_file.o \
        ../objs//src/core/ngx_crc32.o \
        ../objs//src/core/ngx_murmurhash.o \
        ../objs//src/core/ngx_md5.o \
        ../objs//src/core/ngx_sha1.o \
        ../objs//src/core/ngx_rbtree.o \
        ../objs//src/core/ngx_radix_tree.o \
        ../objs//src/core/ngx_slab.o \
        ../objs//src/core/ngx_times.o \
        ../objs//src/core/ngx_shmtx.o \
        ../objs//src/core/ngx_connection.o \
        ../objs//src/core/ngx_cycle.o \
        ../objs//src/core/ngx_spinlock.o \
        ../objs//src/core/ngx_rwlock.o \
        ../objs//src/core/ngx_cpuinfo.o \
        ../objs//src/core/ngx_conf_file.o \
        ../objs//src/core/ngx_module.o \
        ../objs//src/core/ngx_resolver.o \
        ../objs//src/core/ngx_open_file_cache.o \
        ../objs//src/core/ngx_crypt.o \
        ../objs//src/core/ngx_proxy_protocol.o \
        ../objs//src/core/ngx_syslog.o \
        ../objs//src/event/ngx_event.o \
        ../objs//src/event/ngx_event_timer.o \
        ../objs//src/event/ngx_event_posted.o \
        ../objs//src/event/ngx_event_accept.o \
        ../objs//src/event/ngx_event_connect.o \
        ../objs//src/event/ngx_event_pipe.o \
        ../objs//src/os/unix/ngx_time.o \
        ../objs//src/os/unix/ngx_errno.o \
        ../objs//src/os/unix/ngx_alloc.o \
        ../objs//src/os/unix/ngx_files.o \
        ../objs//src/os/unix/ngx_socket.o \
        ../objs//src/os/unix/ngx_recv.o \
        ../objs//src/os/unix/ngx_readv_chain.o \
        ../objs//src/os/unix/ngx_udp_recv.o \
        ../objs//src/os/unix/ngx_send.o \
        ../objs//src/os/unix/ngx_writev_chain.o \
        ../objs//src/os/unix/ngx_udp_send.o \
        ../objs//src/os/unix/ngx_udp_sendmsg_chain.o \
        ../objs//src/os/unix/ngx_channel.o \
        ../objs//src/os/unix/ngx_shmem.o \
        ../objs//src/os/unix/ngx_process.o \
        ../objs//src/os/unix/ngx_daemon.o \
        ../objs//src/os/unix/ngx_setaffinity.o \
        ../objs//src/os/unix/ngx_setproctitle.o \
        ../objs//src/os/unix/ngx_posix_init.o \
        ../objs//src/os/unix/ngx_user.o \
        ../objs//src/os/unix/ngx_dlopen.o \
        ../objs//src/os/unix/ngx_process_cycle.o \
        ../objs//src/os/unix/ngx_linux_init.o \
        ../objs//src/event/modules/ngx_epoll_module.o \
        ../objs//src/os/unix/ngx_linux_sendfile_chain.o \
        ../objs//src/core/ngx_regex.o \
        ../objs//src/http/ngx_http.o \
        ../objs//src/http/ngx_http_core_module.o \
        ../objs//src/http/ngx_http_special_response.o \
        ../objs//src/http/ngx_http_request.o \
        ../objs//src/http/ngx_http_parse.o \
        ../objs//src/http/modules/ngx_http_log_module.o \
        ../objs//src/http/ngx_http_request_body.o \
        ../objs//src/http/ngx_http_variables.o \
        ../objs//src/http/ngx_http_script.o \
        ../objs//src/http/ngx_http_upstream.o \
        ../objs//src/http/ngx_http_upstream_round_robin.o \
        ../objs//src/http/ngx_http_file_cache.o \
        ../objs//src/http/ngx_http_write_filter_module.o \
        ../objs//src/http/ngx_http_header_filter_module.o \
        ../objs//src/http/modules/ngx_http_chunked_filter_module.o \
        ../objs//src/http/modules/ngx_http_range_filter_module.o \
        ../objs//src/http/modules/ngx_http_gzip_filter_module.o \
        ../objs//src/http/ngx_http_postpone_filter_module.o \
        ../objs//src/http/modules/ngx_http_ssi_filter_module.o \
        ../objs//src/http/modules/ngx_http_charset_filter_module.o \
        ../objs//src/http/modules/ngx_http_userid_filter_module.o \
        ../objs//src/http/modules/ngx_http_headers_filter_module.o \
        ../objs//src/http/ngx_http_copy_filter_module.o \
        ../objs//src/http/modules/ngx_http_not_modified_filter_module.o \
        ../objs//src/http/modules/ngx_http_static_module.o \
        ../objs//src/http/modules/ngx_http_autoindex_module.o \
        ../objs//src/http/modules/ngx_http_index_module.o \
        ../objs//src/http/modules/ngx_http_auth_basic_module.o \
        ../objs//src/http/modules/ngx_http_access_module.o \
        ../objs//src/http/modules/ngx_http_limit_conn_module.o \
        ../objs//src/http/modules/ngx_http_limit_req_module.o \
        ../objs//src/http/modules/ngx_http_geo_module.o \
        ../objs//src/http/modules/ngx_http_map_module.o \
        ../objs//src/http/modules/ngx_http_split_clients_module.o \
        ../objs//src/http/modules/ngx_http_referer_module.o \
        ../objs//src/http/modules/ngx_http_rewrite_module.o \
        ../objs//src/http/modules/ngx_http_proxy_module.o \
        ../objs//src/http/modules/ngx_http_fastcgi_module.o \
        ../objs//src/http/modules/ngx_http_uwsgi_module.o \
        ../objs//src/http/modules/ngx_http_scgi_module.o \
        ../objs//src/http/modules/ngx_http_memcached_module.o \
        ../objs//src/http/modules/ngx_http_empty_gif_module.o \
        ../objs//src/http/modules/ngx_http_browser_module.o \
        ../objs//src/http/modules/ngx_http_upstream_hash_module.o \
        ../objs//src/http/modules/ngx_http_upstream_ip_hash_module.o \
        ../objs//src/http/modules/ngx_http_upstream_least_conn_module.o \
        ../objs//src/http/modules/ngx_http_upstream_keepalive_module.o \
        ../objs//src/http/modules/ngx_http_upstream_zone_module.o \
        ../objs//ngx_modules.o

然後執行make且執行,如下圖:

[[email protected] test_demo]#
[[email protected] test_demo]# ./TestHello
iteration list ele info:
ele name:Student-0  id:0  age:20
ele name:Student-1  id:1  age:21
ele name:Student-2  id:2  age:22
ele name:Student-3  id:3  age:23
ele name:Student-4  id:4  age:24
[[email protected] test_demo]#
[[email protected] test_demo]#

四、總結

本篇介紹了ngx_list_t並且說明了使用場景,最後結合一個hello程式,來加深對其理解。對於ngx_queue_t不打算介紹,因為它和linux核心中list是一樣的。下一篇介紹ngx_array_t