linux Socket 缓存 介绍

重要成员变量

这里介绍的成员是驱动可能需要存取的. 以非特别的顺序列出它们.

struct net_device *dev;

接收或发送这个缓存的设备

union { /* ... */ } h;

union { /* ... */ } nh;

union { /*... */} mac;

指向报文中包含的各级的头的指针. union 中的某个成员都是一个不同数据结构类

型的指针. h 含有传输层头部指针(例如, struct tcphdr *th); nh 包含网络层头

部(例如 struct iphdr *iph); 以及 mac 包含链路层头部指针(例如 struct

ethkr * ethernet).

如果你的驱动需要查看 TCP 报文的源和目的地址, 可以在 skb->h.th 中找到. 看

头文件来找到全部的可以这样存取的头部类型.

注意网络驱动负责设置进入报文的 mac 指针. 这个任务正常是由 eth_type_trans

处理, 但是 非以太网驱动不得不直接设置 skb->mac.raw, 如同"非以太网头部"一

节所示.

unsigned char *head;

unsigned char *data;

unsigned char *tail;

unsigned char *end;

用来寻址报文中数据的指针. head 指向分配内存的开始, data 是有效字节的开始

(并且常常稍微比 head 大一些), tail 是有效字节的结尾, end 指向 tail 能够

到达的最大地址. 查看它的另一个方法是可用缓存空间是 skb->end - skb->head,

当前使用的空间是 skb->tail - skb->data.

unsigned int len;

unsigned int data_len;

len 是报文中全部数据的长度, 而 data_len 是报文存储于单个片中的部分的长度.

除非使用发散/汇聚 I/O, data_len 成员的值为 0.

unsigned char ip_summed;

这个报文的校验和策略. 由驱动在进入报文上设置这个成员, 如在"报文接收"一节

中描述的.

unsigned char pkt_type;

在递送中使用的报文分类. 驱动负责设置它为 PACKET_HOST (报文是给自己的),

PACKET_OTHERHOST (不, 这个报文不是给我的), PACKET_BROADCAST, 或者

PACKET_MULTICAST. 以太网驱动不显式修改 pkt_type, 因为 eth_type_trans 为

它们做.

shinfo(struct sk_buff *skb);

unsigned int shinfo(skb)->nr_frags;

skb_frag_t shinfo(skb)->frags;

由于性能的原因, 有些 skb 信息存储于一个分开的结构中, 它在内存中紧接着

skb. 这个"shared info"(这样命名是因为它可以在网络代码中多个 skb 拷贝中共

享)必须通过 shinfo 宏定义来存取. 这个结构中有几个成员, 但是大部分超出本

书的范围. 我们在"发散/汇聚 I/O"一节中见过 nr_frags 和 frags.

在结构中剩下的成员不是特别有趣. 它们用来维护缓存列表, 来统计 socket 拥有的缓存

大小, 等等.

作用于 socket 缓存的函数

使用一个 sk_buff 结构的网络驱动利用正式接口函数来操作它. 许多函数操作一个

socket 缓存; 这里是最有趣的几个:

struct sk_buff *alloc_skb(unsigned int len, int priority);

struct sk_buff *dev_alloc_skb(unsigned int len);

分配一个缓存区. alloc_skb 函数分配一个缓存并且将 skb->data 和 skb->tail

都初始化成 skb->head. dev_alloc_skb 函数是使用 GFP_ATOMIC 优先级调用

alloc_skb 的快捷方法, 并且在 skb->head 和 skb->data 之间保留了一些空间.

这个数据空间用在网络层之间的优化, 驱动不要动它.

void kfree_skb(struct sk_buff *skb);

void dev_kfree_skb(struct sk_buff *skb);

void dev_kfree_skb_irq(struct sk_buff *skb);

void dev_kfree_skb_any(struct sk_buff *skb);

释放缓存. kfree_skb 调用由内核在内部使用. 一个驱动应当使用一种

dev_kfree_skb 的变体: 在非中断上下文中使用 dev_kfree_skb, 在中断上下文中

使用 dev_kfree_skb_irq, 或者 dev_kfree_skb_any 在任何 2 种情况下.

unsigned char *skb_put(struct sk_buff *skb, int len);

