linux内核数据结构

1.linux内核链表

链表和数组的对比

数组优于链表的:

  • 1.内存空间占用的少,因为链表节点会附加上一块或两块下一个节点的信息。

  • 2.数组内的数据可随机访问,但链表不具备随机访问性。数组在内存里是连续的空间。链表在内存地址可能是分散的,所以必须通过上一节点中的信息找能找到下一个节点

链表优于数组的:

  • 1.插入与删除的操作.如果数组的中间插入一个元素,那么这个元素后的所有元素的内存地址都要往后移动.删除的话同理.链表只需要更改有必要更改的节点内的节点信息就够了.并不需要更改节点的内存地址.

  • 2.内存地址的利用率方面.不管内存里还有多少空间,如果没办法一次性给出数组所需的要空间,那就会提示内存不足,而链表可以是分散的空间地址.

链表用法示例:

#include <stdio.h>
#include <stdlib.h>
#include "list.h"

struct person
{
    struct list_head list;
    int age;
};

int main(int argc,char **argv)
{
    int i;
    struct person *p;     //新节点等待插入
    struct person head;  //被插入的节点,头结点
    struct list_head *pos;  //链表头

    INIT_LIST_HEAD(&head.list);  //初始化被插入的节点

    for (i = 0;i < 5;i++) {
        p = (struct person *)malloc(sizeof(struct person ));
        p->age=i*10;
        list_add(&p->list,&head.list); //list_add插入节点,注意参数
    }

    list_for_each(pos, &head.list) {   //list_for_each遍历节点,注意参数
        printf("age = %d\n",((struct person *)pos)->age); //注意此时可强制转换,因为链表list的地址,和struct person的地址是一致的
    }

    return 0;
}

输出结果:40 30 20 10 0

注意 :后插入的先打印(类似栈),调用另一个函数list_add_tail时先插入的先打印(类似队列)

上述list_for_each适用于链表list的地址和struct person的地址是一致的情况,若不一致,需要使用list_for_each_entry。list_for_each_entry实际会调用container_of宏,作用是获取包含链表容器结构的地址。

list_for_each_entry(pos,&head.list,list){
           printf("age=%d\n",pos->age);
}

使用 list_del 函数删除节点:

list_for_each_entry(pos,&head.list,list){
 if(pos->age=30) {
           list_del(&pos->list);
           break; //注意需要break,不用break可用函数list_for_each_entry_safe
           }
}
list_for_each_entry(pos,&head.list,list){
            printf("age=%d\n",pos->age);
           }

最后了解一下 list_replace 替换节点,list_move 移除节点,list_for_each_entry_reverse 往前依次遍历。

2.linux内核队列

队列也称为FIFO,FIFO是先进先出的缩写,使用 kfifo_alloc 创建队列示例:

struct kfifo fifo;
int ret;
ret = kfifo_alloc(&fifo,PAGE_SIZE,GFP_KERNEL);
if(ret)
	return ret;

或使用 kfifo_init 指定缓冲区:

void kfifo_init(struct kfifo *fifo, void *buffer, unsigned int size);

使用 kfifo_in 堆入数据:

unsigned int kfifo_in(struct kfifo *fifo, void *from, unsigned int len);
  1. 该函数把from指针所指的len字节数据包括到kfifo所指的队列中,如果成功,则返回推入数据的字节大小。如果队列中的空闲字节小于len,返回值小于len或0。

使用 kfifo_out 摘取数据:

unsigned int kfifo_out(struct kfifo *fifo, void *to, unsigned int len);
  • 该函数从fifo所指向的队列中拷贝处长度为len字节的数据到to所指的缓冲中。

如果仅仅想"偷窥"队列中的数据,而不真想删除它,使用 kfifo_out_peek 方法:

unsigned int kfifo_out_peek(struct kfifo *fifo, void *to, unsigned int len,unsigned offset);
  • 参数offset指向队列中的索引位置

获得用于存储kfifo队列的空间的总体大小(以字节为单位),可调用方法 kfifo_size

static inline __must_check unsigned int kfifo_size(struct kfifo *fifo);

kfifo_len 方法返回kfifo队列中推入的数据大小:

static inline unsigned int kfifo_len(struct kfifo *fifo);

kfifo_avail 得到kfifo队列中还有多少可用空间

static inline __must_check unsigned int kfifo_avail(struct kfifo *fifo);

kfifo_is_emptykfifo_is_full。如果给定的kfifo分别是空或者满,它们返回非0值。
kfifo_reset 重置kfifo,意味着抛弃所有队列中的内容。
kfifo_free撤销一个使用kfifo_alloc()分配的队列。

3.linux hash 链表

哈希链表的用法与链表很相似,只不过将链表的表头hlist_head放在一个hash散列表(数组)中,表头和节点的数据结构定义如下:

struct hlist_head {
    struct hlist_node *first;
}
struct hlist_node {  
    struct hlist_node *next,**pprev;
}

注意表头里面只存放一个hlist_node的指针,指向链表。
程序示例:

#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "hlistdemo.h"
 
#define log(fmt, arg...) printf(""fmt,##arg)
#define MAX_ADDR 256
 
void main(int argc, char **argv){
    struct hlist_head htable[MAX_ADDR];     //表头hash数组
    struct hdata_node *hnode = NULL;        //父结构体
    struct hlist_node *hlist = NULL, *n = NULL; //链表节点
    int i = 0, quit = 1, opt = 0, key;
    unsigned int data;

    for(i = 0; i < MAX_ADDR; i++)       
        INIT_HLIST_HEAD(&htable[i]);      //表头hash数组动态初始化
        
    log("*********************\n\n"
        "input options:\n"
        "1: insert\n"           //插入
        "2: serach\n"           //查询
        "3: delete\n"           //删除
        "0: quit\n"
        "\n*********************\n");  
        
    while(quit != 0){
        log("\ninput options:");
        scanf("%d",  &opt);
        switch(opt){
            case  1:  //插入
                hnode = calloc(1, sizeof(struct hdata_node)); //分配父结构体节点内存
                if(!hnode){
                    log("insert fail \n");
                    break;
                }
                INIT_HLIST_NODE(&hnode->list); //父结构体结点初始化
                log("\ninput data:");
                scanf("%d",  &hnode->data);
                key = hnode->data % MAX_ADDR;
                hlist_add_head(&hnode->list, &htable[key]); //添加到链表首部 hlist_add_head
                break;
 
            case  2:    //查询
                log("\ninput data:");
                scanf("%d",  &data);         
                key = data % MAX_ADDR;
                if(hlist_empty(&htable[key]))
                    log("data not exist \n");
                else{
                    //遍历对应的槽位,匹配值就打印
                    hlist_for_each_entry(hnode, hlist, &htable[key], list){
                        if(hnode->data == data)
                            log("find data : %u \n", hnode->data);
                    }
                }                
                break;
 
           
            case  3:  //删除
                log("\ninput data:");
                scanf("%d",  &data);         
                key = data % MAX_ADDR;
                if(hlist_empty(&htable[key]))
                    log("data not exist ,delete fail \n");
                else{
                    //遍历对应的槽,匹配值就删除
                    hlist_for_each_entry_safe(hnode, hlist, n, &htable[key], list){
                        if(hnode->data == data){
                            hlist_del(&hnode->list);
                            break;
                        }
                    }
                }                            
                log("delete fail\n");                
                break;
            case 0:
                quit = 0;
                break;
            default:
                log("unrecognized option!");
                break;                
        }
    }
    //退出程序前释放资源
    for(i=0; i < MAX_ADDR; i++){
        //遍历每一个槽,有结点就删除
        hlist_for_each_entry_safe(hnode, hlist, n, &htable[i], list){           
                hlist_del(&hnode->list); 
                log("delete %u \n", hnode->data);
                free(hnode);
                hnode = NULL;
        }
    }
    log("exit\n");
}

4.树结构

参考 https://xiaozhuanlan.com/topic/5036471892

5.映射

作用:将标识数UID与指针联系起来。
示例代码:

struct idr id_huh;
idr_init(&id_huh);  //初始化idr结构体
int id;
do {
    if (!idr_pre_get(&idr_huh, GFP_KERNEL)) //调整后备树大小
        return -ENOSPC;
    ret = idr_get_new(&idr_huh, ptr, &id);  //将id和ptr指针关联起来
   } while(ret == -EAGAIN);

查找id:

void *idr_find(struct idr *idp, int id);   //查找id对应的指针

移除id:

void idr_remove(struct idr *idp, int id);   //移除id

释放idr:

void idr_destroy(struct idr *idp);  //释放idr中未使用的内存
void idr_remove_all(struct idr *idp);   //移除所有id
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值