• nginx源码分析--双端列表


    1.基本数据结构

    1. struct ngx_queue_s {
    2. ngx_queue_t *prev;
    3. ngx_queue_t *next;
    4. };

    结构成员:

    ngx_queue_t *prev;前驱指针

    ngx_queue_t *next;后继指针

    2.操作函数--头结点

    2.1基本函数

    1. define ngx_queue_init(q) \
    2. (q)->prev = q; \
    3. (q)->next = q
    4. #define ngx_queue_empty(h) \
    5. (h == (h)->prev)
    6. #define ngx_queue_sentinel(h) \
    7. (h)
    8. #define ngx_queue_head(h) \
    9. (h)->next
    10. #define ngx_queue_last(h) \
    11. (h)->prev

    1.初始化头结点 将两个指针指向自身

    2.检测前驱结点

    3返回头结点(哨兵:)

    4.头结点和尾结点

     

    2.2添加和删除

    #define ngx_queue_insert_head(h, x)  

    1. #define ngx_queue_insert_head(h, x)  
    2. (x)->next = (h)->next; \
    3. (x)->next->prev = x; \
    4. (x)->prev = h; \
    5. (h)->next = x

    采用一个哨兵的方法

    h作为一个哨兵,x成为一个头结点

    #define ngx_queue_insert_tail(h, x)

    1. #define ngx_queue_insert_tail(h, x) \
    2. (x)->prev = (h)->prev; \
    3. (x)->prev->next = x; \
    4. (x)->next = h; \
    5. (h)->prev = x

        删除

    1. #define ngx_queue_remove(x)
    2. (x)->next->prev = (x)->prev; \
    3. (x)->prev->next = (x)->next; \
    4. (x)->prev = NULL; \
    5. (x)->next = NULL

    3.合并和分离

    1. #define ngx_queue_split(h, q, n)
    2. (n)->prev = (h)->prev; \
    3. (n)->prev->next = n; \
    4. (n)->next = q; \
    5. (h)->prev = (q)->prev; \
    6. (h)->prev->next = h; \
    7. (q)->prev = n;
    8. #define ngx_queue_add(h, n) \
    9. (h)->prev->next = (n)->next; \
    10. (n)->next->prev = (h)->prev; \
    11. (h)->prev = (n)->prev; \
    12. (h)->prev->next = h;

    1.分裂队列 h是头 q是分裂节点的界限 n为分裂完的新节点

    在这里插入图片描述

     2.合并队列 n本身的节点会抛弃

    在这里插入图片描述

     3.操作函数--数据节点

    数据节点本质上与头节点并无区别,所以很多操作代码是相同的数据节点本质上与头节点并无区别,所以很多操作代码是相同的
    添加和删除
    1. #define ngx_queue_remove(x) \
    2. (x)->next->prev = (x)->prev; \
    3. (x)->prev->next = (x)->next
    4. #define ngx_queue_insert_after ngx_queue_insert_head

    注意:

    函数宏ngx_queue_remove()可以“删除” 当前节点,实际上它只是调整了节点的指针,
    把节点从队列里摘除,并没有真正从内存里删除数据:
    寻找:
    1. #define ngx_queue_next(q) \
    2. (q)->next
    3. #define ngx_queue_prev(q) \
    4. (q)->prev

    ngx_queue_t *
    ngx_queue_middle(ngx_queue_t *queue)
    1. gx_queue_t *
    2. ngx_queue_middle(ngx_queue_t *queue)
    3. {
    4. ngx_queue_t *middle, *next;
    5. middle = ngx_queue_head(queue);
    6. if (middle == ngx_queue_last(queue)) {
    7. return middle;
    8. }
    9. next = ngx_queue_head(queue);
    10. for ( ;; ) {
    11. middle = ngx_queue_next(middle);
    12. next = ngx_queue_next(next);
    13. if (next == ngx_queue_last(queue)) {
    14. return middle;
    15. }
    16. next = ngx_queue_next(next);
    17. if (next == ngx_queue_last(queue)) {
    18. return middle;
    19. }
    20. }
    21. }
    1. void queue_sort(ngx_queue_t *queue,
    2. ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *))
    3. {
    4. ngx_queue_t *q, *prev, *next;
    5. q = ngx_queue_head(queue);
    6. if (q == ngx_queue_last(queue)) {
    7. return;
    8. }
    9. for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = next) {
    10. prev = ngx_queue_prev(q);
    11. next = ngx_queue_next(q);
    12. ngx_queue_remove(q);
    13. do {
    14. if (cmp(prev, q) <= 0) {
    15. break;
    16. }
    17. prev = ngx_queue_prev(prev);
    18. } while (prev != ngx_queue_sentinel(queue));
    19. ngx_queue_insert_after(prev, q);
    20. }
    21. }

    全是暴力算法 效率比较低 不做过多解释

    4 test

    引出一个知识:

    双端队列是在两端都可以插入或删除元素的数据结构,在Nginx里它被实现为双向循环链表ngx_queue_t,类似标准容器std : : list。

    借用Boost程序库的概念,前面的ngx_array_t和ngx_list_t属于非侵入式容器,元素无须改动即可加入容器,而 ngx_queue_t 则不同,它是侵入式容器,必须把 ngx_queue_t作为元素的一个成员,然后才能放入队列,与boost.intrusive库很接近。

    1. struct Xinfo
    2. {
    3. int x=0;
    4. ngx_queue_t queue;
    5. }


    例子

    1. #include #include
    2. #include
    3. #include
    4. #include "ngx_queue.h"
    5. //数据定义 节点必须有ngx_queue_t
    6. typedef struct student
    7. {
    8. int id;
    9. int age;
    10. int weight;
    11. ngx_queue_t qnode;
    12. }student;
    13. //排序比较函数
    14. ngx_int_t stu_cmp(const ngx_queue_t *n1, const ngx_queue_t *n2)
    15. {
    16. //拿到数据
    17. struct student *s1 = ngx_queue_data(n1, student, qnode);
    18. struct student *s2 = ngx_queue_data(n2, student, qnode);
    19. return s1->id - s2->id;
    20. }
    21. //遍历
    22. void stu_foreach(const ngx_queue_t *queue)
    23. {
    24. ngx_queue_t *q = ngx_queue_head(queue);
    25. struct student *s = ngx_queue_data(q, student, qnode);
    26. if (q == ngx_queue_last(queue)) {
    27. return;
    28. }
    29. printf("id = %d\n",s->id);
    30. for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = ngx_queue_next(q)) {
    31. s = ngx_queue_data(q, student, qnode);
    32. printf("id = %d\n",s->id);
    33. }
    34. }
    35. int main(int argc,char *argv[])
    36. {
    37. //初始化队列
    38. ngx_queue_t *q = (ngx_queue_t *)calloc(1,sizeof(ngx_queue_t));
    39. ngx_queue_init(q);
    40. //初始化数据
    41. struct student st1 = {1,11,111};
    42. struct student st2 = {2,12,112};
    43. struct student st3 = {3,13,113};
    44. struct student st4 = {4,14,114};
    45. struct student st5 = {5,15,115};
    46. //头插法 将qnode插入
    47. ngx_queue_insert_head(q,&st2.qnode);
    48. ngx_queue_insert_head(q,&st4.qnode);
    49. ngx_queue_insert_head(q,&st1.qnode);
    50. ngx_queue_insert_head(q,&st5.qnode);
    51. ngx_queue_insert_head(q,&st3.qnode);
    52. //排序
    53. printf("================sort=============\n");
    54. stu_foreach(q);
    55. printf("\n");
    56. ngx_queue_sort(q,stu_cmp);
    57. stu_foreach(q);
    58. //找中间值
    59. printf("================middle=============\n");
    60. ngx_queue_t *mid = ngx_queue_middle(q);
    61. student *s = ngx_queue_data(mid, student, qnode);
    62. printf("id:%d\n",s->id);
    63. //分裂
    64. printf("================split=============\n");
    65. ngx_queue_t *n = (ngx_queue_t *)calloc(1,sizeof(ngx_queue_t));
    66. ngx_queue_init(n);
    67. ngx_queue_split(q,mid,n);
    68. printf("=old queue=\n");
    69. stu_foreach(q);
    70. printf("=new queue=\n");
    71. stu_foreach(n);
    72. //合并
    73. printf("================add=============\n");
    74. ngx_queue_add(q,n);
    75. stu_foreach(q);
    76. free(n);
    77. free(q);
    78. return 0;
    79. }
    输出
    在这里插入图片描述

     

  • 相关阅读:
    SpringBoot中bean绑定
    Leetcode543. 二叉树的直径
    算法训练——单调栈专题
    Emmet详解
    Python requests爬虫豆瓣图片返回数据为空。
    【jvm】虚拟机栈之局部变量表
    java计算机毕业设计商店管理系统源码+系统+mysql数据库+lw文档+部署
    RabbitMQ初步到精通-第六章-RabbitMQ之死信队列
    【博学谷学习记录】超强总结,用心分享|Shell字符串
    postman导入请求到jmeter进行简单压测,开发同学一学就会
  • 原文地址:https://blog.csdn.net/qq_62309585/article/details/128050504