2014-06-16
Nginx 高级数据结构 1

本文主要描述 Nginx 中的几种高级数据结构,参考《深入理解 Nginx》,结合源码,对这些数据结构进行一些解剖。文章中的代码可以在这里找到。

1. ngx_queue_t 双向链表

首先,ngx_queue_t 是不从内存池分配内存的,所以有关双向链表的所有内存都由程序员自己负责。ngx_queue_t 的定义如下(src/core/ngx_queue.h)

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

这里定义的其实是两个指针,一个指向前一个节点,一个指向后一个节点,在需要使用双链表的地方加上一个 ngx_queue_t 变量即可。ngx_queue_t 有关的操作函数如下:

ngx_queue_int(h) //h 为链表结构体 ngx_queue_t 的指针。初始化双链表
ngx_queue_empty(h) //h 为链表容器结构体 ngx_queue_t 的指针。 判断链表是否为空
ngx_queue_insert_head(h, x) //h 为链表容器结构体 ngx_queue_t 的指针,x 为插入元素结构体中 ngx_queue_t 成员的指针。将 x 插入到链表头部
ngx_queue_insert_tail(h, x) //h 为链表容器结构体 ngx_queue_t 的指针,x 为插入元素结构体中 ngx_queue_t 成员的指针。将 x 插入到链表尾部
ngx_queue_head(h) //h 为链表容器结构体 ngx_queue_t 的指针。返回链表容器 h 中的第一个元素的 ngx_queue_t 结构体指针
ngx_queue_last(h) //h 为链表容器结构体 ngx_queue_t 的指针。返回链表容器 h 中的最后一个元素的 ngx_queue_t 结构体指针
ngx_queue_sentinel(h) //h 为链表容器结构体 ngx_queue_t 的指针。返回链表结构体的指针
ngx_queue_remove(x) //x 为链表容器结构体 ngx_queue_t 的指针。从容器中移除 x 元素
ngx_queue_split(h, q, n)//h 为链表容器结构体 ngx_queue_t 的指针。该函数用于拆分链表,h 是链表容器,而 q 是链表 h 中的一个元素。这个方法将链表 h 以元素 q 为界拆分成两个链表 h 和 n
ngx_queue_add(h, n) //h 为链表容器结构体 ngx_queue_t 的指针, n为另一个链表容器结构体 ngx_queue_t 的指针。合并链表,将 n 链表添加到 h 链表的末尾
ngx_queue_middle(h) //h 为链表容器结构体 ngx_queue_t 的指针。返回链表中心元素,即第 N/2 + 1 个
ngx_queue_sort(h, cmpfunc) //h 为链表容器结构体 ngx_queue_t 的指针,cmpfunc 是比较回调函数。使用插入排序对链表进行排序
ngx_queue_next(q) //q 为链表中某一个元素结构体的 ngx_queue_t 成员的指针。返回 q 元素的下一个元素。
ngx_queue_prev(q) //q 为链表中某一个元素结构体的 ngx_queue_t 成员的指针。返回 q 元素的上一个元素。
ngx_queue_data(q, type, link) //q 为链表中某一个元素结构体的 ngx_queue_t 成员的指针,type 是链表元素的结构体类型名称,link 是上面这个结构体中 ngx_queue_t 类型的成员名字。返回 q 元素所属结构体的地址
ngx_queue_insert_after(q, x) //q 为链表中某一个元素结构体的 ngx_queue_t 成员的指针,x 为插入元素结构体中 ngx_queue_t 成员的指针。 在 nginx 1.2 中 这个函数是 ngx_queue_insert_head 的一个别名

下面这段代码能够大致说明这个数据结构的一些用法:

#include
#include “ngx_queue.h”typedef struct{ //这个结构体实际上是我们真正使用的
char str
ngx_queue_t qEle //这里的 ngx_queue_t 变量是用来连接双向链表的
int num
}TestNodengx_int_t comp(const ngx_queue_t a, const ngx_queue_t b)
{//这个是排序用的比较函数
TestNode aNode = ngx_queue_data(a, TestNode, qEle); //首先得到 a 所在的结构体的指针
TestNode bNode = ngx_queue_data(b, TestNode, qEle);return aNode->num > bNode->num; //比较两个结构体中的 num 的大小
}int main(void)
{
int i = 0
TestNode node[5], tmp
ngx_queue_t queueContainer, *q
ngx_queue_init(queueContainer); //初始化双向链表for(; i<5 ++i)
{
node[i].num = i
}

//乱序插入双向链表
ngx_queue_insert_tail(queueContainer, &node[0].qEle);
ngx_queue_insert_head(queueContainer, &node[1].qEle);
ngx_queue_insert_tail(queueContainer, &node[2].qEle);
ngx_queue_insert_after(queueContainer, &node[3].qEle);
ngx_queue_insert_tail(queueContainer, &node[4].qEle);

//使用 comp 比较函数 对双向链表进行排序
ngx_queue_sort(queueContainer, comp);for(q = ngx_queue_head(&queueContainer);
q != ngx_queue_sentinel(queueContainer);
q = ngx_queue_next(q))
{//遍历整个链表
tmp = ngx_queue_data(q, TestNode, qEle);
printf(“%d\t, tmp->num);
}
printf(\n);
return 0
}

2. ngx_array_t 动态数组,类似于 STL 中的 vector。代码可以参考这里

typedef struct {
//数组首地址
void elts
//数组中已经使用的元素个数
ngx_uint_t nelts
//每个数组元素占用的内存大小
size_t size
//当前数组中能够容纳的元素个数的总大小
ngx_uint_t nalloc
//内存池对象
ngx_pool_t pool
} ngx_array_t

几个操作函数如下:

ngx_array_create(ngx_pool_t p, ngx_uint_t n, size_t size); //创建一个动态数组,并预分配 n 个大小为 size 的内存空间
ngx_array_init(ngx_array_t a, ngx_pool_t p, ngx_uint_t n, size_t size); //初始化 1 个已经存在的动态数组,并预分配 n 个大小为 size 的内存空间
ngx_array_destroy(ngx_array_t a) //销毁已分配的数组元素空间和 ngx_array_t 动态数组对象
ngx_array_push(ngx_array_t a) //向当前动态数组中添加 1 个元素,返回的是这个新添元素的地址。如果动态数组已经达到容量上限,会导致自动扩容
ngx_array_push_n(ngx_array_t a, ngx_uint_t n) //向当前动态数组中添加 n 个元素,返回的是新添加的这批元素中第一个元素的地址。如果动态数组已经达到容量上限,会导致自动扩容

其中有关扩容的情况,如果是添加 1 个元素的话,那么该内存池有空间就直接添加,没空间的话,会导致先扩成原来的2倍。如果是添加 n 个元素的话,如果内存池空间够的话,直接分配 n 个元素的内存,如果不够的话,分配 2X 的空间,其中 X = (n>=a->nalloc)?n:a->nalloc. 也就是 n 和动态数组中已分配空间的较大值

3. ngx_list_t 链表,定义如下:

typedef struct ngx_list_part_s ngx_list_part_t

//链表中每个元素的结构
struct ngx_list_part_s {
//指向数组的起始地址
void elts
//表示数组中已经使用了多少个元素。必须小于 链表中的 nalloc
ngx_uint_t nelts
//下一个元素的 ngx_list_pars_s 地址
ngx_list_part_t next
};

typedef struct {
//最后一个已使用元素的地址
ngx_list_part_t last
//第一个元素
ngx_list_part_t part
//每个元素的数组中存储的每个值的字节数不能超过 size
size_t size
//每个 ngx_list_part_s 数组的容量,即最多可存储多少个数据
ngx_uint_t nalloc
//内存池对象
ngx_pool_t pool
} ngx_list_t


针对 ngx_list_t 的几个操作函数为 ngx_list_init(ngx_list_t list, ngx_pool_t pool, ngx_uint_tn, size_t size),这个函数会对 list 对应的链表进行初始化。

创建链表函数 ngx_list_create(ngx_pool_t *pool, ngx_uint_tn, size_t size) 这个函数首先创建一个 ngx_list_t 的对象 list,然后调用 ngx_list_init 进行初始化

