菜鳥學習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。