tcp_queue_rcv用于将接收到的skb加入到接收队列receive_queue中,首先会调用tcp_try_coalesce进行分段合并到队列中最后一个skb的尝试,若失败则调用__skb_queue_tail添加该skb到队列尾部;

 static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen,
bool *fragstolen)
{
int eaten; /* 取队尾 */
struct sk_buff *tail = skb_peek_tail(&sk->sk_receive_queue); __skb_pull(skb, hdrlen); /* 尝试进行分段合并 */
eaten = (tail &&
tcp_try_coalesce(sk, tail, skb, fragstolen)) ? : ; /* 更新下一个期望接收的序号 */
tcp_rcv_nxt_update(tcp_sk(sk), TCP_SKB_CB(skb)->end_seq); /* 未合并 */
if (!eaten) {
/* 添加到队列尾部 */
__skb_queue_tail(&sk->sk_receive_queue, skb); /* 关联控制块 */
skb_set_owner_r(skb, sk);
}
return eaten;
}

tcp_try_coalesce函数进行合并数据段操作,若合并成功,则更新CB中的对应字段值;

 static bool tcp_try_coalesce(struct sock *sk,
struct sk_buff *to,
struct sk_buff *from,
bool *fragstolen)
{
int delta; *fragstolen = false; /* Its possible this segment overlaps with prior segment in queue */
/* 序号对不上 */
if (TCP_SKB_CB(from)->seq != TCP_SKB_CB(to)->end_seq)
return false; /* 尝试合并到前一个数据段 */
if (!skb_try_coalesce(to, from, fragstolen, &delta))
return false; /* 调整内存使用 */
atomic_add(delta, &sk->sk_rmem_alloc);
sk_mem_charge(sk, delta);
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPRCVCOALESCE); /* 更新cb相关字段 */
TCP_SKB_CB(to)->end_seq = TCP_SKB_CB(from)->end_seq;
TCP_SKB_CB(to)->ack_seq = TCP_SKB_CB(from)->ack_seq;
TCP_SKB_CB(to)->tcp_flags |= TCP_SKB_CB(from)->tcp_flags;
return true;
}

skb_try_coalesce函数为详细的合并过程,在进行了必要的合并检查之后进行合并;其中当skb线性区域有数据的时候,会将该线性区域处理成frag,并合并到模板skb中;对于非线性区域,则直接进行拷贝,如果是clone的,还需要增加frag的引用计数;合并完成之后,调整skb数据长度值;

 bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
bool *fragstolen, int *delta_truesize)
{
int i, delta, len = from->len; *fragstolen = false; /* 不能为克隆 */
if (skb_cloned(to))
return false; /* to尾部能够容纳得下新数据 */
if (len <= skb_tailroom(to)) {
/* from拷贝到to尾部 */
if (len)
BUG_ON(skb_copy_bits(from, , skb_put(to, len), len));
*delta_truesize = ;
return true;
} /* to或者from有分片 */
if (skb_has_frag_list(to) || skb_has_frag_list(from))
return false; /* 线性缓冲区数据长度不为0 */
if (skb_headlen(from) != ) {
struct page *page;
unsigned int offset; /* 达到最大frags限制 */
if (skb_shinfo(to)->nr_frags +
skb_shinfo(from)->nr_frags >= MAX_SKB_FRAGS)
return false;
/* skb被锁定 */
if (skb_head_is_locked(from))
return false; /* 计算数据增量,去掉头部 */
delta = from->truesize - SKB_DATA_ALIGN(sizeof(struct sk_buff)); /* 找到对应的页和偏移 */
page = virt_to_head_page(from->head);
offset = from->data - (unsigned char *)page_address(page); /* 根据from的页和偏移在to的frags上增加一个frag */
skb_fill_page_desc(to, skb_shinfo(to)->nr_frags,
page, offset, skb_headlen(from));
*fragstolen = true;
} else { /* 达到最大frags限制 */
if (skb_shinfo(to)->nr_frags +
skb_shinfo(from)->nr_frags > MAX_SKB_FRAGS)
return false; /* 计算增量,减掉所有头部和无数据线性区域 */
delta = from->truesize - SKB_TRUESIZE(skb_end_offset(from));
} WARN_ON_ONCE(delta < len); /* 拷贝frags */
memcpy(skb_shinfo(to)->frags + skb_shinfo(to)->nr_frags,
skb_shinfo(from)->frags,
skb_shinfo(from)->nr_frags * sizeof(skb_frag_t));
/* 增加frags数量 */
skb_shinfo(to)->nr_frags += skb_shinfo(from)->nr_frags; /* 不是克隆的,设置from的frags为0 */
if (!skb_cloned(from))
skb_shinfo(from)->nr_frags = ; /* if the skb is not cloned this does nothing
* since we set nr_frags to 0.
*/
/* 克隆的,则需要对frags增加引用 */
for (i = ; i < skb_shinfo(from)->nr_frags; i++)
skb_frag_ref(from, i); /* 总长度加上增量 */
to->truesize += delta; /* 总数据长度增加 */
to->len += len;
/* 非线性数据长度增加 */
to->data_len += len; /* 记录增量 */
*delta_truesize = delta;
return true;
}

