【C语言】linux内核tcp_write_xmit和tcp_write_queue_purge

tcp_write_xmit

一、讲解

这个函数 tcp_write_xmit 是Linux内核TCP协议栈中的一部分,其基本作用是发送数据包到网络。这个函数会根据不同情况推进发送队列的头部,确保只要远程窗口有空间,就可以发送数据。
下面是对该函数的一些主要逻辑的中文解释:
1. 初始化:函数开头有一些初始化操作,比如用 tcp_mstamp_refresh(tp) 刷新时间戳,`tcp_mtu_probe(sk)` 尝试路径MTU发现过程(如果适用),以及 max_segs 的计算。
2. 循环发送:接下来,函数进入一个while循环,尝试发送所有已经排队的SKB(socket buffer)。循环的每一次迭代都会尝试发送一个SKB,直到没有更多可以发送的,或者遇到问题而中断。
3. 发送条件检查:在每次尝试发送前,会进行一系列条件检查:
   - tcp_pacing_check(sk) 检查是否应该基于网络拥塞避免算法暂时停止发送。
   - tcp_cwnd_test(tp, skb) 和 tcp_snd_wnd_test(tp, skb, mss_now) 分别检查拥塞窗口(cwnd)和发送窗口(snd_wnd),以确保我们没有发送超出对方TCP流控制和拥塞控制的数据。
4. 确定发送大小:对于要发送的SKB,它会计算出可以一次发送多少数据(TSO分段,即TCP段上合并发送),以及是否应该延迟发送,从而进行网络流量整形。
5. 发送和处理:如果所有条件均符合,就会通过 tcp_transmit_skb(sk, skb, 1, gfp) 将SKB发送到网络。发送后,会进行一些更新,比如更新拥塞窗口相关数据。
6. 结束条件:如果在发送过程中遇到资源限制(如拥塞窗口满了,或者接收窗口满了),就会跳出发送循环。
7. 后处理:函数的最后部分会基于发送情况更新一些计时器,比如记录流控制限制的时间,决定是否触发进一步的丢包探测等。
整个函数的设计关注于什么时候发送数据,以及如何基于当前网络条件(例如拥塞控制、窗口大小等)做出正确的发送决策。这是一个TCP协议中用于管理数据发送的核心路径,确保数据以有效和合理的方式在网络中传输。

二、中文注释

