上一篇介绍的是ngx_buf_t,本篇介绍ngx_list_t,几乎在Nginx中无处不在,出现频率非常之高。
Nginx中ngx_list_t在名字是链表的含义,但是实际可以理解成是数组形式单链表,比一般的链表要复杂一些,而ngx_queue_t是我们常说的双向链表。这一点需要澄清。
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;
灰色代表,其他内存空间
蓝色代表,申请的内存已经被使用
白色代表,当前链表节点中还有两个剩余空间可用
需要说明一下,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
#include
#include
#include
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且运行,如下图:
[root@localhost test_demo]#
[root@localhost 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
[root@localhost test_demo]#
[root@localhost test_demo]#
本篇介绍了ngx_list_t并且说明了使用场景,最后结合一个hello程序,来加深对其理解。对于ngx_queue_t不打算介绍,因为它和linux内核中list是一样的。下一篇介绍ngx_array_t。