Linux Kernel list 移植优化

Linux Kernel list 移植优化

移植后我们只需要将list_node定义在你自己的结构体中即可。为了可读性和操作简化,请把list_node定义在结构体头部,虽然对于list来说你不把它定义在头部也没有关系。

数据结构

container_of 宏

这是Linux内核中很常用的一个宏定义,配合offsetof可以轻松根据结构体成员计算出结构体首地址,内核通用链表实现依赖于这个宏。

我么先来看 offsetof 宏:

c 复制代码
#ifndef offsetof
typedef unsigned long size_t;
// 获取结构体成员偏移,因为常量指针的值为0,即可以看作结构体首地址为0
#define offsetof(TYPE,MEMBER)((size_t)&((TYPE *)0)->MEMBER)
#endif

这个宏用来获取结构体成员的偏移,通过将结构体首地址定义为 0 ,从而获取结构体成员相对于 0 的偏移, 也就是相对于结构体首地址的偏移。

再来看 container_of 宏:

c 复制代码
#ifndef container_of
/*ptr 成员指针
* type 结构体 比如struct Stu
* member 成员变量,跟指针对应
* */
// 最后一句的意义就是,取结构体某个成员member的地址,减去这个成员在结构体type中的偏移,运算结果就是结构体type的首地址
#define container_of(ptr, type, member) ({          \
        const typeof( ((type *)0)->member ) *__mptr = (const typeof( ((type *)0)->member ) *)(ptr); \
        (type *)( (char *)__mptr - offsetof(type,member) );})
#endif

const typeof( ((type *)0)->member ) *__mptr = (const typeof( ((type *)0)->member ) *)(ptr); 用于获取我们传入的成员变量指针的地址,在用他减去他相对于 0 的偏移(相对于结构体首地址的偏移)即可得到结构体的首地址。

表达式语句

container_of 宏使用了表达式语句,这里做一下介绍。

GNU C对C语言标准作了扩展,允许在一个表达式里内嵌语句,允许在表达式内部使用局部变量、for循环和goto跳转语句。这种类型的表达式,我们称为语句表达式。语句表达式的格式如下。

c 复制代码
({a; b; c;})

语句表达式最外面使用小括号()括起来,里面一对大括号{}包起来的是代码块,代码块里允许内嵌各种语句。语句的格式可以是一般表达式,也可以是循环、跳转语句。和一般表达式一样,语句表达式也有自己的值。语句表达式的值为内嵌语句中最后一个表达式的值。我们举个例子,使用语句表达式求值。

c 复制代码
int sum = ({
    int i;
    for(i = 0; i < 10; ++i);
    i;
})

最后 sum = 10;

list.h

c 复制代码
#ifndef __LIST_H__
#define __LIST_H__

#ifndef offsetof
typedef unsigned long size_t;
// 获取结构体成员偏移,因为常量指针的值为0,即可以看作结构体首地址为0
#define offsetof(TYPE,MEMBER)((size_t)&((TYPE *)0)->MEMBER)
#endif

#ifndef container_of
/*ptr 成员指针
* type 结构体 比如struct Stu
* member 成员变量,跟指针对应
* */
// 最后一句的意义就是,取结构体某个成员member的地址,减去这个成员在结构体type中的偏移,运算结果就是结构体type的首地址
#define container_of(ptr, type, member) ({          \
        const typeof( ((type *)0)->member ) *__mptr = (const typeof( ((type *)0)->member ) *)(ptr); \
        (type *)( (char *)__mptr - offsetof(type,member) );})
#endif

#ifndef list_entry
// 获取结构体指针的成员变量地址
#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)
#endif

struct list_head {
    struct list_head *next, *prev;
};

typedef struct list_head list_head;
typedef struct list_head list_node;

// 初始化链表头
extern void init_list_head(list_head *list);

// 插入节点到当前节点之前
extern void list_add_prev(list_node *node, list_node *cur);

// 插入节点到当前节点之后
extern void list_add_next(list_node *node, list_node *cur);

// 链表头插入元素
extern void list_add_head(list_node *node, list_head *head);

// 链表尾插入元素
extern void list_add_tail(list_node *node, list_head *head);

// 链表删除元素
extern void list_del(list_node *node);

// 链表判断是否为空
extern int list_empty(const list_head *head);

// 链表遍历
// 只进行遍历,不进行危险操作.
#define list_for_each_entry(pos, head, member)                \
    for (pos = list_entry((head)->next, typeof(*pos), member); \
         &pos->member != (head);                              \
         pos = list_entry(pos->member.next, typeof(*pos), member))

// 遍历过程中需要删除节点,我们通过next记录了下一个节点的位置。
#define list_for_each_entry_safe(pos, next, head, member)           \
    for (pos = list_entry((head)->next, typeof(*pos), member),   \
         next = list_entry(pos->member.next, typeof(*pos), member);  \
         &pos->member != (head);                                 \
         pos = next, next = list_entry(next->member.next, typeof(*next), member))

#endif // __LIST_H__

list.c

c 复制代码
#include "list.h"

// 不小心访问到已经释放的内存,便于调试
static void* LIST_POSTION1 = (void*)0xdeadbeef;
static void* LIST_POSTION2 = (void*)0xdeadbeef;

static void __list_add(list_node *new, list_node *prev, list_node *next)
{
    next->prev = new;
    new->next = next;
    new->prev = prev;
    prev->next = new;
}

static void __list_del(list_node *node)
{
    node->next->prev = node->prev;
    node->prev->next = node->next;
}

// 初始化链表头
void init_list_head(list_head *list)
{
    list->next = list->prev = list;
}