/* 这个函数用于将数据包写入网络,并推进发送队列头部。这个操作发生在* 接收到的确认(ACK)扩展了远程窗口时。** LARGESEND注释:!tcp_urg_mode是过度的限制,实际上,只有从snd_up-64k-mss到snd_up* 这段范围内的帧不能是大型帧。考虑到紧急(URG)数据的使用比较少,这不是一个严重的问题。** 当push_one > 0时,最多发送一个数据包。当push_one == 2时,暂时忽略拥塞窗口(cwnd)的限制,* 强制至多发送一个数据包。** 如果没有在传输中的分段(即所有分段都已确认),且我们有排队的分段,* 但现在由于轻微的发送窗口(SWS)问题或其他问题而无法发送任何分段,* 则返回true。*/
static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,int push_one, gfp_t gfp)
{struct tcp_sock *tp = tcp_sk(sk); // 获取TCP套接字结构体struct sk_buff *skb; // 定义一个套接字缓冲区指针unsigned int tso_segs, sent_pkts; // 定义传输段的数量和已发送的数据包数int cwnd_quota; // 定义拥塞窗口配额int result; // 定义结果变量bool is_cwnd_limited = false, is_rwnd_limited = false; // 定义拥塞窗口和接收窗口限制标记u32 max_segs; // 定义最大段数sent_pkts = 0; // 初始化已发送的数据包数量tcp_mstamp_refresh(tp); // 刷新时间戳if (!push_one) {/* 执行MTU探测 */result = tcp_mtu_probe(sk);if (!result) {return false;} else if (result > 0) {sent_pkts = 1;}}max_segs = tcp_tso_segs(sk, mss_now); // 计算最大可发送的段数while ((skb = tcp_send_head(sk))) { // 遍历发送队列unsigned int limit; // 定义发送限制if (tcp_pacing_check(sk)) // 执行流量整形检查break;tso_segs = tcp_init_tso_segs(skb, mss_now); // 初始化TSO段BUG_ON(!tso_segs); // 如果tso_segs为0,触发BUGif (unlikely(tp->repair) && tp->repair_queue == TCP_SEND_QUEUE) {/* 将"skb_mstamp"用作重传计时器的起始点 */tcp_update_skb_after_send(tp, skb);goto repair; // 跳过网络传输}cwnd_quota = tcp_cwnd_test(tp, skb); // 检查拥塞窗口if (!cwnd_quota) {if (push_one == 2)/* 发送一个丢失探针包 */cwnd_quota = 1;elsebreak;}if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now))) {is_rwnd_limited = true; // 判断接收窗口是否限制了发送break;}if (tso_segs == 1) {if (unlikely(!tcp_nagle_test(tp, skb, mss_now,(tcp_skb_is_last(sk, skb) ?nonagle : TCP_NAGLE_PUSH))))break;} else {if (!push_one &&tcp_tso_should_defer(sk, skb, &is_cwnd_limited,&is_rwnd_limited, max_segs))break;}limit = mss_now;if (tso_segs > 1 && !tcp_urg_mode(tp))limit = tcp_mss_split_point(sk, skb, mss_now,min_t(unsigned int,cwnd_quota,max_segs),nonagle);if (skb->len > limit &&unlikely(tso_fragment(sk, TCP_FRAG_IN_WRITE_QUEUE,skb, limit, mss_now, gfp)))break;if (tcp_small_queue_check(sk, skb, 0)) // 小队列检查,防止发送队列过长break;if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp))) // 尝试传输skbbreak;
repair:
/* 推进发送队列头部,这个skb已经被发送出去。* 这个调用将增加packets_out的计数。*/tcp_event_new_data_sent(sk, skb);tcp_minshall_update(tp, mss_now, skb); // 更新发送窗口大小sent_pkts += tcp_skb_pcount(skb); // 累加已发送的数据包计数if (push_one) // 如果设置了push_one标志,只发送一个包,然后停止处理break;}if (is_rwnd_limited) // 如果接收窗口限制了速度,则启动相应的计时器tcp_chrono_start(sk, TCP_CHRONO_RWND_LIMITED);else // 否则,停止该计时器tcp_chrono_stop(sk, TCP_CHRONO_RWND_LIMITED);if (likely(sent_pkts)) { // 如果发送出去了数据包if (tcp_in_cwnd_reduction(sk)) // 如果TCP处于拥塞窗口减少状态tp->prr_out += sent_pkts; // 更新Proportional Rate Reduction的计数/* 每次尾部丢包事件只发送一个丢包探测。 */if (push_one != 2)tcp_schedule_loss_probe(sk, false); // 计划发送丢包探测is_cwnd_limited |= (tcp_packets_in_flight(tp) >= tp->snd_cwnd); // 检查是否受到拥塞窗口的限制tcp_cwnd_validate(sk, is_cwnd_limited); // 验证拥塞窗口限制是否处于激活状态return false;}return !tp->packets_out && !tcp_write_queue_empty(sk); // 如果没有在传输中的数据包,并且写队列不为空,返回true
}

这段代码是Linux内核中用于TCP协议的数据包发送机制的一部分,主要负责在可能的情况下将数据包或数据段发送到网络。在这个过程中,它或许会遇到多种情况,如拥塞窗口(cwnd)限制、接收窗口(rwnd)限制、网络层的流量控制、MTU探测等,并且会相应地更新状态信息(例如发送计时器)和内部计数器。如果确定无法发送数据(例如由于发送窗口关闭等原因),函数可能会返回true,表示有待发送的数据但目前无法发送。在每次有效发送后,它还会安排适当的丢失探测和拥塞窗口更新。

tcp_write_queue_purge

一、讲解

这个函数 tcp_write_queue_purge 是针对 TCP 协议在 Linux 内核网络栈中的一个函数,用于清除指定 socket (sk) 写队列中的所有 sk_buff 结构(即待发送数据包)。
具体功能如下:
1. tcp_chrono_stop(sk, TCP_CHRONO_BUSY);
   停止针对 socket sk 的 TCP_CHRONO_BUSY 计时器。该计时器用于衡量 socket 处于忙碌状态的时间。
