菜鸟学习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、内存中组织结构

菜鸟学习Nginx之ngx_list_t_第1张图片

灰色代表,其他内存空间

蓝色代表,申请的内存已经被使用

白色代表,当前链表节点中还有两个剩余空间可用

需要说明一下,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。

你可能感兴趣的:(Nginx,开源软件,ngx_list_t,Nginx基础数据结构)