nginx源码分析--数组链表

数组和链表的优缺点我们都知道,nginx的数组链表结合了这两种数据结构的优点,表面是链表,链表里每个节点是一个固定大小的数组。结构如下图。
这里写图片描述
ngx_list.h


/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */


#ifndef _NGX_LIST_H_INCLUDED_
#define _NGX_LIST_H_INCLUDED_


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


typedef struct ngx_list_part_s  ngx_list_part_t;

/*
    ngx_list是一个数组链表,表面是一个链表,然后链表里的每一个节点都是一个固定大小的数组,
    ngx_list_t是整个结构的领导者,他指向ngx_list_part_s,ngx_list_part_s指向数组的存储空间,
    因为链表中每个节点,对应的数组可以存储多少个元素,每个元素的大小是固定的,所以这些元数据是存在ngx_list_s中,
    没必要每个节点都存一份。ngx_list_part_s就是这个链表中的节点结构体,
*/
struct ngx_list_part_s {
    void             *elts; // 数组首地址
    ngx_uint_t        nelts; // 数组已经存储了多少个元素
    ngx_list_part_t  *next; // 指向下一个节点的指针
};


typedef struct {
    /*
        最后一个数组节点的结构体指针,这个指针指向的节点是当前也是唯一一个可以用来存储数据的节点,
        每次需要存储数据的时候,都是从last指针指向的节点开始找的,如果存满了就继续增加节点,此时last指向新的
        节点,last一直指向当前可以存储数据的数据节点。
    */
    ngx_list_part_t  *last; 
    ngx_list_part_t   part; // 第一个数组节点的结构体
    size_t            size; // 数组中,每个元素的大小
    ngx_uint_t        nalloc; // 每个数组节点可以存储多少个元素
    ngx_pool_t       *pool; // 用来分配内存的pool
} ngx_list_t;


ngx_list_t *ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size);


/*
    初始化数组链表函数
    @param list需要初始化的链表结构体
    @param pool 用于分配内存的pool
    @param n 该数组分配共可以存储几个元素
    @param size 该数组中每个元素的大小
    @return NGX_OK | NGX_ERROR
*/

static ngx_inline ngx_int_t ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size)
{
    // 分配一个n*size大小的数组。并用赋值给第一个链表节点的list->part.elts,指向数组的首地址
    list->part.elts = ngx_palloc(pool, n * size);
    if (list->part.elts == NULL) {
        return NGX_ERROR;
    }
    // 数组已存0个元素
    list->part.nelts = 0;
    // 下一个链表节点
    list->part.next = NULL;
    // 此时,第一个节点是最后一个节点,需要取地址
    list->last = &list->part;
    // 每个链表节点中,每个数组元素的大小
    list->size = size;
     // 每个链表节点中,数组最多可以存储多少个元素
    list->nalloc = n;
    list->pool = pool;

    return NGX_OK;
}


/*
 *
 *  the iteration through the list:
 *
 *  part = &list.part;
 *  data = part->elts;
 *
 *  for (i = 0 ;; i++) {
 *
 *      if (i >= part->nelts) {
 *          if (part->next == NULL) {
 *              break;
 *          }
 *
 *          part = part->next;
 *          data = part->elts;
 *          i = 0;
 *      }
 *
 *      ...  data[i] ...
 *
 *  }
 */


void *ngx_list_push(ngx_list_t *list);


#endif /* _NGX_LIST_H_INCLUDED_ */

ngx_list.c


/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */


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

/*
    创建数组链表函数
    @param pool 用于分配内存的pool
    @param n 该数组分配共可以存储几个元素
    @param size 该数组中每个元素的大小
    @return 指向数组链表的结构体指针 ngx_list_t
*/
ngx_list_t * ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size)
{
    ngx_list_t  *list;
    // 分配一个ngx_list_t结构体大小的空间,用来存储数组链表的一些信息
    list = ngx_palloc(pool, sizeof(ngx_list_t));
    if (list == NULL) {
        return NULL;
    }
    // 分配n * size大小的数组,并初始化第一个数组节点,使得第一个数组节点的指针指向分配的内存
    list->part.elts = ngx_palloc(pool, n * size);
    if (list->part.elts == NULL) {
        return NULL;
    }
    // 同ngx_list_init函数
    list->part.nelts = 0;
    list->part.next = NULL;
    list->last = &list->part;
    list->size = size;
    list->nalloc = n;
    list->pool = pool;

    return list;
}

/*
    返回可以用来存储数据的首地址,如果第一个数组节点没有空间就往后面的节点找,如果都没有空间就动态增加数组节点
    @param l 存储数据的链表
    @return 可以用来存储数据的首地址
*/
void * ngx_list_push(ngx_list_t *l)
{
    void             *elt;
    ngx_list_part_t  *last;

    last = l->last;
    // 最后一个数组节点都已经存满了,需要增加新的数组节点
    if (last->nelts == l->nalloc) {

        /* 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管理
        last->elts = ngx_palloc(l->pool, l->nalloc * l->size);
        if (last->elts == NULL) {
            return NULL;
        }
        // 新开辟的数组存储了0个元素
        last->nelts = 0;
        // 该数组节点是最后一个数组节点,next为NULL
        last->next = NULL;
        // 把新开辟的数组节点插入到链表的最后面
        l->last->next = last;
        // 此时新开辟的数组节点是最后一个节点,也是唯一一个可用节点,可用的意思是还可以用来存储元素
        l->last = last;
    }
    // 不管需不需要新增数组节点,last->elts + l->size * last->nelts此时都指向第一个可用的地址
    elt = (char *) last->elts + l->size * last->nelts;
    // 存储的元素个数加1
    last->nelts++;
    // 返回可存储数据的首地址,nginx里都是返回一个地址,然后在push函数外进行数据存储,而不是在push函数里操作
    return elt;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值