Nginx源码看看-数据结构

27 篇文章 0 订阅
8 篇文章 0 订阅

Nginx源码看看-数据结构

创建链表时候的内存创建和回收

创建缓冲区链表的函数:

typedef struct ngx_chain_s    ngx_chain_t;

struct ngx_chain_s {
    ngx_buf_t    *buf;      // 指向当前的buf缓冲区
    ngx_chain_t  *next;     // 如果这是最后一个ngx_chain_t,需要把next置为NULL
};
ngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool)
{
    ngx_chain_t  *cl;
    cl = pool->chain;       //从pool.chain 链表头部取一个出来
    if (cl)                 //取到后pool.chain 链表指向下一个位置
    {
        pool->chain = cl->next;
        return cl;
    }  //只有pool.chain 没有了才重新创建
    cl = ngx_palloc(pool, sizeof(ngx_chain_t)); 
    if (cl == NULL)
    {
        return NULL; 
    }
    return cl;
}

该宏释放一个ngx_chain_t类型的对象:

#define ngx_free_chain(pool, cl)                                             
    cl->next = pool->chain;                                                  
    pool->chain = cl;
//释放的对象被挂载在pool.chain链表的头部,需要使用的时候重这里再取出

这样子操作,感觉内存的利用率很好,完全不会做没意义的销毁,新创建对象。

如果要释放整个chain,则迭代此链表,对每个节点使用此宏即可。注意:对ngx_chaint_t类型的释放,并不是真的释放了内存,而仅仅是把这个对象挂在了这个pool对象的一个叫做chain的字段对应的chain上,以供下次从这个pool上分配ngx_chain_t类型对象的时候,快速的从这个pool->chain上取下链首元素就返回了,当然,如果这个链是空的,才会真的在这个pool上使用ngx_palloc函数进行分配。

字符大小写转换

#define ngx_tolower(c)      (u_char) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c)
#define ngx_toupper(c)      (u_char) ((c >= 'a' && c <= 'z') ? (c & ~0x20) : c)
/* 通过三元表达式实现字母的大小写转换 */

双向链表 ngx_queue_t 的结构

数据结构定义:

typedef struct ngx_queue_s ngx_queue_t;
struct ngx_queue_s {
    ngx_queue_t  *prev;
    ngx_queue_t  *next;
};

对,没有错就只是这个样子,只有前后两个指针,其他啥都没有了。。。。

必须用到的数据存储不见了,o((⊙﹏⊙))o ,接着看了下用法就发现,简直厉害啊!!

使用方法:

ngx_queue_t free;            // 这是一个小哨兵,不存数据
ngx_queue_init(&free);       // 初始化一下下 正常双向列表一样prev、next指向自己
struct node{
    int data;
    ngx_queue_t queue;       // 把queue这个结构体乖乖的放在我们要创建链表的结构里
}  node1;   
ngx_queue_insert_head(&free, &node1.queue);
// 对!就这么用,先把queue放任意结构体里,再连到free这个哨兵上,都可以做链表用啦,写这个的人真是绝了
// 这里的ngx_queue_insert_head也是正常的操作,只是两个链表指针操作一下而已

到这里,已经可以看出来厉害了,可以随便加到任意结构体里面去,只是还不知道这个data数据怎么取出来。

获取节点内的数据data:

#define ngx_queue_data(q, type, link)                                         \
    (type *) ((u_char *) q - offsetof(type, link))
/* 依靠内存位置的off_t来得到相应节点元素数据的内存地址,
    q为当前地址,type为相应结构体的类型,link为queue */
/* C 库宏 offsetof(type, member-designator) 会生成一个类型为 size_t 的整型常量,它是一个结构成员相对于结构开头的字节偏移量。成员是由 member-designator 给定的,结构的名称是在 type 中给定的。 */

果然最后,获取数据用到了偏移量。

这个样这个双向链表的,就可以当做类似接口一样使用了,想用哪里用哪里,厉害了o( ̄▽ ̄)d

Java 双向链表LinkedList对比

所谓没有对比就么有伤害,所以拿自己平常用的语言来对比一下[JDK1.8]

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    transient int size = 0;
    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;
    transient Node<E> last;
。。。。。。。。。。。   
    /* 真正想看的核心Node的定义 是作为静态内部类定义的*/
private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
。。。。。。。。。。。 
/* 其他的方法就不看了,不是对比的重点。*/

Java 中使用的是泛型来实现各种数据的绑定,节点中是有data数据的。

然后想了一下,如果使用java能不能写一个像nginx那样的呢?

