在nginx源码中,用了大量的数据结构,现对在nginx源码中对数据结构常用的一种方法进行一些总结。
nginx的事件模块的数据结构中定义了两个变量:timer,queue;结构体如下:
struct ngx_event_s {
void *data;
……
ngx_rbtree_node_t timer;
ngx_queue_t queue;
……
};
其实timer变量是为了将ngx_event_s结构体能挂在一个红黑树结构上去;而queue变量则是为了将ngx_event_s结构体能挂在一个队列结构上去;后续借助offsetof()函数指向所查找结构体的初始位置。
下面举一个比较容易理解的案例,对这种思想进一步加深理解。假如有一个学生的数据结构如下:
typedef struct {
int ID;
char name[10];
int age;
} student;
如果想将所有学生的信息存储在一个双向链表里,按照nginx处理数据结构的思想会出现如下结构体:
typedef struct {
my_queue *pre;
my_queue *next;
} my_queue;
typedef struct {
int ID;
char name[10];
int age;
my_queue queue;
} student;
每个student结构体中的queue变量中的pre指针指向前一个student结构体中的queue变量; next指针指向后一个student结构体中的queue变量;结构图如下:
遍历队列结构时,可通过offsetof()函数计算出queue变量在结构体的相对偏移量值,然后计算出Student 结构体的相对位置。
具体案例代码如下:
/*******************************
* data: 2021-10-28
* author: lijd
*******************************/
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
typedef struct tMy_queue my_queue;
typedef struct {
my_queue *pre;
my_queue *next;
} tMy_queue, *pMy_queue;
typedef struct {
int Id;
char name[10];
int age;
tMy_queue queue;
} tStudent, *pStudent;
// 定义队列头指针
static pMy_queue ptrQueueHead = NULL;
// 队列头上加一个元素
void add_my_queue(pMy_queue ptrNode)
{
ptrNode->next = ptrQueueHead;
ptrNode->pre = NULL;
if(ptrQueueHead != NULL)
{
ptrQueueHead->pre = ptrNode;
}
ptrQueueHead = ptrNode;
}
// 初始化队列结构
void init_func()
{
int i = 0;
pStudent ptrStu = NULL;
for(i = 0; i < 10; i++)
{
ptrStu = (pStudent)malloc(sizeof(tStudent));
memset(ptrStu, 0, sizeof(tStudent));
ptrStu->Id = i;
snprintf(ptrStu->name, 10, "Stu%d", i);
ptrStu->age = 18 + i;
add_my_queue(&ptrStu->queue);
}
}
pStudent find_func(int studentId)
{
pMy_queue ptr_queue = ptrQueueHead;
pStudent ptr_student = NULL;
while(ptr_queue){
// 获取queue结构所属的student结构
ptr_student = (pStudent)((char *)ptr_queue - offsetof(tStudent, queue));
if(ptr_student->Id == studentId)
{
return ptr_student;
}
ptr_queue = ptr_queue->next;
}
return NULL;
}
void exit_func()
{
pStudent ptrStu = NULL;
pMy_queue ptrQue = NULL;
while(ptrQueueHead)
{
ptrQue = ptrQueueHead;
if(ptrQueueHead->next)
{
((pMy_queue)(ptrQueueHead->next))->pre = NULL;
}
ptrQueueHead = ptrQueueHead->next;
// 获取queue结构所属的student结构
ptrStu = (pStudent)((char *)ptrQue - offsetof(tStudent, queue));
// 释放资源
free((void *)ptrStu);
}
}
int main(int argc, char *argv[])
{
pStudent ptrStu = NULL;
init_func();
// 查找Id为6的学生
ptrStu = find_func(6);
if(ptrStu != NULL)
{
printf("Id:%d, name:%s, age:%d\n", ptrStu->Id, ptrStu->name, ptrStu->age);
}
exit_func();
return 0;
}
执行结果如下: