shttpd是一個開源的跨平臺的輕量級web服務框架,源碼下載地址。這裏是使用的1.42版本。
shttpd中雙向鏈表的功能和內核雙向鏈表一致,都是定義一個存放前後指針的節點,將節點存放到某個結構體中,該結構體中的節點關聯起來,然後就可以通過鏈表的方式操作該結構體對象了。
節點結構:
struct llhead 爲內核雙向鏈表的節點,定義如下:
struct llhead {
struct llhead *prev;
struct llhead *next;
};
鏈表的定義:
#define LL_HEAD(H) struct llhead H = { &H, &H }
//例如定義一個靜態的llhead節點
static LL_HEAD(listeners);
鏈表的初始化:
鏈表初始化,用來將節點的前向、後向指針指向自身。定義及使用如下:
#define LL_INIT(N) ((N)->next = (N)->prev = (N))
struct llhead listeners;
LL_INIT(&listeners);
添加節點:
向後添加節點
#define LL_ADD(H, N) \
do { \
((H)->next)->prev = (N); \
(N)->next = ((H)->next); \
(N)->prev = (H); \
(H)->next = (N); \
} while (0)
//使用:
struct sockInfo{
int sockfd;
int family;
int port;
in_addr addr;
LL_HEAD(listeners);
};
// 將2個sockInfo用鏈表連接起來
struct sockInfo si1;
struct sockInfo si2;
LL_INIT(&si1.listeners);
LL_INIT(&si2.listeners);
LL_ADD(&si1.listeners, &si2.listeners);
刪除節點:
將某個節點從鏈表中刪除(讓該鏈表的下一節點與上一節點相互指向對方),並再次初始化該節點
#define LL_DEL(N) \
do { \
((N)->next)->prev = ((N)->prev); \
((N)->prev)->next = ((N)->next); \
LL_INIT(N); \
} while (0)
遍歷節點:
LL_FOREACH通常用於獲取節點,而不能用到刪除節點的場景
LL_FOREACH_SAFE通常刪除節點的場景
此實現貌似不能遍歷H自身~
#define LL_FOREACH(H,N) for (N = (H)->next; N != (H); N = (N)->next)
#define LL_FOREACH_SAFE(H,N,T) \
for (N = (H)->next, T = (N)->next; N != (H);N = (T), T = (N)->next)
// 使用for_each遍歷struct llhead *head
struct llhead *lp, *tmp;
LL_FOREACH_SAFE(head, lp, tmp) {
LL_DEL(lp);
}
鏈表判空
如果鏈表指向自身,則爲空
#define LL_EMPTY(N) ((N)->next == (N))
向鏈表末尾添加節點
#define LL_TAIL(H, N) \
do { \
((H)->prev)->next = (N); \
(N)->prev = ((H)->prev); \
(N)->next = (H); \
(H)->prev = (N); \
} while (0)
獲取結構體變量指針:
根據”結構體(T)變量中的域成員變量N的指針來獲取指向整個結構體變量的指針。
#define LL_ENTRY(P,T,N) ((T *)((char *)(P) - offsetof(T, N)))
// 示例
struct worker {
struct llhead link;
struct llhead connections; /* List of connections */
};
struct llhead *lp = &pworker->link;
struct worker *worker = LL_ENTRY(lp, struct worker, link);
說明:
offsetof用來獲取T類型中N的相對偏移。假設link相對於worker的偏移爲offset,當獲取到link內存中的地址link_ptr時,就可以通過可以獲取到pworker所指向的內存。此時pworker = link_prt - offset;也就是LL_ENTRY的實現。
offsetof實現:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
假設存在結構體struct TYPE如下
struct TYPE{
T t1;
//…
P p1;
};
當定義變量:struct TYPE tp1;其地址爲&tp1,其成員變量t1的地址爲&(tp1.t1)
則如下代碼可以取到p1在內存中的地址以及相對於tp1的位移
struct TYPE tp1;
struct TYPE *ptp = &tp1;
int addr_t1 = &ptp->t1;// t1在內存中的地址
int addr_p1 = &ptp->p1;// p1內存中的地址
// p1相對於tp的內存偏移爲
size_t offset = (char *)&tp1->p1 - (char *)tp1;
當ptp指向null指針時,offsetof =(size_t) ((char *)&0->t1 ;
//list.h
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#ifndef LLIST_HEADER_INCLUDED
#define LLIST_HEADER_INCLUDED
/*
* Linked list macros.
*/
struct llhead {
struct llhead *prev;
struct llhead *next;
};
#define LL_INIT(N) ((N)->next = (N)->prev = (N))
#define LL_HEAD(H) struct llhead H = { &H, &H }
#define LL_ENTRY(P,T,N) ((T *)((char *)(P) - offsetof(T, N)))
#define LL_ADD(H, N) \
do { \
((H)->next)->prev = (N); \
(N)->next = ((H)->next); \
(N)->prev = (H); \
(H)->next = (N); \
} while (0)
#define LL_TAIL(H, N) \
do { \
((H)->prev)->next = (N); \
(N)->prev = ((H)->prev); \
(N)->next = (H); \
(H)->prev = (N); \
} while (0)
#define LL_DEL(N) \
do { \
((N)->next)->prev = ((N)->prev); \
((N)->prev)->next = ((N)->next); \
LL_INIT(N); \
} while (0)
#define LL_EMPTY(N) ((N)->next == (N))
#define LL_FOREACH(H,N) for (N = (H)->next; N != (H); N = (N)->next)
#define LL_FOREACH_SAFE(H,N,T) \
for (N = (H)->next, T = (N)->next; N != (H); \
N = (T), T = (N)->next)
#endif /* LLIST_HEADER_INCLUDED */