linux 驱动学习之list链表机制
2017-10-27 17:24
417 查看
转载:http://blog.chinaunix.net/uid-27122224-id-3277511.html
结构:
具体使用:
struct list_head head;
LIST_HEAD_INIT(head);
LIST_HEAD(head);
举例来说:
struct person *one = list_entry(pos, struct person, list);
把list_entry 展开
((struct person )((char )(pos) - (unsigned long)(&((struct person *)0)->list)))
其中
1.(unsigned long)(&((struct person *)0)->list)的意思就是取list 变量在struct person结构中的偏移量。
2.((char
4000
)(pos) - (unsigned long)(&((struct person )0)->list))
就是将pos指针往前移动offset位置,即是本来pos是struct list_head类型,它即是list。即是把
pos指针往struct person结构的头地址位置移动过去,如上图的pos和虚箭头。
当pos移到struct person结构头后就转 成(struct person *)指针,这样就可以得到struct person
*变量了。
list_for_each(pos, head)是遍历整个head链表中的每个元素,每个元素都用pos指向。
list_for_each_safe(pos, n, head)是用于删除链表head中的元素,不是上面有删除链表元素的函数了
吗,为什么这里又要定义一个这样的宏呢。看下这个宏后面有个safe字,就是说用这个宏来删除是安全的,
直接用前面的那些删除函数是不安全的。
原因:
list_for_each
删除pos后,list_for_each要移到下一个元素,还需要用pos来取得下一个元素,但pos的指向已 经改变,如果不直接退出而是在继续操作的话,就会出错了。
ist_for_each_safe
这里我们使用了n作为一个临时的指针,当pos被删除后,还可以用n来获得下一个元素的位置。
双向链表
struct list_head{ struct list_head *next, *prev; }; struct dlist{ int no; void* data; struct dlist *prev, *next; };
结构:
struct list_head结构的操作
1.初始化
就是用head的地址初始化其两个成员next和prev ,使其都指向自己。#define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0)
具体使用:
struct list_head head;
LIST_HEAD_INIT(head);
LIST_HEAD(head);
2.增加节点的函数
相关api: static inline void __list_add(); static inline void list_add(); static inline void list_add_tail(); 具体实现: /** * __list_add - Insert a new entry between two known consecutive entries. * @new: * @prev: * @next: * * This is only for internal list manipulation where we know the prev/next * entries */ 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; } //这个函数在prev和next间插入一个节点new。 /** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static __inline__ void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } //这个函数在head节点后面插入new节点。 /** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); }
3.删除节点的函数
1.list_del(struct list_head *entry)是从链表中删除entry节点。 2.list_del_init(struct list_head *entry) 不但从链表中删除节点,还把这个节点的向前向后指针都指 向自己,即初始化。 具体实现: /** * __list_del - * @prev: * @next: * * Delete a list entry by making the prev/next entries point to each other. * * This is only for internal list manipulation where we know the prev/next * entries */ static __inline__ void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * * Note: list_empty on entry does not return true after this, the entry is in * an undefined state. */ static __inline__ void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); } /** * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. */ static __inline__ void list_del_init(struct list_head *entry) { __list_del(entry->prev, entry->next); INIT_LIST_HEAD(entry); }
3.判断链表是否为空
/** * list_empty - tests whether a list is empty * @head: the list to test. */ static __inline__ int list_empty(struct list_head *head) { return head->next == head; }
4.通过list_head变量来获取整个结构的变量
/** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
举例来说:
struct person *one = list_entry(pos, struct person, list);
把list_entry 展开
((struct person )((char )(pos) - (unsigned long)(&((struct person *)0)->list)))
其中
1.(unsigned long)(&((struct person *)0)->list)的意思就是取list 变量在struct person结构中的偏移量。
2.((char
4000
)(pos) - (unsigned long)(&((struct person )0)->list))
就是将pos指针往前移动offset位置,即是本来pos是struct list_head类型,它即是list。即是把
pos指针往struct person结构的头地址位置移动过去,如上图的pos和虚箭头。
当pos移到struct person结构头后就转 成(struct person *)指针,这样就可以得到struct person
*变量了。
5.循环遍历双向链表
/** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos: the &struct list_head to use as a loop counter. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next)
list_for_each(pos, head)是遍历整个head链表中的每个元素,每个元素都用pos指向。
list_for_each_safe(pos, n, head)是用于删除链表head中的元素,不是上面有删除链表元素的函数了
吗,为什么这里又要定义一个这样的宏呢。看下这个宏后面有个safe字,就是说用这个宏来删除是安全的,
直接用前面的那些删除函数是不安全的。
原因:
list_for_each
删除pos后,list_for_each要移到下一个元素,还需要用pos来取得下一个元素,但pos的指向已 经改变,如果不直接退出而是在继续操作的话,就会出错了。
ist_for_each_safe
这里我们使用了n作为一个临时的指针,当pos被删除后,还可以用n来获得下一个元素的位置。
6.实现举例
#include <stdio.h> #include "list.h" struct person { int age; int weight; struct list_head list; }; int main(int argc, char* argv[]) { struct person *tmp; struct list_head *pos, *n; int age_i, weight_j; // 定义并初始化一个链表头 struct person person_head; INIT_LIST_HEAD(&person_head.list); for(age_i = 10, weight_j = 35; age_i < 40; age_i += 5, weight_j += 5) { tmp =(struct person*)malloc(sizeof(struct person)); tmp->age = age_i; tmp->weight = weight_j; // 把这个节点链接到链表后面 // 这里因为每次的节点都是加在person_head的后面,所以先加进来的节点就在链表里的最 后面 // 打印的时候看到的顺序就是先加进来的就在最后面打印 list_add(&(tmp->list), &(person_head.list)); } // 下面把这个链表中各个节点的值打印出来 printf("\n"); printf("=========== print the list ===============\n"); list_for_each(pos, &person_head.list) { // 这里我们用list_entry来取得pos所在的结构的指针 tmp = list_entry(pos, struct person, list); printf("age:%d, weight: %d \n", tmp->age, tmp->weight); } printf("\n"); // 下面删除一个节点中,age为20的节点 printf("========== print list after delete a node which age is 20 ==========\n"); list_for_each_safe(pos, n, &person_head.list) { tmp = list_entry(pos, struct person, list); if(tmp->age == 20) { list_del_init(pos); free(tmp); } } list_for_each(pos, &person_head.list) { tmp = list_entry(pos, struct person, list); printf("age:%d, weight: %d \n", tmp->age, tmp->weight); } // 释放资源 list_for_each_safe(pos, n, &person_head.list) { tmp = list_entry(pos, struct person, list); list_del_init(pos); free(tmp); } return 0; }
相关文章推荐
- 【嵌入式Linux学习七步曲之第四篇 Linux内核移植】详解Linux2.6内核中基于platform机制的驱动模型
- linux内核学习中-- 史上最全 linux通用链表“list.h”详解
- 详解Linux2.6内核中基于platform机制的驱动模型<强烈推荐,这是一篇学习驱动模型千年一遇的好文章>
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux内核抢占实现机制分析
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之一--中断概述
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux内核抢占实现机制分析
- 一步一步学习 Linux 驱动之 platform 机制(tiny210 按键驱动)
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】全面解析Linux内核的同步与互斥机制--同步篇
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux系统调用的实现机制分析
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux内核定时器实现机制
- 嵌入式Linux驱动学习之路(十一)按键驱动-中断机制
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之目录
- Linux下驱动:分层、分离机制学习笔记
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux内核抢占实现机制分析
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之二--Linux中断的组织形式
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之四--中断API
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】全面解析Linux内核的同步与互斥机制--互斥篇
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux系统调用的实现机制分析
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux系统调用的实现机制分析
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux信号机制分析