niginx源码分析二----基础数据结构

    xiaoxiao2023-11-16  162

    为了保证操作的高效性以及对内存的严格控制,nginx自己封装了很多基础的数据结构,这些基本的数据类型在nginx的源码中随处可见,熟悉和掌握各种类型的用法是了解nginx底层原理的基础,下面就一起看看这些独具特色的ngx系风格的数据类型。 (以下未做注释的属性成员都是不太重要的,可忽略。)

    ngx_str_t

    相关函数:

    初始化: ngx_str_null(&local_conf->hello_string); 赋值: ngx_str_set(&r->headers_out.content_type, "text/html");

    ngx_buf_t

    描述:缓冲区ngx_buf_t是nginx处理大数据的关键,它既应用于内存数据也应用于磁盘数据,ngx_buf_t本质上提供了一些指针成员和标志位。 位置: ngx_buf.h 定义: struct ngx_buf_s { u_char *pos; /* pos是告诉使用者本次应该从pos这个位置开始处理内存中的数据,因为同一个ngx_buf_t可能被多次反复处理,pos的作用类似文件处理过程中的文件指针*/ u_char *last; /* last 表示有效的内容到此为止, 一般pos到last之间的内容是希望nginx处理的内容*/ off_t file_pos; /* 处理文件时,file_pos和file_last作用和post,last作用类似*/ off_t file_last; u_char *start; /* start of buffer */ u_char *end; /* end of buffer */ ngx_buf_tag_t tag; /* 表示当前缓冲区的类型,例如由哪个模块使用就指向这个模块ngx_module_t变量的地址*/ ngx_file_t *file; /* 引用的文件*/ ngx_buf_t *shadow; /* 当前缓冲区的影子缓冲区,非重点*/ /* the buf's content could be changed */ unsigned temporary:1; /* * the buf's content is in a memory cache or in a read only memory * and must not be changed */ unsigned memory:1; /* the buf's content is mmap()ed and must not be changed */ unsigned mmap:1; /* 标志位:为1时表示这段内存是由mmap系统映射过来的,不可以被修改*/ unsigned recycled:1; /* 标志位:为1时表示这段内存可回收*/ unsigned in_file:1; /* 标志位:为1时表示处理的这段缓冲区是文件而不是内存*/ unsigned flush:1; /* 标志位:为1时表示需要执行flush操作*/ unsigned sync:1; /* 标志位:为1时表示以同步的方式处理这块内存,需谨慎*/ unsigned last_buf:1; /* 标志位:表示是否是最后一块缓冲区*/ unsigned last_in_chain:1; /* 标志位:表示是否是ngx_chain_t中的最后一块缓冲区*/ unsigned last_shadow:1; /*标志位:表示是否是最后一个影子缓冲区,和shadow配合使用*/ unsigned temp_file:1; /*标志位:表示当前缓存区是否属于临时文件*/ /* STUB */ int num; };

    常用方法: ngx_palloc(ngx_pool_t *pool, size_t size) ngx_pcalloc(r->pool, 1024) ngx_create_temp_buf(r->pool,1024)

    ngx_file_t

    描述:nginx通过ngx_file_t结构体来描述某个文件 位置: ngx_file.h

    struct ngx_file_s { ngx_fd_t fd; //文件句柄描述符 ngx_str_t name; //文件名称 ngx_file_info_t info; //文件大小等资源信息,实际就是Linux系统定义的stat结构 off_t offset; //该偏移量告诉Nginx现在文件处理到哪里了,一般不用设置它,Nginx框架会根据当前发送状态设置它 off_t sys_offset; //当前文件系统偏移量,一般不用设置它,同样由nginx框架设置 ngx_log_t *log; //日志对象,相关的日志文件会输出到log指定的日志文件中 #if (NGX_THREADS || NGX_COMPAT) ngx_int_t (*thread_handler)(ngx_thread_task_t *task, ngx_file_t *file); void *thread_ctx; ngx_thread_task_t *thread_task; #endif #if (NGX_HAVE_FILE_AIO || NGX_COMPAT) ngx_event_aio_t *aio; #endif unsigned valid_info:1; unsigned directio:1; };

    ngx_pool_cleanup_t

    描述:对于nginx打开的文件句柄,请求执行完毕后,需要及时关闭掉,否则会出现句柄泄露

    struct ngx_pool_cleanup_s { ngx_pool_cleanup_pt handler; //执行清理资源工作的回调方法 void *data; // handler方法需要的参数 ngx_pool_cleanup_t *next; //下一个需要清理的对象 };

    ngx_array_t

    动态数组:

    typedef struct { void *elts; // 数组元素的起始地址 ngx_uint_t nelts; // 当前已经存放的数组元素个数 size_t size; // 每个元素所占用的空间大小 ngx_uint_t nalloc; // 数组中能存放的元素总数 ngx_pool_t *pool; // 该数组所属的内存池地址 } ngx_array_t;

    nginx提供的五个方法:

    创建数组,并分配n个大小为size的空间 ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size) { ngx_array_t *a; // 先申请结构体ngx_array_t的内存 a = ngx_palloc(p, sizeof(ngx_array_t)); if (a == NULL) { return NULL; } // 然后调用ngx_array_init申请各元素的内存空间 if (ngx_array_init(a, p, n, size) != NGX_OK) { return NULL; } return a; } 销毁数组 void ngx_array_destroy(ngx_array_t *a) { ngx_pool_t *p; p = a->pool; // 如果内存池的起始可用地址正好是数组a的结束地址,则直接把起始可用地址往前移动数组的长度大小位置即可 if ((u_char *) a->elts + a->size * a->nalloc == p->d.last) { p->d.last -= a->size * a->nalloc; } // 继续将p的last指针往前移动一个ngx_array_t 结构体的大小位置,回收掉结构体本身占用的那部分内存 if ((u_char *) a + sizeof(ngx_array_t) == p->d.last) { p->d.last = (u_char *) a; } } 数组初始化 ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size) { array->nelts = 0; array->size = size; array->nalloc = n; array->pool = pool; array->elts = ngx_palloc(pool, n * size); if (array->elts == NULL) { return NGX_ERROR; } return NGX_OK; } 添加一个元素 void * ngx_array_push(ngx_array_t *a) { void *elt, *new; size_t size; ngx_pool_t *p; // 数组中元素已经达到了最大值,需要扩容 if (a->nelts == a->nalloc) { /* the array is full */ size = a->size * a->nalloc; p = a->pool; // 如果内存池中剩余的空间比一个数组元素还大,则将内存池的last指针往后移动一个单位。 if ((u_char *) a->elts + size == p->d.last && p->d.last + a->size <= p->d.end) { /* * the array allocation is the last in the pool * and there is space for new allocation */ p->d.last += a->size; a->nalloc++; // 当前内存池剩余空间已不足以容纳一个元素,则需要重新申请动态数组,并且是申请的空间大小是原来的2倍,然后将旧数组中的元素拷贝至新数组,这个过程可能会耗时较长 } else { /* allocate a new array */ new = ngx_palloc(p, 2 * size); if (new == NULL) { return NULL; } ngx_memcpy(new, a->elts, size); a->elts = new; a->nalloc *= 2; } } elt = (u_char *) a->elts + a->size * a->nelts; a->nelts++; return elt; } 添加n个元素 void * ngx_array_push_n(ngx_array_t *a, ngx_uint_t n) { void *elt, *new; size_t size; ngx_uint_t nalloc; ngx_pool_t *p; size = n * a->size; if (a->nelts + n > a->nalloc) { /* the array is full */ p = a->pool; if ((u_char *) a->elts + a->size * a->nalloc == p->d.last && p->d.last + size <= p->d.end) { /* * the array allocation is the last in the pool * and there is space for new allocation */ p->d.last += size; a->nalloc += n; } else { /* allocate a new array */ nalloc = 2 * ((n >= a->nalloc) ? n : a->nalloc); new = ngx_palloc(p, nalloc * a->size); if (new == NULL) { return NULL; } ngx_memcpy(new, a->elts, a->nelts * a->size); a->elts = new; a->nalloc = nalloc; } } elt = (u_char *) a->elts + a->size * a->nelts; a->nelts += n; return elt; }

    说明: ngx_array_push和ngx_array_push_n都可能触发数组的扩容操作,扩容的时候会判断当前内存池是否有足够的空间,如果有,则只需要扩容一个或者n个元素大小的空间;如果内存不足够,则会重新申请动态数组的空间,并且申请的空间大小为原来的2倍,并将元素从旧数组拷贝至新数组中。

    最新回复(0)