往链表中添加元素的函数 ngx_list_push(ngx_list_t *l) 这个函数会添加一个新元素,返回的是新元素的首地址。如果失败,返回 NULL。首先检测链表的最后一个数组是否已满,没满就直接返回,已满的话就分配新的数组,添加到链表中,然后返回相应的地址。

2012-03-29
Github之旅一---创建自己的项目

首先在github.com上面创建一个免费的账号[可能需要翻墙],上面的账号分为两种,收费和免费,免费的有30M空间貌似。然后来到github的首页,在右侧看到“Your Repositories”,点击这里的New Repositories,然后就会跳到另外一个页面,输入你的项目名字和项目描述,URL那一栏自己取个名字就行了,以后访问你的项目会用到,填写完成之后,就点击”Create repositories”就行,跳到的页面会教你怎么做,都是写基本的git命令,照着做就行了,创建的README文件貌似不能为空,为空的话提交不能成功。上传之后,你可以在相应的URL看到你的项目了。最基本的创建自己的项目就OK了,接下来可以clone别人的项目来看看了

2012-03-29
github连接出现port 22:Bad file number问题

一直想试试用github,可以尝试了很久未果,在windows下和Fedora下都出现了port 22错误,一直以为是GFW的问题,想改host什么的,也找不到host,每次上github都是翻墙出去的,但是ping却能ping通。今天终于找到一篇可以解决这个问题的文章了,转载过来存个档

原文地址:http://rangercyh.blog.51cto.com/1444712/749490

前段时间我试用了时下非常酷的代码托管站点github以及gitorious。感触颇多。

首先是我以前一直想要有这种代码托管的网站,可是直到前段时间我才知道已经有了。o(︶︿︶)o 唉,跟不上潮流啊!

其次是这些网站做得比我想要的要漂亮的多了,我最喜欢简洁明了,而这两个网站也正是如此,甚至比我想要的还要好。这里也推荐给大家使用。

顺带说明一下github是闭源的,gitorious是开源的。不管怎么样,都不错,反正我用免费的。。。。如何在局域网自己架我就不说了,自己去网站上看吧。给出它们的链接:

github:https://github.com/

gitorious:https://gitorious.org/

当然,这两个网站都是使用git版本控制工具来的,虽然我以前只使用过css,但用过git之后我觉得我以前弱爆了。。。好吧。不谈这个。。免得得罪svn和cvs党。。

不过在使用github时我遇到了点小问题。在官网里也没有说明,其实非常简单,但我想肯定会有跟我一样的菜鸟不知所措的。所以我列一下我遇到的问题。

按照github官网上Set Up Git来操作,当执行到ssh连接时,会出现我下面的问题:

“Bad file number这是个什么玩意”我当时就是这么想的。

后来我仔细研究了官网上的帮助文档,发现它提到过一个config配置文件,我当然没有创建过这个文件。于是我就google了一下,参考前人写了一个config放到rsa相同目录下:

但是我重新连接时仍然不行:

于是我怒了!!!静下来思考觉得可能是这个port 22的问题,因为我ping了一下github.com发现是可以ping通的。然后我用IceSword查看了一下端口,发现22端口是被禁掉的。原来这个22端口是默认的ssh连接端口,不过貌似这个服务有很多弱点,所以系统默认不开放。。当然,开放它是一个策略,不过既然有弱点,我还是不要去动它,那么我使用另一个端口去连接ssh就行了嘛~~~呵呵!这个新的端口不能和现在程序使用的服务端口重复,而且要确保开放,看到github的网址我想到了443端口,它是用来支持https服务的。这个端口默认开放,哈哈!!

下面我只需要修改下配置文件就可以了,如下:

注意Hostname修改成了ssh.github.com。接下来连接就发现一切OK了~~哈哈!

输入yes之后在rsa的路径下会生成一个known_hosts的文件

打开看了就发现其实就是一个类似cookie的文件罢了:

当这个文件存在时以后就不用费劲的输入yes了,直接就连接上了:

怎么样!我是不是很有才。。。。好吧。我又2了。。。