linux的结构体list使用。list_entry使用。链表。 和等待队列基本使用方法

本文深入探讨了Linux系统中结构体list的使用,包括list_entry函数如何从结构体内部数据获取结构体指针。此外,文章详细讲解了list_head的初始化、插入、删除操作,以及list_add和list_add_tail的区别。接着,文章转向等待队列的使用,阐述了等待队列头的结构、等待队列的添加、删除和唤醒过程,以及它们在进程阻塞和调度中的作用。通过实例代码和分析,帮助读者理解Linux内核中这些数据结构的实现细节。
摘要由CSDN通过智能技术生成

很多地方都有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()这个是反向遍历方法。

 * list_for_each_safe     -      iterate over a list safe against removal of list entry
  */
#define list_for_each_safe(pos, n, head) \
       for (pos = (head)->next, n = pos->next; pos != (head); \
              pos = n, n = pos->next)
在for循环中n暂存pos下一个节点的地址,避免因pos节点被释放而造成的断链。也就是说你 可以遍历完当前节点后将其删除同时可以 接着访问下一个节点,遍历完毕后就只剩下一个头节点。这就叫safe。



8、还有个操作是移动操作。移动到某个位置,但是链表问题就是你只能确定一个头结构体指针,不能确定其他的节点地址,要想知道其他节点地址,只能像上面那样,遍历来找。



二、注意,主要是等待队列为什么睡眠函数里面放的是队列,但是睡眠的却是进程的原因

注意等待队列,同样是队列,同样是双向链接表!!所以定义的

  1. struct __wait_queue_head {
  2.     spinlock_t lock;      /*因为等待队列可以在中断时随时修改,因此设置一个自旋锁保证一致性*/
  3.     struct list_head task_list;
  4. };

这个是有一个list_head的,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值