Fastdfs源码分析2----链表设计

libfastcommon是从Fastdfs源码提取的库,其中list设计为有头双链表。 设计方法明显是参照linux kernel。

T a n g s h u n c a i \color{#222514}{-}\color{#222514}{-}\color{#222514}{-}\color{#222514}{-}\color{#4285f4}{T}\color{#ea4335}{a}\color{#fbbc05}{n}\color{#4285f4}{g}\color{#34a853}{s}\color{#ea4335}{h}\color{#4285f4}{u}\color{#fbbc05}{n}\color{#34a853}{c}\color{#ea4335}{a}\color{#fbbc05}{i}

双链表设计

按照我们的传统,直接上源码(经过注释)

#ifndef _FC_LIST_H
#define _FC_LIST_H

// fastdfs是有头链表(头节点单独占用一个节点)

struct fc_list_head {
	struct fc_list_head *next;
	struct fc_list_head *prev;
};

#define FC_INIT_LIST_HEAD(head)  \
    do {  \
		(head)->next = (head)->prev = head;	\
	} while (0)

// c++兼容
#ifdef __cplusplus
extern "C" {
#endif

// _new节点头插到head开头的链表
static inline void
fc_list_add (struct fc_list_head *_new, struct fc_list_head *head)
{
	_new->prev = head;
	_new->next = head->next;

	_new->prev->next = _new;
	_new->next->prev = _new;
}


// _new节点尾插到head开头的链表
static inline void
fc_list_add_tail (struct fc_list_head *_new, struct fc_list_head *head)
{
	_new->next = head;
	_new->prev = head->prev;

	_new->prev->next = _new;
	_new->next->prev = _new;
}

/* _new节点插入到prev和next节点之间(prev和next必须是紧邻的一前一后2个节点)*/
static inline void
fc_list_add_internal(struct fc_list_head *_new, struct fc_list_head *prev,
        struct fc_list_head *next)
{
	next->prev = _new;
	_new->next = next;

	_new->prev = prev;
	prev->next = _new;
}

// old节点从所在链表取下,并初始化为指定表示的节点
static inline void
fc_list_del (struct fc_list_head *old)
{
	old->prev->next = old->next;
	old->next->prev = old->prev;

	old->next = (struct fc_list_head *)0xbabebabe;
	old->prev = (struct fc_list_head *)0xcafecafe;
}

// old从所在链表取下,并初始化为单节点链表
static inline void
fc_list_del_init (struct fc_list_head *old)
{
	old->prev->next = old->next;
	old->next->prev = old->prev;

	old->next = old;
	old->prev = old;
}


/* list节点从原链表取下,插入head链表头 */
static inline void
fc_list_move (struct fc_list_head *list, struct fc_list_head *head)
{
	list->prev->next = list->next;
	list->next->prev = list->prev;
	fc_list_add (list, head);
}


/* list节点从原链表取下,插入head链表尾 */
static inline void
fc_list_move_tail (struct fc_list_head *list, struct fc_list_head *head)
{
	list->prev->next = list->next;
	list->next->prev = list->prev;
	fc_list_add_tail (list, head);
}


static inline int
fc_list_empty (struct fc_list_head *head)
{
	return (head->next == head);
}


/* list链表剪开,list尾巴指向head的链表的第一节点,head指向list头形成新的链表 */
static inline void
__fc_list_splice (struct fc_list_head *list, struct fc_list_head *head)
{
	(list->prev)->next = (head->next);
	(head->next)->prev = (list->prev);

	(head)->next = (list->next);
	(list->next)->prev = (head);
}


static inline void
fc_list_splice (struct fc_list_head *list, struct fc_list_head *head)
{
	if (fc_list_empty (list))
		return;

	__fc_list_splice (list, head);
}


static inline void
fc_list_splice_init (struct fc_list_head *list, struct fc_list_head *head)
{
	if (fc_list_empty (list))
		return;

	__fc_list_splice (list, head);
	FC_INIT_LIST_HEAD (list);
}

/* list节点是否是head链表的最后一个节点 */
static inline int fc_list_is_last(const struct fc_list_head *list,
        const struct fc_list_head *head)
{
	return list->next == head;
}

// 统计head为头的链表下面的节点数
static inline int fc_list_count(struct fc_list_head *head)
{
	struct fc_list_head *pos;
	int count;

	count = 0;
	for (pos = head->next; pos != head; pos = pos->next) {
		++count;
	}
	return count;
}


// 以下代码,是借鉴linux kernel list的container_of()宏

/* ptr是fc_list_head在结构体的地址,type是结构体类型,member是fc_list_head在结构体里面的成员名
该宏代表fc_list_head所挂靠的大结构体的首地址
*/
#define fc_list_entry(ptr, type, member)					\
	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))


