王晓华前辈在“一个队列引发的惨案”一节中,说到用循环队列处理固定长度缓冲区问题,小僧读到这里,深深一惊,不禁感叹算法与数据结构结合的巧妙!!!也许是因为小僧是菜鸟,也许是因为小僧少见多怪,也许是因为小僧……反正不论如何,小僧始终觉得这个想法很好,值得借鉴。
结合小僧目前的见识,粗略对王前辈提到的内容进行了一点点加工,话休繁絮,且听小僧一一道来。
对于网络设备而言(小僧目前仅仅接触过UAG设备和负载均衡设备,而且主攻负载均衡设备),系统日志肯定是必须的,而且有一定规格,随着设备的运行,系统日志肯定会满规格,并且覆盖旧日志的情况非常普遍,既然如此,单单就系统日志这一类情况来看,使用链表这种随添随加的数据结构,效率未必高。因为在一开始,未满规格时,每来一条日志,都要现场开辟一段空间来作为节点,而开辟空间的过程是要有一定开销的,因为设备已经启动并有了流量;就算满规格了,不用再开辟空间了,那么想一想,日志嘛,暂时就是固定长度的字符串而已,并没有让这些节点携带某些特殊标记,既然如此,在小僧看来,辛辛苦苦开辟的空间,仅仅用来存个字符串,未免太奢侈。
综上所述,对于该问题的解决,小僧打算使用二维数组log_buf[x][y]。x表示系统日志规格,即总共可以有多少条日志,y表示一条日志的长度。这个二维数组是全局的,在设备启动时就分配好了空间,所以不用担心有流量时/跑业务时临时开辟空间来存日志。另外,对头、尾节点的指向,此时因为采用了数组,所以可以直接用下标,而不用一个结构体指针来专门指向,个人认为,对于菜鸟来讲,这样还是更好一些啊 。
好了,背景介绍完毕,请看代码
#include <stdio.h>
#include <string.h>
#define LOG_NORM 10 /* 日志规格:最多可以有多少条日志 */
#define LOG_LEN 20 /* 单条日志长度 */
#define LOG_TEST_NUM 20 /* 测试用日志总条数 */
#define CONSISTENCY_PROC(tmp) (tmp = (tmp + 1) % LOG_NORM) /* 一致性处理 */
int head = 0, tail = 0;
char log_buff[LOG_NORM][LOG_LEN];
char *log_example[LOG_TEST_NUM] = {
"0 log_test",
"1 log_test",
"2 log_test",
"3 log_test",
"4 log_test",
"5 log_test",
"6 log_test",
"7 log_test",
"8 log_test",
"9 log_test",
"10 log_test",
"11 log_test",
"12 log_test",
"13 log_test",
"14 log_test",
"15 log_test",
"16 log_test",
"17 log_test",
"18 log_test",
"19 log_test"
};
void print_current_log_list(int head, int tail)
{
int tmp = head;
printf("start, head = %d, tail = %d\n", head, tail);
while (1)
{
printf("log_buff[%d]: %s\n", tmp, log_buff[tmp]);
CONSISTENCY_PROC(tmp);
if (tmp == tail)
{
printf("log_buff[%d]: %s\n", tmp, log_buff[tmp]);
printf("end\n");
break;
}
}
}
int main(int argc, char *argv[])
{
int i;
memset(log_buff, 0, sizeof(log_buff));
for (i = 0; i < LOG_TEST_NUM; i++)
{
if (0 == i)
{
memcpy(log_buff[head], log_example[i], strlen(log_example[i]));
printf("start, head = %d, tail = %d\n", head, tail);
printf("log_buff[%d]: %s\n", head, log_buff[head]);
printf("end\n");
continue;
}
CONSISTENCY_PROC(tail);
if (tail == head)
{
CONSISTENCY_PROC(head);
}
memcpy(log_buff[tail], log_example[i], strlen(log_example[i]));
print_current_log_list(head, tail);
}
return;
}
因为这仅仅是一个示例代码而已,所以小僧只是用了一个指针数组来指向20条示例日志,而日志规格是10条;小僧使用一个函数来专门遍历每次有日志“插入”时当前存储的所有日志。如果施主感兴趣,小僧建议还是运行一下该程序较好,可以更直观的感受用下标表示头、尾而不是使用指针指向来表示头尾的那种畅快淋漓!!!
在小僧看来,这么多行代码,真正有价值就是下面这段
CONSISTENCY_PROC(tail);
if (tail == head)
{
CONSISTENCY_PROC(head);
}
当转了一圈,发现蛇咬了尾巴时,那么尾不动,将下一条日志的下标赋值给头即可(这编辑器我也是醉了,排版这么难搞)。