FreeBSD kernel
提供了对双向链表的支持(定义在sys/sys/queue.h
中):
/*
* List declarations.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_CLASS_HEAD(name, type) \
struct name { \
class type *lh_first; /* first element */ \
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
#define LIST_CLASS_ENTRY(type) \
struct { \
class type *le_next; /* next element */ \
class type **le_prev; /* address of previous next element */ \
}
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
#define LIST_FIRST(head) ((head)->lh_first)
#define LIST_FOREACH(var, head, field) \
for ((var) = LIST_FIRST((head)); \
(var); \
(var) = LIST_NEXT((var), field))
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_INSERT_HEAD(head, elm, field) do { \
QMD_LIST_CHECK_HEAD((head), field); \
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
LIST_FIRST((head)) = (elm); \
(elm)->field.le_prev = &LIST_FIRST((head)); \
} while (0)
......
以FreeBSD Device Drivers代码为例:
(1)race_softc
结构体定义:
struct race_softc {
LIST_ENTRY(race_softc) list;
int unit;
};
展开以后变成如下代码:
struct race_softc {
struct { \
struct race_softc *le_next; /* next element */ \
struct race_softc **le_prev; /* address of previous next element */ \
} list;
int unit;
};
(2)双向链表头定义:
static LIST_HEAD(, race_softc) race_list = LIST_HEAD_INITIALIZER(&race_list);
展开以后变成如下代码:
struct {struct race_softc *lh_first;} race_list = {NULL};
(3)插入一个元素:
sc = (struct race_softc *)malloc(sizeof(struct race_softc), M_RACE, M_WAITOK | M_ZERO);
sc->unit = unit;
LIST_INSERT_HEAD(&race_list, sc, list);
展开以后变成如下代码:
sc = (struct race_softc *)malloc(sizeof(struct race_softc), M_RACE, M_WAITOK | M_ZERO);
sc->unit = unit;
do { \
QMD_LIST_CHECK_HEAD((race_list), list); \
if ((LIST_NEXT((sc), list) = LIST_FIRST((race_list))) != NULL) \
LIST_FIRST((race_list))->list.le_prev = &LIST_NEXT((sc), list);\
LIST_FIRST((race_list)) = (sc); \
(sc)->list.le_prev = &LIST_FIRST((race_list)); \
} while (0)
展开以后变成如下代码:
do {
if (((((sc))->list.le_next) = (((&race_list))->lh_first)) != ((void *)0)) (((&race_list))->lh_first)->list.le_prev = &(((sc))->list.le_next);
(((&race_list))->lh_first) = (sc);
(sc)->list.le_prev = &(((&race_list))->lh_first);
} while (0);
即把元素插在链表头部。因为sc
位于链表头部,所以其list.le_prev
指向它自己。