zoukankan      html  css  js  c++  java
  • Linux queue.h之TAILQ队列分析

    这两天想看看memcached的实现,所以先学习了libevent,使用起来还是比较简单的,其实是对select/poll/kqueue等的封装,学习libevent过程中又遇到了linux下队列的使用,简单分析如下,权当做记录: 

    libevent中的例子中使用的是FreeBSD下的queue.h,在linux的/usr/include/sys/queue.h也有该头文件,但是是一个缩减版本,而且没有看到queue 的access method,不知道是不是跟我们的linux服务器版本有关,没办法google了一下,找到了FreeBSD 下queue.h的定义,我们看一下tail queue的定义 

    C代码  收藏代码
    1. #define TAILQ_HEAD(name, type)                
    2. struct name {                         
    3.     struct type *tqh_first; /* first element */   
    4.     struct type **tqh_last; /* addr of last next element */  
    5. }  
    6.   
    7. #define TAILQ_ENTRY(type)                     
    8. struct {                              
    9.     struct type *tqe_next;  /* next element */        
    10.     struct type **tqe_prev;/* addr of previous next element*/   
    11. }                                                                  
    12.   
    13. #define TAILQ_INIT(head) do {                 
    14.     (head)->tqh_first = NULL;                  
    15.     (head)->tqh_last = &(head)->tqh_first;          
    16. while (0)  
    17.   
    18. #define TAILQ_INSERT_TAIL(head, elm, field) do {          
    19.     (elm)->field.tqe_next = NULL;              
    20.     (elm)->field.tqe_prev = (head)->tqh_last;       
    21.     *(head)->tqh_last = (elm);                 
    22.     (head)->tqh_last = &(elm)->field.tqe_next;          
    23. while (0)  
    24.   
    25. #define TAILQ_INSERT_BEFORE(listelm, elm, field) do {         
    26.     (elm)->field.tqe_prev = (listelm)->field.tqe_prev;      
    27.     (elm)->field.tqe_next = (listelm);             
    28.     *(listelm)->field.tqe_prev = (elm);            
    29.     (listelm)->field.tqe_prev = &(elm)->field.tqe_next;     
    30. while (0)  
    31. #define TAILQ_FIRST(head)       ((head)->tqh_first)  
    32.   
    33. #define TAILQ_NEXT(elm, field)      ((elm)->field.tqe_next)  
    34. ....  


    我们就先分析上面的这些定义,先看个应用的例子 

    C代码  收藏代码
    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3. #include "queue.h"  
    4.   
    5. struct QUEUE_ITEM{  
    6.     int value;  
    7.     TAILQ_ENTRY(QUEUE_ITEM) entries;  
    8. };  
    9. TAILQ_HEAD(,QUEUE_ITEM) queue_head;  
    10. int main(int argc,char **argv){  
    11.     struct QUEUE_ITEM *item;  
    12.     struct QUEUE_ITEM *tmp_item;  
    13.   
    14.     TAILQ_INIT(&queue_head);  
    15.     int i=0;  
    16.     for(i=5;i<10;i+=2){  
    17.         item=malloc(sizeof(item));  
    18.         item->value=i;  
    19.         TAILQ_INSERT_TAIL(&queue_head, item, entries);  
    20.     }  
    21.       
    22.     struct QUEUE_ITEM *ins_item;  
    23.     ins_item=malloc(sizeof(ins_item));  
    24.   
    25.     ins_item->value=100;  
    26.     TAILQ_INSERT_BEFORE(item,ins_item,entries);  
    27.   
    28.   
    29.     tmp_item=TAILQ_FIRST(&queue_head);  
    30.     printf("first element is %d ",tmp_item->value);  
    31.   
    32.     tmp_item=TAILQ_NEXT(tmp_item,entries);  
    33.     printf("next element is %d ",tmp_item->value);  
    34.   
    35.     tmp_item=TAILQ_NEXT(tmp_item,entries);  
    36.     printf("next element is %d ",tmp_item->value);  
    37.   
    38.     tmp_item=TAILQ_NEXT(tmp_item,entries);  
    39.     printf("next element is %d ",tmp_item->value);  
    40.   
    41. }  


    结果: 
    Java代码  收藏代码
    1. first element is 5  
    2. next element is 7  
    3. next element is 100  
    4. next element is 9  


    分析: 
    QUEUE_ITEM 是我们定义的存放在队列里的东东,简单起见只包括一个int值 
    TAILQ_ENTRY(QUEUE_ITEM) entries 主要是存放下一个对象和前一个对象的指针,具体见 header 
      
    根据头文件进行宏替换后,实际我们声明的是这样的结构: 

    C代码  收藏代码
    1.  struct QUEUE_ITEM{  
    2.     int value;  
    3.         struct {              
    4.             struct QUEUE_ITEM *tqe_next;      
    5.         struct QUEUE_ITEM **tqe_prev;  
    6.         }entries;  
    7. };  
    8.    


    TAILQ_HEAD(,QUEUE_ITEM) queue_head; 实际是 
    C代码  收藏代码
    1. struct {                  
    2.     struct QUEUE_ITEM *tqh_first;     
    3.     struct QUEUE_ITEM **tqh_last;     
    4. }queue_head;  


    接着我们定义了QUEUE_ITEM的两个指针变量item和tmp_item 

    TAILQ_INIT(&queue_head); 相当于是 
    C代码  收藏代码
    1. do {  
    2.     (&queue_head)->tqh_first = NULL;               
    3.     (&queue_head)->tqh_last = &(&queue_head)->tqh_first;        
    4. while (0);  

    head的初始化如 下图1 

    接着我们通过循环分配了几个元素,并赋值 
    C代码  收藏代码
    1. TAILQ_INSERT_TAIL(&queue_head, item, entries); 相当于执行  
    2.   
    3. do {  
    4.     (item)->entries.tqe_next = NULL;   
    5.     (item)->entries.tqe_prev = (&queue_head)->tqh_last;             
    6.     *(&queue_head)->tqh_last = (item);                     
    7.     (&queue_head)->tqh_last = &(item)->entries.tqe_next;            
    8. while (0);  

    也就是我们的循环执行下面代码段,结果分析见图2,3 
    C代码  收藏代码
    1. for(i=5;i<10;i+=2){  
    2.     item=malloc(sizeof(item));  
    3.     item->value=i;  
    4.     do {  
    5.         (item)->entries.tqe_next = NULL;  
    6.         //首次执行相当于item->entries.tqe_prev=&(&queue_head)->tqh_first  
    7.         //以后执行相当于是(item)->entries.tqe_prev=&(前一个item)->entries.tqe_next;  
    8.         (item)->entries.tqe_prev = (&queue_head)->tqh_last;  
    9.         //首次执行相当于(&queue_head)->tqh_first=item  
    10.         //以后执行相当于是(前一个item)->entries.tqe_next=当前item  
    11.         *(&queue_head)->tqh_last = (item);  
    12.         (&queue_head)->tqh_last = &(item)->entries.tqe_next;  
    13.     } while (0);  
    14. }  

    C代码  收藏代码
    1. 最终建立的链表结构如图,下面看一下insert操作,经过宏替换后代码如下  
    2.   
    3. struct QUEUE_ITEM *ins_item;  
    4. ins_item=malloc(sizeof(ins_item));  
    5. ins_item->value=100;  
    6.   
    7. do {  
    8.     (ins_item)->entries.tqe_prev = (item)->entries.tqe_prev;  
    9.     (ins_item)->entries.tqe_next = (item);  
    10.     //这句话体现了TAILQ的特色,tqe_prev是前一个元素的下个元素地址,  
    11.     //所以正好应该是当前插入item的地址  
    12.     *(item)->entries.tqe_prev = (ins_item);  
    13.     (item)->entries.tqe_prev = &(ins_item)->entries.tqe_next;  
    14. while (0);  


     

    总结:TAILQ的最大特点就是每个entry的二级指针tqe_prev其存放的是前一个元素的下个元素地址,呵呵,听起来都很拗口 
    我现在就是不知道为什么linux的queue.h只有建立tailq的宏定义而缺少所有的access method,初涉linux c编程,请大家指教 

    附经过宏替换后的所有代码 
    C代码  收藏代码
    1. #include "stdio.h"  
    2. #include "stdlib.h"  
    3. struct QUEUE_ITEM{  
    4.     int value;  
    5.     struct {  
    6.         struct QUEUE_ITEM *tqe_next;  
    7.         struct QUEUE_ITEM **tqe_prev;  
    8.     }entries;  
    9. };  
    10. struct {  
    11.     struct QUEUE_ITEM *tqh_first;  
    12.     struct QUEUE_ITEM **tqh_last;  
    13. }queue_head;  
    14.   
    15. int main(int argc,char **argv){  
    16.     struct QUEUE_ITEM *item;  
    17.     struct QUEUE_ITEM *tmp_item;  
    18.   
    19.     do {  
    20.         (&queue_head)->tqh_first = NULL;  
    21.         (&queue_head)->tqh_last = &(&queue_head)->tqh_first;  
    22.     } while (0);  
    23.   
    24.     int i=0;  
    25.     for(i=5;i<10;i+=2){  
    26.         item=malloc(sizeof(item));  
    27.         item->value=i;  
    28.         do {  
    29.             (item)->entries.tqe_next = NULL;  
    30.             //首次执行相当于item->entries.tqe_prev=&(&queue_head)->tqh_first  
    31.             //以后执行相当于是(item)->entries.tqe_prev=&(前一个item)->entries.tqe_next;  
    32.             (item)->entries.tqe_prev = (&queue_head)->tqh_last;  
    33.             //首次执行相当于(&queue_head)->tqh_first=item  
    34.             //以后执行相当于是(前一个item)->entries.tqe_next=当前item  
    35.             *(&queue_head)->tqh_last = (item);  
    36.             (&queue_head)->tqh_last = &(item)->entries.tqe_next;  
    37.         } while (0);  
    38.     }  
    39.   
    40.     struct QUEUE_ITEM *ins_item;  
    41.     ins_item=malloc(sizeof(ins_item));  
    42.   
    43.     ins_item->value=100;  
    44.     do {  
    45.         (ins_item)->entries.tqe_prev = (item)->entries.tqe_prev;  
    46.         (ins_item)->entries.tqe_next = (item);  
    47.         *(item)->entries.tqe_prev = (ins_item);  
    48.         (item)->entries.tqe_prev = &(ins_item)->entries.tqe_next;  
    49.     } while (0);  
    50.   
    51.     tmp_item=((&queue_head)->tqh_first);  
    52.     printf("first element is %d ",tmp_item->value);  
    53.   
    54.     tmp_item=((tmp_item)->entries.tqe_next);  
    55.     printf("next element is %d ",tmp_item->value);  
    56.   
    57.     tmp_item=((tmp_item)->entries.tqe_next);  
    58.     printf("next element is %d ",tmp_item->value);  
    59.   
    60.     tmp_item=((tmp_item)->entries.tqe_next);  
    61.     printf("next element is %d ",tmp_item->value);  
    62.   
    63. }  
  • 相关阅读:
    initializer_list形参
    前置递增运算符和后置递增运算符的区别
    基本的TCP socket API
    C++ find()函数
    python-对目录下的文件按时间排序
    js常用方法
    selenium中的对文本进行全选,复制,粘贴,剪切和删除的操作
    python 打包exe 命令及去除 cmd框
    mysql命令行修改密码
    html文件转换成excel
  • 原文地址:https://www.cnblogs.com/renweihang/p/9230647.html
Copyright © 2011-2022 走看看