// 插入节点到当前节点之前
void list_add_prev(list_node *node, list_node *cur)
{
    __list_add(node, cur->prev, cur);
}

// 插入节点到当前节点之后
void list_add_next(list_node *node, list_node *cur)
{
    __list_add(node, cur, cur->next);
}

// 链表头插入元素
void list_add_head(list_node *node, list_head *head)
{
    __list_add(node, head, head->next);
}

// 链表尾插入元素
void list_add_tail(list_node *node, list_head *head)
{
    __list_add(node, head->prev, head);
}

// 链表删除元素
void list_del(list_node *node)
{
    __list_del(node);
    node->next = LIST_POSTION1;
    node->prev = LIST_POSTION2;
}

// 链表判断是否为空
int list_empty(const list_head *head)
{
    return head->next == head;
}

example

c 复制代码
#include "list.h"
#include <stdio.h>

struct example {
    // 虽然这对于链表来说并不是必须的, 但还是为了可读性, 请将list_node 定义为第一个成员变量
    list_node list;
    int value;
};

int main(int argc, char const *argv[])
{
    // 初始化链表头
    list_head head;
    init_list_head(&head);
    // 初始化链表元素
    struct example e1, e2, e3;
    e1.value = 1;
    e2.value = 2;
    e3.value = 3;
    fprintf(stdout, "list_add_head: %d %d %d\n", e1.value, e2.value, e3.value);
    // 链表头插入元素
    list_add_head(&e1.list, &head);
    list_add_head(&e2.list, &head);
    list_add_head(&e3.list, &head);

    struct example e4, e5, e6;
    e4.value = 4;
    e5.value = 5;
    e6.value = 6;
    fprintf(stdout, "list_add_tail: %d %d %d\n", e4.value, e5.value, e6.value);
    // 链表尾插入元素
    list_add_tail(&e4.list, &head);
    list_add_tail(&e5.list, &head);
    list_add_tail(&e6.list, &head);
    // 遍历链表
    struct example *pos;
    list_for_each_entry(pos, &head, list)
    {
        fprintf(stdout, "value: %d\n", pos->value);
    }
    fprintf(stdout, "list_del %d\n", e2.value);
    // 删除链表元素
    list_del(&e2.list);
    // 遍历链表
    list_for_each_entry(pos, &head, list)
    {
        fprintf(stdout, "value: %d\n", pos->value);
    }

    fprintf(stdout, "list_add_prev list_add_next: in = %d cur = 5\n", e2.value);
    // 遍历添加节点
    struct example *next, *cur;
    list_for_each_entry_safe(pos, next, &head, list)
    {
        if (pos->value == 5)
        {
            cur = pos;
            break;
        }
    }
    struct example e7, e8;
    e7.value = 7;
    e8.value = 8;
    fprintf(stdout, "prev in = %d cur = %d\n", e7.value, cur->value);
    // // 插入节点到当前节点之前
    list_add_prev(&e7.list, &cur->list);
    fprintf(stdout, "next in = %d cur = %d\n", e8.value, cur->value);
    // // 插入节点到当前节点之后
    list_add_next(&e8.list, &cur->list);

    // 遍历链表
    list_for_each_entry(pos, &head, list)
    {
        fprintf(stdout, "value: %d\n", pos->value);
    }
    fprintf(stdout, "list_del all\n");
    // 遍历删除链表
    list_for_each_entry_safe(pos, next, &head, list)
    {
        fprintf(stdout, "del value: %d\n", pos->value);
        list_del(&pos->list);
        // 如果需要, 在这里释放内存
        // free(pos);
    }
    if (list_empty(&head))
    {
        fprintf(stdout, "list is empty\n");
    }

    return 0;
}

// ➜  list git:(master) ✗ ./exp 
// list_add_head: 1 2 3
// list_add_tail: 4 5 6
// value: 3
// value: 2
// value: 1
// value: 4
// value: 5
// value: 6
// list_del 2
// value: 3
// value: 1
// value: 4
// value: 5
// value: 6
// list_add_prev list_add_next: in = 2 cur = 5
// prev in = 7 cur = 5
// next in = 8 cur = 5
// value: 3
// value: 1
// value: 4
// value: 7
// value: 5
// value: 8
// value: 6
// list_del all
// del value: 3
// del value: 1
// del value: 4
// del value: 7
// del value: 5
// del value: 8
// del value: 6
// list is empty
相关推荐
University of Feriburg1 小时前
4-c语言中的数据类型
linux·c语言·笔记·学习·嵌入式实时数据库·嵌入式软件
小雅痞1 小时前
C语言--统计字符串中最长的单词
c语言
辰辰大美女呀3 小时前
C 语言高级编程指南:回调函数与设计模式
c语言·开发语言·设计模式
梁下轻语的秋缘4 小时前
每日c/c++题 备战蓝桥杯(求解三个数的最大公约数与最小公倍数)
c语言·c++·学习·算法·蓝桥杯
Net_Walke7 小时前
【C语言】container_of 宏定义
c语言
代码AC不AC10 小时前
【数据结构】堆
c语言·数据结构·学习··深度剖析
九离十10 小时前
数据结构(5)——栈
c语言·数据结构
双叶83611 小时前
(51单片机)独立按键控制流水灯LED流向(独立按键教程)(LED使用教程)
c语言·开发语言·数据结构·单片机·嵌入式硬件·游戏·51单片机
Wx120不知道取啥名17 小时前
C语言跳表(Skip List)算法:数据世界的“时光穿梭机”
c语言·数据结构·算法·list·跳表算法