【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缓冲区),清理重传队列,重置相关的计数器和状态,以便套接字可以被安全地关闭或重置。
相关文章:

【C语言】linux内核tcp_write_xmit和tcp_write_queue_purge
tcp_write_xmit 一、讲解 这个函数 tcp_write_xmit 是Linux内核TCP协议栈中的一部分,其基本作用是发送数据包到网络。这个函数会根据不同情况推进发送队列的头部,确保只要远程窗口有空间,就可以发送数据。 下面是对该函数的一些主要逻辑的中…...
opencv实现视频人脸识别
一. 实现指定图像的人脸识别 注意: 以下实例参考《OpenCV轻松入门面向Python》李立宗著,使用python语言,编辑器为PyCharm,且都运行成功。 1.dface3.jpg图片文件和当前代码放在同一级目录下。 2.级联分类器文件和当前代码文件放在…...
【今日面经】24/3/9 广州Java某小厂电话面经
面经来源:https://www.nowcoder.com/?type818_1 目录 1、 和equals()有什么区别?2、String变量直接赋值和构造函数赋值比较相等吗?3、String一些方法?4、抽象类和接口有什么区别?5、Java容器有哪些?6、Lis…...

日期问题---算法精讲
前言 今天讲讲日期问题,所谓日期问题,在蓝桥杯中出现众多,但是解法比较固定。 一般有判断日期合法性,判断是否闰年,判断日期的特殊形式(回文或abababab型等) 目录 例题 题2 题三 总结 …...

倒计时35天
dp预备(来源:b站acm刘春英老师) 1. 2. 3. 4. 5. 6. 7....
JAVA后端开发面试基础知识(七)——多线程
1. 线程池原理 优点 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线…...
Apache的安装与目录结构详细解说
1. Apache安装步骤 Apache是一款开源的Web服务器软件,常用于搭建网站和服务。以下是Apache的安装步骤: 在官方网站(https://httpd.apache.org/)下载最新版本的Apache软件包。解压下载的软件包到指定目录。运行安装程序ÿ…...

axios的详细使用
目录 axios:现代前端开发的HTTP客户端王者 一、axios简介 二、axios的基本用法 1. 安装axios 2. 发起GET请求 3. 发起POST请求 三、axios的高级特性 1. 拦截器 2. 取消请求 3. 自动转换JSON数据 四、axios在前端开发中的应用 五、总结 axios:…...

空间复杂度的OJ练习——轮转数组
旋转数组OJ链接:https://leetcode-cn.com/problems/rotate-array/ 题目: 思路: 通过题目我们可以知道这是一个无序数组,只需要将数组中的数按给定条件重新排列,因此我们可以想到以下几种方法: 1.暴力求解法…...

学习与学习理论 - 2024教招 - test
一 方向 所有学习理论大的观点,到某个人物个人的观点。抖音:按照粉丝数量、收藏数量、点赞数量排名从编程(思想)、java、自己所拥有的特点看学习方法顺序:java、自身、教学理论的总观点、教学理论代表人物的观点、散兵…...

Spring web开发(入门)
1、我们在执行程序时,运行的需要是这个界面 2、简单的web接口(127.0.0.1表示本机IP) package com.example.demo;import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestCont…...

这是谁的女儿?其母亲早已红过头了,现在小小年纪的她也爆红网络,没想到吧?
这是谁的女儿?其母亲早已红过头了,现在小小年纪的她也爆红网络,没想到吧? 原来,作母亲的她在红极一时后似乎沉寂了下来,没想到她11岁的女儿近年来也在社交媒体上走红,她为何也成了小网红呢&…...
鸿蒙开发之gson解析
作为老牌的Java程序员,几乎每个项目都逃不掉fastjson/gson等三方库。那么在OpenHarmony/HarmonyOS应用开发中,做数据解析时能不能使用fastjson/gson三方库呢?于是我搜索了一下,其实在arkts开发过程中也是可以使用JS里自带的JSONparse和JSONstringify方法来实现JSON和对象转…...

图形库实战丨C语言扫雷小游戏(超2w字,附图片素材)
目录 效果展示 游玩链接(无需安装图形库及VS) 开发环境及准备 1.VS2022版本 2.图形库 游戏初始化 1.头文件 2.创建窗口 3.主函数框架 开始界面函数 1.初始化 1-1.设置背景颜色及字体 1-2.处理背景音乐及图片素材 1-3.处理背景图位置 2.选…...
c++: string中 find, rfind, find_frist_of, find_laste_of 与 substr之间的操作
在 C 的 std::string 类中,有几个成员函数可以用于在字符串中执行搜索和子字符串提取操作。以下是这些函数的简要说明: find(): 查找子字符串的第一个出现位置。 size_t find(const string& str, size_t pos 0) const; size_t find(const char* s, …...
[python3] dataclass的对象排序
在使用 dataclass(orderTrue) 中,会比较数据类中定义的所有属性。具体来说,生成的比较运算符方法会按照数据类中定义属性的顺序逐个比较属性的取值。 下面是一个示例代码,演示了 orderTrue 比较数据类中所有属性的情况: from da…...
数据库基础——mysql知识体系(掌握mysql,看完这篇文章就够了)
1.关系型数据库 关系型数据库是一种基于关系模型的数据库系统,将数据组织成表格的形式,表格由行和列组成,每行代表一个记录,每列代表一个属性。它使用结构化查询语言SQL进行数据管理和操作。 特点:1.数据的组织&…...

Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(二)
Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(前导) Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(一) Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(三) 五、实验目的 本次实验使用电脑上的…...
高级语言讲义2010计专(仅高级语言部分)
1.编写一程序,对输入的正整数,求他的约数和。 如:18的约数和为1236939 #include <stdio.h>int getsum(int n){int i,sum0;for(i1;i<n;i)if(n%i0)sumi;return sum; } int main(){int sum getsum(18);printf("%d",sum); …...
你喜欢那种舞者呢?
迷宫中的舞者:程序员职业赛道的探索与魅力 在数字世界的深处,程序员的职业赛道宛如一座神秘而迷人的迷宫。这个迷宫中,每个转角都隐藏着无限的可能,每个领域都散发着独特的魅力。前端开发者如同花园中的精灵,后端工程师…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...