当前位置:首页>Linux>Linux内核双向环形队列skb_queue0x00

Linux内核双向环形队列skb_queue0x00

  • 2026-10-02 06:34:22
Linux内核双向环形队列skb_queue0x00

一、一些概念

1.skb_queue是Linux内核网络子系统中管理sk_buff(Socket Buffer,网络数据包)的双向环形队列,由struct sk_buff_head 管理,带自旋锁保护并发访问。

2.核心结构:

二、常用操作带锁或不带锁(在include/linux/skbuff.h中定义)

1.初始化

//初始化队列头(带锁初始化)voidskb_queue_head_init(struct sk_buff_head *list);//仅初始化链表指针,不初始化锁(底层用)void __skb_queue_head_init(struct sk_buff_head *list);

2.入队(带锁版本,安全)

//队头插入(优先处理)voidskb_queue_head(struct sk_buff_head *list, struct sk_buff *newskb);//队尾插入(FIFO)voidskb_queue_tail(struct sk_buff_head *list, struct sk_buff *newskb);底层无锁版本:__skb_queue_head / __skb_queue_tail(需自行加锁)。

3.出队(带锁版本,安全)

//从队头取出并移除skbstruct sk_buff *skb_dequeue(struct sk_buff_head *list);//从队尾取出并移除skbstruct sk_buff *skb_dequeue_tail(struct sk_buff_head *list);底层无锁版本:__skb_dequeue / __skb_dequeue_tail

4.查看(不修改队列)

//查看队头skb(不取出)struct sk_buff *skb_peek(const struct sk_buff_head *list);//查看队尾skb(不取出)struct sk_buff *skb_peek_tail(const struct sk_buff_head *list);//队列是否为空intskb_queue_empty(conststruct sk_buff_head *list);//获取队列长度__u32 skb_queue_len(conststruct sk_buff_head *list);

5.清空队列

//清空队列,释放所有skb(带锁)voidskb_queue_purge(struct sk_buff_head *list);//底层无锁版本(需自行加锁)void __skb_queue_purge(struct sk_buff_head *list);

6. 遍历队列(安全)

//遍历宏(需持有锁)struct sk_buff *skb;skb_queue_walk(list, skb) {    //处理skb}//安全遍历(允许遍历中删除)skb_queue_walk_safe(list, skb, tmp) {    //处理skb}

三、测试

1.源码

skb_queue_TestDrv.c

#include<linux/module.h>#include<linux/kernel.h>#include<linux/skbuff.h>#include<linux/delay.h>//定义一个全局skb队列static struct sk_buff_head g_skb_queue;//构造一个测试用skbstatic struct sk_buff *alloc_test_skb(int pkt_id){    struct sk_buff *skb;    unsigned char *data;    int len = 64;    skb = alloc_skb(len, GFP_KERNEL);    if (!skb)        return NULL;    //预留头部空间    skb_reserve(skb, 32);    //挂载数据    data = skb_put(skb, len);    snprintf(data, len, "test_packet_%d", pkt_id);    //识别    skb->cb[0] = pkt_id;    return skb;}//遍历并打印队列内容(安全遍历)staticvoidshow_queue(constchar *info){    struct sk_buff *skb;    printk(KERN_INFO "=== %s (qlen = %u) ===\n", info, skb_queue_len(&g_skb_queue));    skb_queue_walk(&g_skb_queue, skb) {        printk(KERN_INFO "  skb id: %d, data: %s\n",               (int)skb->cb[0], skb->data);    }}staticint __init skb_queue_demo_init(void){    struct sk_buff *skb;    int i;    printk(KERN_INFO "skb_queue demo init\n");    //1.初始化队列(含锁初始化)    skb_queue_head_init(&g_skb_queue);    //2.入队3个包(尾插:FIFO)    for (i = 1; i <= 3; i++) {        skb = alloc_test_skb(i);        if (skb) {            skb_queue_tail(&g_skb_queue, skb);            printk(KERN_INFO "enqueue skb id %d\n", i);        }    }    show_queue("after enqueue 3 packets");    //3.从头部出队一个    skb = skb_dequeue(&g_skb_queue);    if (skb) {        printk(KERN_INFO "dequeue skb id %d\n", (int)skb->cb[0]);        kfree_skb(skb); // 必须释放    }    show_queue("after dequeue 1 packet");    //4.头部再插入一个(优先级更高)    skb = alloc_test_skb(99);    if (skb) {        skb_queue_head(&g_skb_queue, skb);        printk(KERN_INFO "queue_head insert skb 99\n");    }    show_queue("after insert to head");    //5.批量出队处理    printk(KERN_INFO "=== start dequeue all ===\n");    while ((skb = skb_dequeue(&g_skb_queue))) {        printk(KERN_INFO "process skb id %d\n", (int)skb->cb[0]);        kfree_skb(skb);    }    show_queue("after dequeue all");    return 0;}staticvoid __exit skb_queue_demo_exit(void){    // 清空队列(防止退出时泄漏)    skb_queue_purge(&g_skb_queue);    printk(KERN_INFO "skb_queue demo exit\n");}module_init(skb_queue_demo_init);module_exit(skb_queue_demo_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("AXUN");MODULE_DESCRIPTION("skb_queue demo");

2.Makefile文件

CC = gcc-12  # 新增:指定编译器为gcc-12obj-m += skb_queue_TestDrv.o  # 要编译的模块名(对应 skb_queue_TestDrv.c)KERNELDIR ?= /lib/modules/$(shell uname -r)/build  #内核源码路径PWD := $(shell pwd)  #当前目录all:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules  #编译模块clean:$(MAKE) -C $(KERNELDIR) M=$(PWD) clean  #清理编译产物

3.编译及加载模块(参考第一个驱动程序开发)

最新文章

随机文章