TCP输入 之 tcp_queue_rcv的更多相关文章

  1. TCP输入 之 tcp_rcv_established

    概述 tcp_rcv_established用于处理已连接状态下的输入,处理过程根据首部预测字段分为快速路径和慢速路径: 1. 在快路中,对是有有数据负荷进行不同处理: (1) 若无数据,则处理输入a ...

  2. TCP输入 之 tcp_data_queue

    tcp_data_queue作用为数据段的接收处理,其中分为多种情况: (1) 无数据,释放skb,返回: (2) 预期接收的数据段,a. 进行0窗口判断:b. 进程上下文,复制数据到用户空间:c. ...

  3. TCP输入 之 快速路径和慢速路径

    概述 快速路径:用于处理预期的,理想情况下的数据段,在这种情况下,不会对一些边缘情形进行检测,进而达到快速处理的目的: 慢速路径:用于处理那些非预期的,非理想情况下的数据段,即不满足快速路径的情况下数 ...

  4. TCP输入 之 tcp_prequeue

    在未开启tcp_low_latency的情况下,软中断将skb送上来,加入到prequeue中,然后 在未启用tcp_low_latency且有用户进程在读取数据的情况下,skb入队到prequeue ...

  5. TCP输入 之 tcp_v4_rcv

    tcp_v4_rcv函数为TCP的总入口,数据包从IP层传递上来,进入该函数:其协议操作函数结构如下所示,其中handler即为IP层向TCP传递数据包的回调函数,设置为tcp_v4_rcv: sta ...

  6. tcp 输入 简析 转载

    正常来说 TCP 收消息过程会涉及三个队列: Backlog Queue sk->sk_backlog Prequeue tp->ucopy.prequeue Receive Queue  ...

  7. tcp 输入 prequeue以及backlog队列

    /*ipv4_specific是TCP传输层到网络层数据发送以及TCP建立过程的真正OPS, 在tcp_prot->init中被赋值给inet_connection_sock->icsk_ ...

  8. 前端学HTTP之连接管理

    前面的话 HTTP连接是HTTP报文传输的关键通道.要掌握HTTP就需要理解HTTP连接的来龙去脉以及如何使用这些连接 如果想查看一个网页,浏览器收到URL时,会执行下图所示的步骤.将服务器的IP地址 ...

  9. atitit.http原理与概论attilax总结

    atitit.http原理与概论attilax总结 1. 图解HTTP 作者:[日]上野宣 著1 2. HTTP权威指南(国内首本HTTP及其相关核心Web技术权威著作)1 3. TCP/IP详解(中 ...

随机推荐

  1. Spark 源码和应用开发环境的构建

    引言 Spark 现在无疑是大数据领域最热门的技术之一,读者很容易搜索到介绍如何应用 Spark 技术的文章,但是作为开发人员,在了解了应用的概念之后,更习惯的是打开开发环境,开发一些应用来更深入的学 ...

  2. apply()和call()的方法

    apply()和call()的方法的区别 参考文档https://www.cnblogs.com/lengyuehuahun/p/5643625.html 一直都没太明白apply()与call()的 ...

  3. Linux工具- Sysdig

    Sysdig 是一个超级系统工具,比 strace.tcpdump.lsof 加起来还强大.可用来捕获系统状态信息,保存数据并进行过滤和分析.使用 Lua 开发,提供命令行接口以及强大的交互界面. 使 ...

  4. Java高并发程序设计学习笔记(九):锁的优化和注意事项

    转自:https://blog.csdn.net/dataiyangu/article/details/87612028 锁优化的思路和方法减少锁持有时间减小锁粒度锁分离锁粗化举个栗子举个栗子锁消除虚 ...

  5. Spring Boot WebFlux整合mongoDB

    引入maven文件 <dependency> <groupId>org.springframework.boot</groupId> <artifactId& ...

  6. 怎么处理系统蓝屏后提示代码0x000000d1的错误?

    电脑开机有时会出现蓝屏,导致蓝屏的原因有很多,每种错误都有不同的代码.下面就来和大家分享一下电脑开机蓝屏出现0x000000d1错误代码是什么原因?我们又该怎么去解决这个问题. 电脑开机蓝屏出现0x0 ...

  7. kubernetes资源清单之pod

    什么是pod? Pod是一组一个或多个容器(例如Docker容器),具有共享的存储/网络,以及有关如何运行这些容器的规范. Pod的内容始终位于同一地点,并在同一时间安排,并在共享上下文中运行. Po ...

  8. 解决Chrome无法安装CRX离线插件

    解释说明: 谷歌浏览器Chrome,版本号67.0.3396.99,自这个版本后的Chrome,手动拖放插件文件crx到谷歌浏览器,这种安装插件的方式,一定会失败,它会提示“无法从该网站添加应用,扩展 ...

  9. C++ 内联函数 摘自 C++ 应用程序性能优化

    内联函数 在C++语言的设计中,内联函数的引入可以说完全是为了性能的考虑.因此在编写对性能要求比较高的C++程序时,非常有必要仔细考量内联函数的使用. 所谓"内联",即将被调用函数 ...

  10. ubantu32位 linux下hexedit的下载安装

    Hexedit软件介绍: hexedit是一个开源的完全免费的命令行软件,可用于在任何GNU / Linux操作系统下以十六进制和ASCII(美国信息交换标准代码)格式查看和编辑文件. 下载: 在so ...