强大的原理小弟不才,未能见识,只是因为在驱动代码中struct list_head把我搞的云里雾里的,不时痛定思定的花了几个时候来推敲,哎,在明白原理后才恍然大悟,原来代码都是用来实现原理的,所以,有时候"内功"修为还是很有必要的,话不多说,分解如下:
INIT_LIST_HEAD(&nandr->devs)
list_add_tail(&dev->list, &nandr->devs)
其中参数都是struct list_head {
struct list_head *next, *prev;
};
初始化的时候是把 next和prev都指向了 head。
list_add_tail调用的是如下函数:
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
相比而言,__list_add只是多添加了一个参数head->prev,就是这个参数,所有的玄妙都在于此。
进入__list_add可以看到如下代码:
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
代码如此简单,但是很容易陷入的指针指向问题,我们知道lisd_add_tail就是在链表尾添加一个struct list_head,从第一个开始分析,刚开始的时候next和prev都指向head,
所以在第一次执行__list_add时:
__list_add的三个参数是new,head->prev,head,(其中head->prev=head)
next->prev = new; => head->prev=new。
new->next = next; =>new->=head
new->prev = prev; =>new->prev=head。
prev->next = new; 这一句是在这里写出来很容易误解,从第三句(也就上一句代码)可以发现,new和head的prev都是指向prev,意思是每一次添加的新元素的prev都是指向head的,所以__list_add的第二个参数其实都是head,而prev->next=new相当于head->next=new,整个链表是个环形。
好,如果还没明白的话可以看第二次添加,
第一次添加后head->prev=new,head->next=new,new->next=head,new->prev=head。
为了区分new,假设第二次添加的是ayu,
__list_add(new, head->prev, head); 这个时候head应该是new,因为表尾是new了。
__list_add(new, head->prev, head)=>__list_add(ayu,new->prev,new) 这个new不是__list_add代码中的new,它是我们传递给__list_add的参数。
next->prev = new; =>new->prev=ayu。
new->next = next; => ayu->next=new。
new->prev = prev; =>ayu->prev=new->prev=head
prev->next = new; => head->=new。
可能描述的不太清晰,最后总结一下:
双向链表最后一个元素的指向是last->prev=head,last->next=(last-1)
当添加一个量时,比如new,那么最后一个元素的指向和new都发生了哪些变化,因为都是加到表尾所以new->prev=head,new->next=last,而last的变化时last->prev=new,而last->next没有改变还是last->next=(last-1)。当然添加一个元素后,表头head的next需要更新prev->next=nex(指向最新的表尾)