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从它所在的链表上取下。 
以上的链表操作都是先关中断的。这在中断上下文中是不需要的,所以另外有一套与上面函数同名但是有前缀“__”的函数供运行在中断上下文中的函数调用。

Linux网络协议栈(二)——套接字缓存(socket buffer)的更多相关文章

  1. Linux网络编程——原始套接字实例:MAC 头部报文分析

    通过<Linux网络编程——原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 M ...

  2. Linux网络编程之套接字基础

    1.套接字的基本结构 struct sockaddr 这个结构用来存储套接字地址. 数据定义: struct sockaddr { unsigned short sa_family; /* addre ...

  3. Linux网络编程——原始套接字能干什么?

    通常情况下程序员接所接触到的套接字(Socket)为两类: (1)流式套接字(SOCK_STREAM):一种面向连接的 Socket,针对于面向连接的TCP 服务应用: (2)数据报式套接字(SOCK ...

  4. Linux网络编程——原始套接字编程

    原始套接字编程和之前的 UDP 编程差不多,无非就是创建一个套接字后,通过这个套接字接收数据或者发送数据.区别在于,原始套接字可以自行组装数据包(伪装本地 IP,本地 MAC),可以接收本机网卡上所有 ...

  5. Linux 网络编程 高级套接字

    一.套接字选项: 有以下3中方式可以对套接字选项进行设置: getsockopt 和 setsockopt 函数 fcntl函数 ioctl函数 getsockopt和setsockopt函数用于获得 ...

  6. LINUX 网络编程 原始套接字

    一 原始套接字 原始套接字(SOCK_RAW)是一种不同于SOCK_STREAM.SOCK_DGRAM的套接字,它实现于系统核心.然而,原始套接字能做什么呢?首先来说,普通的套接字无法处理ICMP.I ...

  7. 理解 Linux 网络栈(1):Linux 网络协议栈简单总结

    本系列文章总结 Linux 网络栈,包括: (1)Linux 网络协议栈总结 (2)非虚拟化Linux环境中的网络分段卸载技术 GSO/TSO/UFO/LRO/GRO (3)QEMU/KVM + Vx ...

  8. Linux网络通信(TCP套接字编写,多进程多线程版本)

    预备知识 源IP地址和目的IP地址 IP地址在上一篇博客中也介绍过,它是用来标识网络中不同主机的地址.两台主机进行通信时,发送方需要知道自己往哪一台主机发送,这就需要知道接受方主机的的IP地址,也就是 ...

  9. (十三) [终篇] 一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

随机推荐

  1. hdu 2112

    #include<stdio.h> #include<string.h> #define N 200 #define inf 999999999999 __int64 map[ ...

  2. jmesa应用

    一直以来,都没发现什么好的分页组件,最初时用过displaytag,仔细研究了一下,发现它并没有别人说的那么强大,至少离自己的期望还很远,因此尝试寻找其它新的分页组件,但很久以来都没发现自己满意的.无 ...

  3. C#路径,文件,目录,I/O常见操作汇总

    原文发布时间为:2008-10-25 -- 来源于本人的百度文章 [由搬家工具导入] 路径,文件,目录,I/O常见操作汇总 摘要:     文件操作是程序中非常基础和重要的内容,而路径、文件、目录以及 ...

  4. 关于HTML文件、JS文件、CSS文件

    把JS和CSS脚本写在html里和写在独立文件里有什么区别? 1. 都写在html里是性能最优的方案. 2. 都写在html里是可维护性最差的方案. 3. 分开写在js.css.html是可维护性最有 ...

  5. POJ 2965 The Pilots Brothers' refrigerator【BFS+状压 Or 脑洞】

    题目链接: http://poj.org/problem?id=1753 题意: 给定冰箱门的开关情况,改变一个门则其所在行列的门都会发生改变,求出改变门的最少操作使得最终所有门都是打开状态. 代码: ...

  6. 洛谷 P3811 【模板】乘法逆元

    P3811 [模板]乘法逆元 题目背景 这是一道模板题 题目描述 给定n,p求1~n中所有整数在模p意义下的乘法逆元. 输入输出格式 输入格式: 一行n,p 输出格式: n行,第i行表示i在模p意义下 ...

  7. jmete命令行停止失败的原因分析

    1.在jmeter的master机器上使用如下方式启动远程IP地址2.2.2.2,3.3.3.3上的jmeter slave服务,执行到最后生成报告: sh apache-jmeter-3.1/bin ...

  8. MySQL 资源大全

    干货!MySQL 资源大全 提交 我的留言 加载中 已留言 shlomi-noach 发起维护的 MySQL 资源列表,内容覆盖:分析工具.备份.性能测试.配置.部署.GUI 等. 伯乐在线已在 Gi ...

  9. cf246 ENew Reform (并查集找环)

    Berland has n cities connected by m bidirectional roads. No road connects a city to itself, and each ...

  10. Git撤销&amp;回滚操作

    开发过程中.你肯定会遇到这种场景: 场景一: 糟了.我刚把不想要的代码.commit到本地仓库中了.可是还没有做push操作! 场景二: 彻底完了.刚线上更新的代码出现故障了.须要还原这次提交的代码! ...