// pos是fc_list_head链表节点指针,head是链表头。该宏是一个for循环,pos从链表头的下一个节点开始,遍历指向链表最后一个节点
#define fc_list_for_each(pos, head)				     \
	for (pos = (head)->next; pos != (head); pos = pos->next)


/* pos是大结构体类型的迭代指针(iter),head是fc_list_head链表头,member是链表节点在大结构体的成员名。
该宏代是一个for循环,pos迭代指向head作为头的链表的下面的所有节点所挂靠的大结构体的首地址。
*/
#define fc_list_for_each_entry(pos, head, member)				\
	for (pos = fc_list_entry((head)->next, typeof(*pos), member);	\
	     &pos->member != (head); 					\
	     pos = fc_list_entry(pos->member.next, typeof(*pos), member))

/* 与上一个宏的基本功能一样,pos是大结构体类型指针,n是指向pos的下一个指针 */
#define fc_list_for_each_entry_safe(pos, n, head, member)			\
	for (pos = fc_list_entry((head)->next, typeof(*pos), member),	\
		n = fc_list_entry(pos->member.next, typeof(*pos), member);	\
	     &pos->member != (head); 					\
	     pos = n, n = fc_list_entry(n->member.next, typeof(*n), member))

// 反向(从尾到头节点的下一个节点)遍历head链表。pos是迭代器,指向当前遍历到的节点
#define fc_list_for_each_prev(pos, head) \
    for (pos = (head)->prev; pos != (head); pos = pos->prev)

// c++兼容
#ifdef __cplusplus
}
#endif

#endif

博主在还是学生时,一个linux驱动开发老师说过,linux kernel里面的所有东西都是经典,它不仅仅是编程的问题,而是一个哲学的应用,编程的艺术。在这种思想的推动下,linux里面的源码比普通的程序要精美许多。
如果问自己阅读linux kernel学会了什么知识? 在下的答案是:我什么都没有学会。
思想比招式要重要。中级程序员通常停留在招式上面,死记硬背一些工作中用不到的东西,而没有精力学会更多的知识,以自己能很熟练的记住,而别人记不住作为自己水平高的依据。 像tcpip三次握手这种问题,在现实工作中又有多少使用的场景呢?
高级程序员,研究框架并非是停留在应用的阶段,如果能持续突破瓶颈,上升到哲学和社会学的层面是必然。

链表与数组的对比很明显。数组是静态分配的,链表是可以扩容缩容的。数组查询速度快,链表查询速度慢。链表和数组的结合,就是哈希表(散列表)(天下事情无非这么几种:现实存在的,组合应用的,抽象推理实现探索的)。那么链表有多少种?在下也没有数过,数过也忘了。 如果有“突然”被问这个问题,我肯定是不假思索的回答单双链表完事儿(在我看来,链表都是长得差不多,脸盲),有很大概率还会被对方误以为技术很菜。

链表分类:

  • 单向有头链表
  • 单向无头链表
  • 双向有头链表
  • 双向无头链表
  • 交叉十字链表

以上又可分为循环、不循环链表。
其中值得一说的,也就是双向循环链表,linux kernel的源码用的也是这个链表。
另外一个是十字交叉链表,即:一个链表有四个指针(两对儿prev 和 next),这样一个节点可以最多同时挂在2条链表里面,形成网状的数据结构。 博主的老师当时写这个代码时,说他确切知道的也就百度有用这种链表,一般的公司可能从开业到倒闭也用不到一次这种数据结构。 有兴趣纯技术学习的朋友,可以自行百度十字交叉链表。 我这里也对这个链表没有了任何印象,以后大概率是用不到的,所以只提示有内容但是不具体讲。

发布了75 篇原创文章 · 获赞 71 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/jacky128256/article/details/104458474