unsigned char *__skb_put(struct sk_buff *skb, int len);

更新 sk_buff 结构中的 tail 和 len 成员; 它们用来增加数据到缓存的结尾, 每

个函数的返回值是 skb->tail 的前一个值(换句话说, 它指向刚刚创建的数据空

间). 驱动可以使用返回值通过引用 memcpy(skb_put(...), data, len) 来拷贝数

据或者一个等同的东东. 两个函数的区别在于 skb_put 检查以确认数据适合缓存,

而 __skb_put 省略这个检查.

unsigned char *skb_push(struct sk_buff *skb, int len);

unsigned char *__skb_push(struct sk_buff *skb, int len);

递减 skb->data 和递增 skb->len 的函数. 它们与 skb_put 相似, 除了数据是添

加到报文的开始而不是结尾. 返回值指向刚刚创建的数据空间. 这些函数用来在发

送报文之前添加一个硬件头部. 又一次, __skb_push 不同在它不检查空间是否足

够.

int skb_tailroom(struct sk_buff *skb);

返回可以在缓存中放置数据的可用空间数量. 如果驱动放了多于它能持有的数据到

缓存中, 系统傻掉. 尽管你可能反对说一个 printk 会足够来标识出这个错误, 内

存破坏对系统是非常有害的以至于开发者决定采取确定的动作. 实际中, 你不该需

要检查可用空间, 如果缓存被正确地分配了. 因为驱动常常在分配缓存前获知报文

的大小, 只有一个严重坏掉的驱动会在缓存中安放太多的数据, 这样出乱子就可当

作一个应得的惩罚.

int skb_headroom(struct sk_buff *skb);

返回 data 前面的可用空间数量, 就是, 可以 "push" 给缓存多少字节.

void skb_reserve(struct sk_buff *skb, int len);

递增 data 和 tail. 这个函数可用来在填充数据前保留空间. 大部分以太网接口

保留 2 个字节在报文的前面; 因此, IP 头对齐到 16 字节, 在 14 字节的以太网

头后面. snull 也这样做, 尽管没有在"报文接收"一节中展现这个指令以避免在那

时引入过多概念.

unsigned char *skb_pull(struct sk_buff *skb, int len);

从报文的头部去除数据. 驱动不会需要使用这个函数, 但是为完整而包含在这儿.

它递减 skb->len 和递增 skb->data; 这是硬件头如何从进入报文开始被剥离.

int skb_is_nonlinear(struct sk_buff *skb);

返回一个真值, 如果这个 skb 分离为多个片为发散/汇聚 I/O.

int skb_headlen(struct sk_buff *skb);

返回 skb 的第一个片的长度(由 skb->data 指着).

void *kmap_skb_frag(skb_frag_t *frag);

void kunmap_skb_frag(void *vaddr);

如果你必须从内核中的一个非线性 skb 直接存取片, 这些函数为你映射以及去映

射它们. 使用一个原子性 kmap, 因此你不能一次映射多于一个片.

内核定义了几个其他的作用于 socket 缓存的函数, 但是它们是打算用于高层网络代码,

驱动不需要它们.

相关推荐
ice___Cpu1 小时前
Linux 基本使用和 web 程序部署 ( 8000 字 Linux 入门 )
linux·运维·前端
z202305081 小时前
linux 之0号进程、1号进程、2号进程
linux·运维·服务器
Happy鱿鱼2 小时前
C语言-数据结构 有向图拓扑排序TopologicalSort(邻接表存储)
c语言·开发语言·数据结构
KBDYD10102 小时前
C语言--结构体变量和数组的定义、初始化、赋值
c语言·开发语言·数据结构·算法
LWDlwd05252 小时前
shell指令及笔试题
c语言
狐心kitsune2 小时前
erlang学习:Linux常用命令1
linux·学习·erlang
Crossoads2 小时前
【数据结构】排序算法---桶排序
c语言·开发语言·数据结构·算法·排序算法
DREAM依旧3 小时前
《深入了解 Linux 操作系统》
linux
QXH2000003 小时前
数据结构—单链表
c语言·开发语言·数据结构
David猪大卫3 小时前
数据结构修炼——顺序表和链表的区别与联系
c语言·数据结构·学习·算法·leetcode·链表·蓝桥杯