2. while ((skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) {
   这是一个 while 循环,从 socket sk 的写队列(sk_write_queue)中逐个取出 sk_buff(数据包)。
3. tcp_skb_tsorted_anchor_cleanup(skb);
   清理与给定 sk_buff 相关的时间排序元数据。
4. sk_wmem_free_skb(sk, skb);
   释放刚从队列中取出的 sk_buff 的内存,并调整 socket sk 的写内存计数器。
5. tcp_rtx_queue_purge(sk);
   清空重传队列,释放所有在 TCP 重传队列中的 sk_buff 结构。
6. INIT_LIST_HEAD(&tcp_sk(sk)->tsorted_sent_queue);
   初始化 socket sk 的 ts_sorted_sent_queue 链表头。该链表管理时间排序的已发送数据包队列。
7. sk_mem_reclaim(sk);
   尝试回收 socket sk 的内存,根据当前内存使用情况,可能会释放或重新调整部分内存资源。
8. tcp_clear_all_retrans_hints(tcp_sk(sk));
   清除全部 retrans hint 标记,这些标记用于优化数据包的重传处理。
9. tcp_sk(sk)->packets_out = 0;
   将 socket sk 的 packets_out 计数器重置为 0,这个计数器记录了在飞数据包的数目(在路上但尚未确认的数据包)。
10. inet_csk(sk)->icsk_backoff = 0;
    重置 socket sk 的指数退避计数器 icsk_backoff 到 0。在遇到网络拥堵导致超时重传时,该计数器值会增加。
总体来说,这个函数的作用是清理给定 socket sk 的所有已排队但尚未发送的数据包,确保在某些情况下(例如关闭连接前)发送队列被适当清空,从而释放相关资源。

二、中文注释

void tcp_write_queue_purge(struct sock *sk)
{struct sk_buff *skb;// 停止TCP的计时器(例如,忙时计时器)tcp_chrono_stop(sk, TCP_CHRONO_BUSY);// 循环,一直到写队列为空while ((skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) {// 清理已排序发送队列的anchor(锚)信息tcp_skb_tsorted_anchor_cleanup(skb);// 释放skb占用的写缓冲区内存sk_wmem_free_skb(sk, skb);}// 清除重传队列tcp_rtx_queue_purge(sk);// 初始化TCP控制块的已排序发送队列头部INIT_LIST_HEAD(&tcp_sk(sk)->tsorted_sent_queue);// 试图回收sk_buff结构所占用的内存sk_mem_reclaim(sk);// 清除所有用于快速重传的标志tcp_clear_all_retrans_hints(tcp_sk(sk));// 将"出站数据包数量"计数器设置为0tcp_sk(sk)->packets_out = 0;// 将网络传输层的退避级别设为0inet_csk(sk)->icsk_backoff = 0;
}

以上是该函数的中文注释。函数的功能是清理TCP套接字的写队列,释放其中的skb(socket缓冲区),清理重传队列,重置相关的计数器和状态,以便套接字可以被安全地关闭或重置。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/733629.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot单元测试与热部署简析

1 Spring Boot的简介 Spring Boot是一个用于构建独立的、生产级别的Spring应用程序的框架。它简化了Spring应用程序的开发过程,提供了自动配置和默认配置,使得开发者只需专注于业务逻辑的实现,而不用去关注繁琐的配置问题。 Spring …

ElasticSearch 底层读写原理

ElasticSearch 底层读写原理 ​ 写请求是写入 primary shard,然后同步给所有的 replica shard;读请求可以从 primary shard 或 replica shard 读取,采用的是随机轮询算法。 1、ES写入数据的过程 1.选择任意一个DataNode发送请求&#xff0c…

Java17 --- springCloud之LoadBalancer

目录 一、LoadBalancer实现负载均衡 1.1、创建两个相同的微服务 1.2、在客户端80引入loadBalancer的pom 1.3、80服务controller层&#xff1a; 一、LoadBalancer实现负载均衡 1.1、创建两个相同的微服务 1.2、在客户端80引入loadBalancer的pom <!--loadbalancer-->&…

WPF 窗口添加投影效果Effect

BlurRadius&#xff1a;阴影半径 Color&#xff1a;颜色 Direction&#xff1a;投影方向 ShadowDepth&#xff1a;投影的深度 <Window.Effect><DropShadowEffect BlurRadius"10" Color"#FF858484" Direction"300" ShadowDepth&quo…

LLM长上下文外推方法

现在的LLM都集中在卷上下文长度了&#xff0c;最新的Claude3已经支持200K的上下文&#xff0c;见&#xff1a;cost-context。下面是一些提升LLM长度外推能力的方法总结&#xff1a; 数据工程 符尧大佬的最新工作&#xff1a;Data Engineering for Scaling Language Models to …

【Nestjs实操】环境变量和全局配置

一、环境变量 1、使用dotenv 安装pnpm add dotenv。 根目录下创建.env文件&#xff0c;内容如下&#xff1a; NODE_ENVdevelopment使用 import {config} from "dotenv"; const path require(path); config({path:path.join(__dirname,../.env)}); console.log(…

探索代理服务器:保护您的网络安全与隐私

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 隐藏真实IP地址&#xff1a; 访问控制&#xff1a; 加速访问速度&#xff1a; 过滤内容&#xff1a; 突破访问限制&#xff1…

Python图像处理:1.插值、频域变换与对比度增强

一、几何变换 7.图像的插值 (1)原理介绍 下面对比三种插值方法&#xff0c;分别是最近邻插值法、双线性插值法、卷积插值法&#xff0c;三种方法的前提和特点、优缺点、适用场景如下&#xff1a; 最近邻插值&#xff08;Nearest Neighbor Interpolation&#xff09;&#xf…

Linux网络套接字补充

(&#xff61;&#xff65;∀&#xff65;)&#xff89;&#xff9e;嗨&#xff01;你好这里是ky233的主页&#xff1a;这里是ky233的主页&#xff0c;欢迎光临~https://blog.csdn.net/ky233?typeblog 点个关注不迷路⌯▾⌯ 目录 一、地址转换函数 二、TCP协议通讯流程 三、…

线上应用部署了两台load为1四核服务器

线上应用部署了两台服务器。 项目发布后&#xff0c;我对线上服务器的性能进行了跟踪&#xff0c;发现一台负载为3&#xff0c;另一台负载为1&#xff0c;其中一台四核服务器已经快到瓶颈了&#xff0c;所以我们紧急排查原因。 1、使用TOP命令查看占用CPU较大的负载和进程&…

JavaWeb基础入门——(二)MySQL数据库基础(2-SQL 结构化查询语言)

四、MySQL逻辑结构 4.1 逻辑结构 4.1 记录 五、SQL 结构化查询语言 5.1 SQL概述 SQL&#xff08;Structural Query Language&#xff09;结构化查询语言&#xff0c;用于存取、查询、更新数据以及管理关系型数据库系统 5.1.1 SQL发展 SQL是在1981年由IBM公司推出&#xff0c;…

深入理解 Webpack 热更新原理:提升开发效率的关键

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

了解华为(PVID VLAN)与思科的(Native VLAN)本征VLAN的区别并学习思科网络中二层交换机的三层结构局域网VLAN配置

一、什么是二层交换机&#xff1f; 二层交换机&#xff08;Layer 2 Switch&#xff09;是一种网络设备&#xff0c;主要工作在OSI模型的数据链路层&#xff08;第二层&#xff09;&#xff0c;用于在局域网内部进行数据包的交换和转发。二层交换机通过学习MAC地址表&#xff0…

计算机服务器中了locked勒索病毒怎么解密,locked勒索病毒解密流程

科技的发展带动了企业生产&#xff0c;越来越多的企业开始利用计算机服务器办公&#xff0c;为企业的生产运营提供了极大便利&#xff0c;但随之而来的网络安全威胁也引起了众多企业的关注。近日&#xff0c;云天数据恢复中心接到许多企业的求助&#xff0c;企业的计算机服务器…

arthas之生产环境排查问题常用功能

背景 生产环境调试使用。对问题进行高效排查。 目录 一、watch idea安装arthas插件 idea插件下载代理配置 A. 选择Http Proxy Settings, 打开配置页面B. 选择 Auto-detect proxy settingC. 上图中选择Ok即可, plugin 列表 刷新得到插件内容 启动arthas客户端watch监听 通过art…

(关键点检测)YOLOv8实现多类人体姿态估计的输出格式分析

&#xff08;关键点检测&#xff09;YOLOv8实现多类人体姿态估计的输出格式分析 任务分析 所使用的数据配置文件 网络结构 导出模型 用 netron 可视化 输出格式分析 参考链接 1. 任务分析 判断人体关键点时一并给出关键点所属的类别&#xff0c;比如男人&#xff0c;女…

【Redis知识点总结】(二)——Redis高性能IO模型剖析

Redis知识点总结&#xff08;二&#xff09;——Redis高性能IO模型及其事件驱动框架剖析 IO多路复用传统的阻塞式IO同步非阻塞IOIO多路复用机制 Redis的IO模型Redis的事件驱动框架 IO多路复用 Redis的高性能的秘密&#xff0c;在于它底层使用了IO多路复用这种高性能的网络IO&a…

vue 自定义组件绑定model+弹出选择支持上下按键选择

参考地址v-modelhttps://v2.cn.vuejs.org/v2/guide/components-custom-events.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6%E7%9A%84-v-model 原文代码 Vue.component(base-checkbox, {model: {prop: checked,event: change},props: {checked: Boolean},template: `…

2.4_3 死锁的处理策略——避免死锁

文章目录 2.4_3 死锁的处理策略——避免死锁&#xff08;一&#xff09;什么是安全序列&#xff08;二&#xff09;安全序列、不安全状态、死锁的联系&#xff08;三&#xff09;银行家算法 总结 2.4_3 死锁的处理策略——避免死锁 银行家算法是“避免死锁”策略的最著名的一个…

Elasticsearch架构原理

一. Elasticsearch架构原理 1、Elasticsearch的节点类型 在Elasticsearch主要分成两类节点&#xff0c;一类是Master&#xff0c;一类是DataNode。 1.1 Master节点 在Elasticsearch启动时&#xff0c;会选举出来一个Master节点。当某个节点启动后&#xff0c;然后使用Zen D…