很多地方都有list_entry函数使用,http://blog.csdn.net/hs794502825/article/details/10364977
但是分析着很复杂,给个例子就很清楚了,
typedef struct xxx
{
......//其他值
type1 member;
......//其他值
}type;
定义变量:
type a;
type *b;
tpye1*ptr;
执行:
ptr=&(a.member);
b=list_entry(ptr,type,member);
这个操作可以使b指向a,得到了a的地址,就是从结构体内部数据入手,得到结构体自身指针。以中间类型为准。
这是使用结论,链接中有源代码分析为什么会这样的。
详细分析。 https://blog.csdn.net/wangliang888888/article/details/51213751
list_head从字面上理解,好像是头结点的意思。但从这里的代码来看却是普通结点的结构体,在后面的代码中将list_head当成普通的结点来处理。
1、定义: 初始化真正的头结点,而此类链表是链表自身存在于一个数据结构体中,
struct kool_list{ int to; struct list_head list; int from; };
struct kool_list mylist;
2、那么这种类型的,初始化是有两种方法:
INIT_LIST_HEAD(&mylist.list);或者
LIST_HEAD(mylist);
上述相当于struct list_head name = {.next=&name, .prev=&name} 此时的name是mylist.list
3、注意点 但是注意虽然自己定义了一个数据结构,但是初始化的时候,是初始化的自己定义的数据结构中的list这个部分,当然,使用第二种就不用这么细致了。
4、插入(注意使用链表是因为只用记忆一个头结点,其他的用链表指针指向即可,不用专门记忆,想知道任何节点内容,只能遍历!!!): 而剩下的插入,list_add(struct list_head *new, struct list_head *head)==__list_add(new, head, head->next);是将new插入head和head之后的节点之间。当只有一个head存在,,那么就是将new插在head之后。此时看到并不是说一定插在头结点后面,而是新节点插在第二个变量和第三个变量之间。
此处仍然要注意,因为加入的是参数是链表地址,并不是自己的结构体地址,而且第二个参数是自己的头结构体内部的所对应指针。如果想做到交换两个节点位置,这个与直接插入指定位置还是不相同的,,
5、static inline void list_add_tail(struct list_head *new, struct list_head *head)==__list_add(new, head->prev, head);这个明显能看出来是往前 插入。。
list_add和list_add_tail虽然原型一样,但调用底层函数__list_add时传递了不同的参数,从而实现了在head指向节点之前或之后添加新的对象
6、链表删除,
仅仅是把节点从链表中断开,但是节点自身空间本身是用户设定的,还需用户专门释放。
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
} 这个的意思是第一个参数是要删除节点的前一个节点,后面参数是要删除节点的后一个节点。
真正的删除为:
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
删除entry所指的结点,同时将entry所指向的结点指针域封死。可以看到该函数对上面函数进行调用。而且封死的意义是与下面函数对应的,static inline void list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
INIT_LIST_HEAD(entry);
}
删除entry所指向的结点,同时调用LIST_INIT_HEAD()把被删除节点为作为链表头构建一个新的空双循环链表。因为这个是对切除下来的重新给构建了一个新的空双循环链表。
7、遍历的时候,一般都是从链表位置开始然后从链表位置找到自身的结构体指针,因为最终是想得到结构体,而不是链表的。
list_for_each(pos, &mylist.list){//pos是每个链表节点的位置
tmp=list_entry(pos, struct kool_list, list)// 此处是pos这个从list_for_each中取到的每个链表自身位置,然后第二个是说清楚链表节点所在的容器,也就是结构体本身的定义,而list是结构体的链表元素的成员名称,不是成员类型名称,类型是确定的struct list_head,就是list。所以三个分别是:链表值(从上list_for_each得到)、容器定义、链表成员自身在容器中的名字。
list_for_each_prev()这个是反向遍历方法。
8、还有个操作是移动操作。移动到某个位置,但是链表问题就是你只能确定一个头结构体指针,不能确定其他的节点地址,要想知道其他节点地址,只能像上面那样,遍历来找。
二、注意,主要是等待队列为什么睡眠函数里面放的是队列,但是睡眠的却是进程的原因,
注意等待队列,同样是队列,同样是双向链接表!!所以定义的
- struct __wait_queue_head {
- spinlock_t lock; /*因为等待队列可以在中断时随时修改,因此设置一个自旋锁保证一致性*/
- struct list_head task_list;
- };
这个是有一个list_head的,