试了一下,结果写不粗来,主类的属性类中取不到主类的数据,(ノ`Д)ノ

(用接口,继承啥的都可以,但不是想要的。。。。。果然面对对象的语言跟C完全不一样)

就是不知道有没有大神能写的出来了。。。。感觉是写法本身跟Java的思想就不匹配

ngx_cycle 核心结构体

Nginx框架是围绕着ngx_cycle_t结构体来控制进程运行的。ngx_cycle_t结构体的prefix、conf_prefix、conf_file等字符串类型成员保存着Nginx配置文件的路径。Nginx在程序运行的时候会使用ngx_init_cycle方法来根据nginx.conf加载模块。

struct ngx_cycle_s {
    void                  ****conf_ctx;
    /* 保存着所有模块存储配置项的结构体的指针,它首先是一个数组,每个数组成员又是一个指针,
       这个指针指向另一个存储着指针的数组 */  

    ngx_pool_t               *pool; 
    /* 内存池 */

    ngx_log_t                *log;
    ngx_log_t                 new_log;
    /* 日志模块中提供了生成基本ngx_log_t日志对象的功能,
       这里的log实际上是在还没有执行ngx_init_cycle方法前,如果有信息需要输出到日志,
       就会暂时使用log对象,它会输出到屏幕。
     * 在ngx_init_cycle方法执行后,将会根据nginx.conf配置文件中的配置项,构造出正确的日志文件,
       此时会对log重新赋值(用new_log的地址覆盖上面的log指针)
     */ 

    ngx_uint_t                log_use_stderr;  
    /* unsigned  log_use_stderr:1; */

    ngx_uint_t                files_n;
    ngx_connection_t        **files;
    /* 对于poll、rtsig这样的事件模块,会以有效文件句柄数来预先建立这些ngx_connection_t结构体,
       以加速事件的收集、分发。这时files就会保存所有ngx_connection_t的指针组成的数组,files_n是指针的总数。*/

    ngx_connection_t         *free_connections;
    ngx_uint_t                free_connection_n;
    /* 空闲连接池,与free_connection_n配合使用 */

    ngx_queue_t               reusable_connections_queue;
    /* 可重复使用的双向连接队列,成员类型是ngx_connection_t */

    ngx_array_t               listening;
    /* 动态数组,表示监听端口及相关参数 */

    ngx_array_t               paths; 
    /* 保存Nginx要操作的所有目录,如果目录不存在,则会试图创建,而创建目录失败将导致Nginx启动失败。*/

    ngx_list_t                open_files; 
    /* 保存Nginx已经打开的所有文件,类型是ngx_open_file_t,Nginx不会主动添加文件,这是给模块自己用的,
       如果需要的话,模块向其中添加文件路径名,Nginx框架会在ngx_init_cycle方法中打开这些文件 */

    ngx_list_t                shared_memory;
    /* 单链表存储ngx_shm_zone_t,每个元素表示一块共享内存。*/

    ngx_uint_t                connection_n;
    ngx_connection_t         *connections;
    ngx_event_t              *read_events;
    ngx_event_t              *write_events;
    /* 表示当前进程中所有的连接对象和它们的读写事件,connection_n是它们的数量 */

    ngx_cycle_t              *old_cycle;
    /* 旧的ngx_cycle_t对象用于引用上一个ngx_cycle_t对象中的成员,例如ngx_init_cycle方法,
       在启动初期,需要建立一个临时的ngx_cycle_t对象保存一些变量,在调用ngx_init_cycle方法时,
       就可以把旧的ngx_cycle_t的对象传进去,而这时old_clcle对象就会保存这个前期的ngx_clcle_t对象。*/ 

    ngx_str_t                 conf_file; 
    /* 配置文件相对于安装目录的路径名 */

    ngx_str_t                 conf_param; 
    /* nginx处理配置文件时需要特殊处理的在命令行携带的参数,一般是-g选项携带的参数 */

    ngx_str_t                 conf_prefix;
    ngx_str_t                 prefix; 
    /* Nginx配置文件所在目录的路径和安装目录的路径 */

    ngx_str_t                 lock_file; 
    /* 用于进程间同步的文件锁名称 */

    ngx_str_t                 hostname; 
    /* 使用gethostname系统调用得到的主机名 */
};

ngx_command_t 核心模块指令

这个结构体在之前看模块开发的时候有用到,一直不理解是什么玩意儿,看了结构体定义,果然清晰多了!

typedef struct ngx_command_s     ngx_command_t;

struct ngx_command_s {
    ngx_str_t             name;            // 配置指令的名称
    ngx_uint_t            type;            // 配置指令的类型(参数个数描述等)
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    /*  当nginx在解析配置的时候,如果遇到这个配置指令,将会把读取到的值传递给这个函数进行解析保存。
        因为具体每个配置指令的值如何处理,只有定义这个配置指令的人是最清楚的,set可能复杂也可能很简单。
        比如errlog模块的“error_log”指令就是调用ngx_error_log写一条日志,并不需要存储什么配置数据。
     *  cf: 保存从配置文件读取到的原始字符串以及相关信息。这个参数的args字段是一个ngx_str_t类型的数组,
            该数组首元素是这个配置指令本身,第二个元素开始才是参数。
        cmd: 这个配置指令对应的ngx_command_t结构。
        conf: 就是定义的存储这个配置值的结构体
     */   

    ngx_uint_t            conf;
    /* 该字段被NGX_HTTP_MODULE类型模块所用,指定当前配置项存储的内存位置。实际上是使用哪个内存池的问题。
       因为http模块对所有http模块所要保存的配置信息,划分了main, server和location三个地方进行存储,
       每个地方都有一个内存池用来分配存储这些信息的内存。
       这里可能的值为NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET或NGX_HTTP_LOC_CONF_OFFSET。
       当然也可以直接置为0,就是NGX_HTTP_MAIN_CONF_OFFSET。 
     */   

    ngx_uint_t            offset; 
    /* 指定该配置项值的精确存放位置,一般指定为某一个结构体变量的字段偏移。
       比如我们定义了一个结构体A,该项配置的值需要存储到该结构体的b字段。那么在这里就可以填写offsetof(A, b)。
       对于有些配置项,它的值不需要保存或者是需要保存到更为复杂的结构中时,这里可以设置为0。 
     */  

    void                 *post; 
    /* 可指向任何一个在读取配置过程中需要的数据,以便于进行配置读取的处理。大多时候不需要,设为0即可。*/
};

static ngx_command_t  ngx_core_commands[];   // 保存了核心指令

ngx_module_t 模块信息

在模块开发的时候要用到这个进行说明模块本身的信息。

从某种意义上来说,这是这个模块最重要的一个信息。它告诉了nginx这个模块的一些信息,上面定义的配置信息,还有模块上下文信息,都是通过这个结构来告诉nginx系统的,也就是加载模块的上层代码,都需要通过定义的这个结构,来获取这些信息。

typedef struct ngx_module_s      ngx_module_t;
struct ngx_module_s {
    ngx_uint_t            ctx_index;
    /* 在相应模块类的计数,nginx模块分为四种:core、event、http和mail
     * ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;
     */
    ngx_uint_t            index;
    /* 模块计数器,按照每个模块在ngx_modules[]数组中的声明顺序,从0开始依次给每个模块赋值 */
    ngx_uint_t            spare0;
    ngx_uint_t            spare1;
    ngx_uint_t            spare2;
    ngx_uint_t            spare3;
    ngx_uint_t            version;     
    /* 模块当前版本 */

    void                 *ctx;
    /* 模块上下文,不同类别模块有不同上下文,ngx_http_conf_ctx_t/ngx_mail_conf_ctx_t/... */
    ngx_command_t        *commands; 
    /* 该模块的命令集,是ngx_command_t数组 */
    ngx_uint_t            type; 
    /* 模块种类:core、event、http和mail */
    ngx_int_t           (*init_master)(ngx_log_t *log);           // master进程初始化调用
    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);       // 模块初始化调用
    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);      // worker进程初始化调用
    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);       // 线程初始化调用
    void                (*exit_thread)(ngx_cycle_t *cycle);       // 线程退出调用
    void                (*exit_process)(ngx_cycle_t *cycle);      // worker进程结束调用
    void                (*exit_master)(ngx_cycle_t *cycle);       // master进程结束调用
    /* callback:如果该模块需要发生这些行为执行特定的功能,可以通过这些回调函数指针注册一个回调函数接口来实现 */
    uintptr_t             spare_hook0;
    uintptr_t             spare_hook1;
    uintptr_t             spare_hook2;
    uintptr_t             spare_hook3;
    uintptr_t             spare_hook4;
    uintptr_t             spare_hook5;
    uintptr_t             spare_hook6;
    uintptr_t             spare_hook7;
};
#define NGX_NUMBER_MAJOR  3
#define NGX_NUMBER_MINOR  1
#define NGX_MODULE_V1          0, 0, 0, 0,                              \
    NGX_DSO_ABI_COMPATIBILITY, NGX_NUMBER_MAJOR, NGX_NUMBER_MINOR
    /* 前7个成员初始化 */
#define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0 
    /* 后8个成员初始化 */

恩。。就这些了,接下来就是从main开始看的东东了。是nginx初始化参数,解析命令行参数。。等等的东西了!目前看不下去了。。。得找个好的资料继续学习才行 ︿( ̄︶ ̄)︿


参考资料

博客(总):https://blog.csdn.net/wuchunlai_2012/article/category/6098793

看源码的好地方:http://lxr.nginx.org/source 【真的很好用】


小杭 _(¦3」∠)_


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小_杭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值