Linux内核数据结构—链表
2018-01-09 00:00
411 查看
[b][b][b][b][b](点击上方蓝字,快速关注)[/b][/b][/b][/b][/b]链表(循环双向链表)是Linux内核中最简单、最常用的一种数据结构。内核中关于链表定义的代码位于: include/linux/list.h。list.h文件中对每个函数都有注释,这里就不详细说了。其实刚开始只要先了解一个常用的链表操作(追加,删除,遍历)的实现方法,其他方法基本都是基于这些常用操作的。
链表结构:
![](https://ss.csdn.net/p?http://mmbiz.qpic.cn/mmbiz_png/ibmAwmYxgDBOajMuDkOKogbn6pPic7FKcF5CwTAfCv7ZiakbTP9Ficpj0r093s6Hm6eSjUcGbL2muatAlPiciceF2TMQ/0?wx_fmt=png)
1. 链表的初始化
初始化是一个双向链表,还是环状的,这和stl中的list是一样的。下面是形成一个空链表,list作为头指针(不是头节点)
![](https://ss.csdn.net/p?http://mmbiz.qpic.cn/mmbiz_png/ibmAwmYxgDBOajMuDkOKogbn6pPic7FKcFC7P6aJCibUaqkicYdcFjbwDgVCdE8NRpPKCB3ElyGMA7ZibYA6ssgZGrA/0?wx_fmt=png)
2. 访问数据
内核链表不同于普通链表的是,它是内嵌到数据对象中,这么说来,就是同类型的对象内部都嵌有这个链表,并且是通过这个链表穿针引线在一起,我们关心的是对象中的数据内容,那么究竟是如何通过对象内部的链表来访问其中的数据内容的呢?
![](https://ss.csdn.net/p?http://mmbiz.qpic.cn/mmbiz_png/ibmAwmYxgDBOajMuDkOKogbn6pPic7FKcF6wbdxAZaz1UxSUjZeD87jQYURKVvG6dna3ssjrA2xSqlzFfTibHVp0w/0?wx_fmt=png)
宏定义转到:
![](https://ss.csdn.net/p?http://mmbiz.qpic.cn/mmbiz_png/ibmAwmYxgDBOajMuDkOKogbn6pPic7FKcFUL3REnlYhs0KETr2Idhlw1dw86STqCHgSibuxmviaA5bgw4icaYicZ2WfQ/0?wx_fmt=png)
还有一个:
![](https://ss.csdn.net/p?http://mmbiz.qpic.cn/mmbiz_png/ibmAwmYxgDBOajMuDkOKogbn6pPic7FKcFxkialScicIw1AAiaGia0A5fG2lgrBXeXZlCuM1HIejiaD7ZRvYC7Iu0bWSg/0?wx_fmt=png)
从后往前一步步分析:将0x0地址强制转换为TYPE*类型,然后取TYPE中的成员MEMBER地址,因为起始地址为0,得到的MEMBER的地址就直接是该成员相对于TYPE对象的偏移地址了。所以该语句的功能是:得到TYPE类型对象中MEMBER成员的地址偏移量。 对于container_of:先看第一条:const typeof(((type *)0)->member)*__mptr = (ptr); 首先将0x0转换为TYPE类型的指针变量,再引用member成员,typeof(x)返回的是x的数据类型,所以typeof(((type*)0)->member)返回的就是member成员的数据类型,该语句就是将__mptr强制转换为member成员的数据类型,然后再将ptr赋给它。ptr本来就是指向member的指针;说白了,就是定义一个member成员类型的指针变量,然后将ptr赋给它。好,来看下一条语句:先将__mptr强制转换为char*类型(因为char* 类型进行加减的话,加减量为sizeof(char)*offset,char占一个字节空间,这样指针加减的步长就是1个字节,实现加一减一。)实现地址的精确定位,如果不这样的话,假如原始数据类型是int,那么这减去offset,实际上就是减去sizeof(int *)*offset 的大小了。所以整体意思就是:得到指向type的指针,已知成员的地址,然后减去这个成员相对于整个结构对象的地址偏移量,不就得到这个数据对象的地址么最后看list_entry:有了前面的剖析,好理解了,该宏定义作为右值,返回type类型对象的地址。type->member,表明member是type中的成员,所以可以得知member是内嵌的链表,type则是对象指针,那么ptr是链表类型指针,并且是属于那个穿针引线的链表中的某一个链表节点,所有的对象结构都挂在ptr所在的链表上。该宏定义可以返回ptr对应链表节点的对象的地址。关键是对应,找到的对象的地址是ptr这个链表所对应的那个对象的地址。ptr和member之间的关系就是,ptr是member类型的指针。其实上面就是地址的强制转换,然后得到偏移量之类的,就是指针,而且上面有个特点,那就是跟结构体中的内存对齐无关。
3. 遍历链表(实际是得到指定链表节点对应的数据对象结构)有了前面的定位某个结构地址,遍历就好办了。得到第一个节点元素地址
![](https://ss.csdn.net/p?http://mmbiz.qpic.cn/mmbiz_png/ibmAwmYxgDBOajMuDkOKogbn6pPic7FKcFNwXP7NrX93OmWfk8Ud6ribcdMI97RtknmDOzLvCma1zcBhUrGZB3Lzg/0?wx_fmt=png)
上面传入的ptr是将各个type对象串起来的链表的头指针,ptr->next 就是该链表的第一个节点。上面返回值就是挂载在第一个节点上的对象地址。对象结构就是像下面这样挂载在链表上:
![](https://ss.csdn.net/p?http://mmbiz.qpic.cn/mmbiz_png/ibmAwmYxgDBOajMuDkOKogbn6pPic7FKcFfXoR6nr8jhO3TYzmolo00qIrWX9AbIhntQDtrJsMOB1Yajs3UPDs0Q/0?wx_fmt=png)
访问第一个对象就是:
![](https://ss.csdn.net/p?http://mmbiz.qpic.cn/mmbiz_png/ibmAwmYxgDBOajMuDkOKogbn6pPic7FKcFVjoCPoDIXXziaiaaUq9GNsXwJl36bAUicwtLHHW467lCWWBmtTnS26Vnw/0?wx_fmt=png)
遍历所有数据对象就是:
![](https://ss.csdn.net/p?http://mmbiz.qpic.cn/mmbiz_png/ibmAwmYxgDBOajMuDkOKogbn6pPic7FKcFfsziacXnGxUdp0LB2DnzumP4Vp1v5qwALwfdGbNBbxp7UD57Gia32XicA/0?wx_fmt=png)
4、添加元素
![](https://ss.csdn.net/p?http://mmbiz.qpic.cn/mmbiz_png/ibmAwmYxgDBOajMuDkOKogbn6pPic7FKcFFkT2sqqKN7XsR4vH7J8ZazaIwW92ryYgiaLJOCExGtbbNfVfODDuffA/0?wx_fmt=png)
new元素添加到prev的后面,next的前面。
![](https://ss.csdn.net/p?http://mmbiz.qpic.cn/mmbiz_png/ibmAwmYxgDBOajMuDkOKogbn6pPic7FKcFOXzNYqLBmHicRTxUicTvpyzfvVOoWlnibx7d9CxTeGicp3eqOdDzVGUiajQ/0?wx_fmt=png)
5、删除元素
![](https://ss.csdn.net/p?http://mmbiz.qpic.cn/mmbiz_png/ibmAwmYxgDBOajMuDkOKogbn6pPic7FKcF3Ice0LNDsPfD0tlyLFAoIr1j24BTxfTlEqry4lLFqh3Xk3tZmxPF2g/0?wx_fmt=png)
6、替换元素
![](https://ss.csdn.net/p?http://mmbiz.qpic.cn/mmbiz_png/ibmAwmYxgDBOajMuDkOKogbn6pPic7FKcFtplxKIjjKcbTb7Ad7jhQrEJs66KwrsTOcyLTfKzcV7WbFXBthleE5A/0?wx_fmt=png)
7、移动元素
![](https://ss.csdn.net/p?http://mmbiz.qpic.cn/mmbiz_png/ibmAwmYxgDBOajMuDkOKogbn6pPic7FKcFHe2Ab9wjyqkSFtPq1vN0zNs4GkFFzu1xLUafpZbSoVZqSVW93lxw6w/0?wx_fmt=png)
链表结构:
1. 链表的初始化
初始化是一个双向链表,还是环状的,这和stl中的list是一样的。下面是形成一个空链表,list作为头指针(不是头节点)
2. 访问数据
内核链表不同于普通链表的是,它是内嵌到数据对象中,这么说来,就是同类型的对象内部都嵌有这个链表,并且是通过这个链表穿针引线在一起,我们关心的是对象中的数据内容,那么究竟是如何通过对象内部的链表来访问其中的数据内容的呢?
宏定义转到:
还有一个:
从后往前一步步分析:将0x0地址强制转换为TYPE*类型,然后取TYPE中的成员MEMBER地址,因为起始地址为0,得到的MEMBER的地址就直接是该成员相对于TYPE对象的偏移地址了。所以该语句的功能是:得到TYPE类型对象中MEMBER成员的地址偏移量。 对于container_of:先看第一条:const typeof(((type *)0)->member)*__mptr = (ptr); 首先将0x0转换为TYPE类型的指针变量,再引用member成员,typeof(x)返回的是x的数据类型,所以typeof(((type*)0)->member)返回的就是member成员的数据类型,该语句就是将__mptr强制转换为member成员的数据类型,然后再将ptr赋给它。ptr本来就是指向member的指针;说白了,就是定义一个member成员类型的指针变量,然后将ptr赋给它。好,来看下一条语句:先将__mptr强制转换为char*类型(因为char* 类型进行加减的话,加减量为sizeof(char)*offset,char占一个字节空间,这样指针加减的步长就是1个字节,实现加一减一。)实现地址的精确定位,如果不这样的话,假如原始数据类型是int,那么这减去offset,实际上就是减去sizeof(int *)*offset 的大小了。所以整体意思就是:得到指向type的指针,已知成员的地址,然后减去这个成员相对于整个结构对象的地址偏移量,不就得到这个数据对象的地址么最后看list_entry:有了前面的剖析,好理解了,该宏定义作为右值,返回type类型对象的地址。type->member,表明member是type中的成员,所以可以得知member是内嵌的链表,type则是对象指针,那么ptr是链表类型指针,并且是属于那个穿针引线的链表中的某一个链表节点,所有的对象结构都挂在ptr所在的链表上。该宏定义可以返回ptr对应链表节点的对象的地址。关键是对应,找到的对象的地址是ptr这个链表所对应的那个对象的地址。ptr和member之间的关系就是,ptr是member类型的指针。其实上面就是地址的强制转换,然后得到偏移量之类的,就是指针,而且上面有个特点,那就是跟结构体中的内存对齐无关。
3. 遍历链表(实际是得到指定链表节点对应的数据对象结构)有了前面的定位某个结构地址,遍历就好办了。得到第一个节点元素地址
上面传入的ptr是将各个type对象串起来的链表的头指针,ptr->next 就是该链表的第一个节点。上面返回值就是挂载在第一个节点上的对象地址。对象结构就是像下面这样挂载在链表上:
访问第一个对象就是:
遍历所有数据对象就是:
4、添加元素
new元素添加到prev的后面,next的前面。
5、删除元素
6、替换元素
7、移动元素
相关文章推荐
- Linux内核数据结构之链表
- linux内核数据结构之链表
- linux内核数据结构之链表
- 【Linux内核数据结构】最为经典的链表list
- linux内核数据结构之链表
- Linux内核数据结构之链表
- Linux内核数据结构之链表
- linux内核数据结构之链表【转】
- linux内核数据结构-顺序存储链表(1)
- linux内核数据结构---链表(1)
- AT&T汇编语言与GCC内嵌汇编,Linux内核数据结构之链表
- linux内核数据结构之链表
- linux 内核链表(二)
- 深入分析 Linux 内核链表
- linux内核之链表操作解析【转】
- 深入分析 Linux 内核链表
- linux内核的异常链表--只逮捕,不行刑
- Linux 内核链表 【转】
- linux的数据结构---链表
- Linux内核中链表的学习