Linux网络协议栈(二)——套接字缓存(socket buffer)

简介: Linux网络核心数据结构是套接字缓存(socket buffer),简称skb。它代表一个要发送或处理的报文,并贯穿于整个协议栈。1、    套接字缓存skb由两部分组成:(1)    报文数据:它保存了实际在网络中传输的数据;(2)    管理数据:供内核处理报文的额外数据,这些数据构成了协议之间交换的控制信息。

Linux网络核心数据结构是套接字缓存(socket buffer),简称skb。它代表一个要发送或处理的报文,并贯穿于整个协议栈。
1、    套接字缓存
skb由两部分组成:
(1)    报文数据:它保存了实际在网络中传输的数据;
(2)    管理数据:供内核处理报文的额外数据,这些数据构成了协议之间交换的控制信息。
当应用程序向一个socket传输数据之后,该socket将创建相应的套接字缓存,并将用户数据拷贝到缓存中。当报文在各协议层传达输的过程中,每一导的报文头将插入到用户数据之前。skb为报文头申请了足够的空间,所以避免了由于插入报文头而对报文进行多次拷贝。用户数据只拷贝了两次:一是从用户空间拷贝到内核;二是报文数据从内核传送到网络适配器。
1.1、sk_buff
套接字缓存结构:

Code

1.2、与sk_buff相关的函数
与sk_buff相关的函数涉及到网络报文存储结构和控制结构的分配、复制、释放,以及控制结构里的各指针的操作,还有各种标志的检查。重要的函数说明如下: 

struct sk_buff *alloc_skb(unsigned int size,int gfp_mask) 
分配大小为size的存储空间存放网络报文,同时分配它的控制结构。size的值是16字节对齐的,gfp_mask是内存分配的优先级。常见的内存分配优先级有GFP_ATOMIC,代表分配过程不能被中断,一般用于中断上下文中分配内存;GFP_KERNEL,代表分配过程可以被中断,相应的分配请求被放到等待队列中。分配成功之后,因为还没有存放具体的网络报文,所以sk_buff的 data,tail指针都指向存储空间的起始地址,len的大小为0,而且 is_clone和cloned两个标记的值都是0。 

struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask) 
从控制结构skb中 clone出一个新的控制结构,它们都指向同一个网络报文。clone成功之后,将新的控制结构和原来的控制结构的 is_clone,cloned两个标记都置位。同时还增加网络报文的引用计数(这个引用计数存放在存储空间的结束地址的内存中,由函数atomic_t *skb_datarefp(struct sk_buff *skb)访问,引用计数记录了这个存储空间有多少个控制结构)。由于存在多个控制结构指向同一个存储空间的情况,所以在修改存储空间里面的内容时,先要确定这个存储空间的引用计数为1,或者用下面的拷贝函数复制一个新的存储空间,然后才可以修改它里面的内容。 

struct sk_buff *skb_copy(struct sk_buff *skb, int gfp_mask) 
复制控制结构skb和它所指的存储空间的内容。复制成功之后,新的控制结构和存储空间与原来的控制结构和存储空间相对独立。所以新的控制结构里的is_clone,cloned两个标记都是0,而且新的存储空间的引用计数是1。
 
void kfree_skb(struct sk_buff *skb)
释放控制结构skb和它所指的存储空间。由于一个存储空间可以有多个控制结构,所以只有在存储空间的引用计数为1的情况下才释放存储空间,一般情况下,只释放控制结构skb。 

unsigned char *skb_put(struct sk_buff *skb, unsigned int len) 
将tail指针下移,并增加skb的 len值。data和 tail之间的空间就是可以存放网络报文的空间。这个操作增加了可以存储网络报文的空间,但是增加不能使tail的值大于end的值,skb的 len值大于truesize的值。 

unsigned char *skb_push(struct sk_buff *skb, unsigned int len) 
将data指针上移,并增加skb的 len值。这个操作在存储空间的头部增加了一段可以存储网络报文的空间,上一个操作在存储空间的尾部增加了一段可以存储网络报文的空间。但是增加不能使data的值小于head的值,skb的 len值大于truesize的值。 

unsigned char * skb_pull(struct sk_buff *skb, unsigned int len) 
将data指针下移,并减小skb的 len值。这个操作使data指针指向下一层网络报文的头部。 

void skb_reserve(struct sk_buff *skb, unsigned int len) 
将data指针和tail指针同时下移。这个操作在存储空间的头部预留 len长度的空隙。
 
void skb_trim(struct sk_buff *skb, unsigned int len) 
将网络报文的长度缩减到 len。这个操作丢弃了网络报文尾部的填充值。 

int skb_cloned(struct sk_buff *skb) 
判断skb是否是一个 clone的控制结构。如果是clone的,它的cloned标记是1,而且它指向的存储空间的引用计数大于1。

2、    套接字缓存队列(Socket-Buffer Queues)
2.1、sk_buff_head
在网络协议栈的实现中,有时需要把许多网络报文放到一个队列中做异步处
理。LINUX 为此定义了相关的数据结构 sk_buff_head。这是一个双向链表的
头,它把sk_buff链接成一个双向链表。

复制代码
//套接字缓存队列头
struct sk_buff_head {
    /* These two members must be first. */
    struct sk_buff    *next;
    struct sk_buff    *prev;

    __u32        qlen; //队列的长度,即队列中报文的数量
    spinlock_t    lock;
};
复制代码

2.2、与 sk_buff_head相关的函数
void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk) 
将newsk加到链表 list的头部。 

void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
将newsk加到链表 list的尾部。 

struct sk_buff *skb_dequeue(struct sk_buff_head *list) 
从链表 list的头部取下一个 sk_buff。 

struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list) 
从链表 list的尾部取下一个 sk_buff。
 
skb_insert(struct sk_buff *old, struct sk_buff *newsk) 
将newsk加到old所在的链表上,并且 newsk在old的前面。
 
void skb_append(struct sk_buff *old, struct sk_buff *newsk) 
将newsk加到old所在的链表上,并且 newsk在old的后面。
 
void skb_unlink(struct sk_buff *skb) 
将skb从它所在的链表上取下。 
以上的链表操作都是先关中断的。这在中断上下文中是不需要的,所以另外有一套与上面函数同名但是有前缀“__”的函数供运行在中断上下文中的函数调用。

目录
相关文章
|
12天前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
95 6
|
13天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
50 3
|
13天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
38 2
|
20天前
|
缓存 监控 Linux
|
24天前
|
Linux Shell 数据安全/隐私保护
|
25天前
|
域名解析 网络协议 安全
|
7天前
|
运维 监控 网络协议
运维工程师日常工作中最常用的20个Linux命令,涵盖文件操作、目录管理、权限设置、系统监控等方面
本文介绍了运维工程师日常工作中最常用的20个Linux命令,涵盖文件操作、目录管理、权限设置、系统监控等方面,旨在帮助读者提高工作效率。从基本的文件查看与编辑,到高级的网络配置与安全管理,这些命令是运维工作中的必备工具。
35 3
|
2月前
|
运维 监控 网络协议
|
13天前
|
安全 网络协议 Linux
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。通过掌握 ping 命令,读者可以轻松测试网络连通性、诊断网络问题并提升网络管理能力。
46 3
下一篇
无影云桌面