数据结构与算法 Linux内核中双向链表的实现基础

Posted 码农有道

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法 Linux内核中双向链表的实现基础相关的知识,希望对你有一定的参考价值。

在前面文章中总结了双向链表,我们继续对Linux内核中双向链表进行探讨,将分为两篇文章讲述,本文则主要涉及Linux内核中非常常用的两个经典宏定义offsetofcontainer_of。它是理解Linux内涵双向链表的基础。

倘若你查看过Linux Kernel的源码,那么你对 offsetof 和 container_of 这两个宏应该不陌生。这两个宏最初是极客写出的,后来在Linux内核中被推广使用。下面分别介绍。


offsetof

定义:offsetof在linux内核的include/linux/stddef.h中定义。

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

功能:获得结构体(TYPE)的变量成员(MEMBER)在此结构体中的偏移量。

(2)  ((TYPE *)0)->MEMBER: 访问结构中的数据成员。

(4)  (size_t)(&(((TYPE*)0)->MEMBER)): 结果转换类型。对于32位系统而言,size_t是unsigned int类型;对于64位系统而言,size_t是unsigned long类型。

TYPE是结构体,它代表"整体";而MEMBER是成员,它是整体中的某一部分。将offsetof看作一个数学问题来看待,问题就相当简单了:已知'整体'和该整体中'某一个部分',而计算该部分在整体中的偏移下面看一个例子。

#include <stdio.h>

// 获得结构体(TYPE)的变量成员(MEMBER)在此结构体中的偏移量。
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

struct student
{ char gender; int id; int age; char name[20];
};

int main()
{ int gender_offset, id_offset, age_offset, name_offset; gender_offset = offsetof(struct student, gender); id_offset = offsetof(struct student, id); age_offset = offsetof(struct student, age); name_offset = offsetof(struct student, name); printf("gender_offset = %d\n", gender_offset); printf("id_offset = %d\n", id_offset); printf("age_offset = %d\n", age_offset); printf("name_offset = %d\n", name_offset);
}

运行结果

gender_offset = 0
id_offset = 4
age_offset = 8
name_offset = 12


container_of

定义:container_of在linux内核的include/linux/kernel.h中定义。

#define container_of(ptr, type, member) ({   \
    const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    (type *)( (char *)__mptr - offsetof(type,member) );})

说明根据"结构体(type)变量"中的"域成员变量(member)的指针(ptr)"来获取指向整个结构体变量的指针。

(1)  typeof( ( (type *)0)->member ): 取出member成员的变量类型。

(3)  (char *)__mptr: 将__mptr转换为字节型指针。

(4)  offsetof(type,member))    就是获取"member成员"在"结构体type"中的位置偏移。

(6)  (type *)( (char *)__mptr - offsetof(type,member) ):  就是将"char *类型的结构体type的指针"转换为"type *类型的结构体type的指针"。

#include <stdio.h>
#
include <string.h>

// 获得结构体(TYPE)的变量成员(MEMBER)在此结构体中的偏移量。
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

// 根据"结构体(type)变量"中的"域成员变量(member)的指针(ptr)"
来获取指向整个结构体变量的指针

#define container_of(ptr, type, member) ({   \    const typeof( ((type *)0)->member ) *__mptr = (ptr); \    (type *)( (char *)__mptr - offsetof(type,member) );})

struct student
{    char gender;    int id;    int age;    char name[20];
};

int main()
{    struct student stu;    struct student *pstu;    stu.gender = '1';    stu.id = 9527;    stu.age = 24;    strcpy(stu.name, "zhouxingxing");    // 根据"id地址" 获取 "结构体的地址"。    pstu = container_of(&stu.id, struct student, id);    // 根据获取到的结构体student的地址,访问其它成员    printf("gender= %c\n", pstu->gender);    printf("age= %d\n", pstu->age);    printf("name= %s\n", pstu->name);
}

运行结果:

gender= 1
age= 24
name= zhouxingxing


推荐阅读:





专注服务器后台技术栈知识总结分享

欢迎关注交流共同进步

码农有道 coding


码农有道,为您提供通俗易懂的技术文章,让技术变的更简单!

以上是关于数据结构与算法 Linux内核中双向链表的实现基础的主要内容,如果未能解决你的问题,请参考以下文章

linux内核共享双向链表

Linux内核中双向链表的经典实现

Linux 内核数据结构:双向链表

第33课 双向循环链表的实现

Linux内核基础设施

Linux 内核数据结构:Linux 双向链表