【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,触发BUG
        if (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;
            else
                break;
        }
        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))) // 尝试传输skb
            break;
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));
    // 将"出站数据包数量"计数器设置为0
    tcp_sk(sk)->packets_out = 0;
    // 将网络传输层的退避级别设为0
    inet_csk(sk)->icsk_backoff = 0;
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实验二(一):IPV4编址及IPV4路由基础实验

一实验介绍 1.关于本实验 IPv4( Internet Protocol Version 4)是 TCP/IP 协议族中最为核心的协议之一。 它工作在 TCP/IP参考模型的网际互联层,该层与 OSI参考模型的网络层相对应。 网络层提供了无连接数据传输服务,即网络在发送分组时不需要先建立连…

算法设计与分析(超详解!) 第一节 算法概述

1.算法的定义 算法的非形式化定义:算法是规则的有限集合,是为解决特定问题而规定的一系列操作。 可以理解为:算法(algorithm)是指在解决问题时,按照某种机械的步骤一定可以得到问题的结果(有的…

数学建模-动态规划(美赛运用)

动态规划模型的要素是对问题解决的抽象,其可分为: 阶段。指对问题进行解决的自然划分。例如:在最短线路问题中,每进行走一步的决策就是一个阶段。 状态。指一个阶段开始时的自然状况。例如:在最短线路问题中&#xff…

嵌入式Qt 制作一个登录对话框

一.登录对话框需求分析 二.代码实现 main.c&#xff1a; #include <QtGui/QApplication> #include "widget.h"int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); }Widget.h&#xff1a; #ifndef _WIDGET_H_…

EDA软件

EDA软件 EDA概念IC类EDA&#xff08;芯片EDA软件&#xff09;数字芯片和模拟芯片的区别模拟芯片产品种类IC设计类数字电路设计模拟电路设计 IC制造类IC封装类 PCB类EDA&#xff08;板级EDA软件&#xff09;Mentor公司板级EDACadence公司板级EDAAltium公司&#xff08;已被日本瑞…

bug_java

文章目录 1.创建Maven时&#xff1a; idea报错为&#xff1a;java&#xff1a;错误&#xff1a;不支持发行版本52. Springbot启动报错-类文件具有错误的版本 61.0, 应为 52.0 1.创建Maven时&#xff1a; idea报错为&#xff1a;java&#xff1a;错误&#xff1a;不支持发行版本…

贪吃蛇(C语言实现)

贪食蛇&#xff08;也叫贪吃蛇&#xff09;是一款经典的小游戏。 —————————————————————— 本博客实现使用C语言在Windows环境的控制台中模拟实现贪吃蛇小游戏。 实行的基本功能&#xff1a; • 贪吃蛇地图的绘制 • 蛇吃食物的功能&#xff08;上、…

【重新定义matlab强大系列十七】Matlab深入浅出长短期记忆神经网络LSTM

&#x1f517; 运行环境&#xff1a;Matlab &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 #### 防伪水印——左手の明天 #### &#x1f497; 大家好&#x1f917;&#x1f91…

Netty Review - 探究Netty服务端主程序无异常退出的背后机制

文章目录 概述故障场景尝试改进问题分析铺垫&#xff1a; Daemon线程Netty服务端启动源码分析逻辑分析 如何避免Netty服务端意外退出最佳实践 概述 在使用Netty进行服务端程序开发时&#xff0c;初学者可能会遇到各种问题&#xff0c;其中之一就是服务端意外退出的问题。这种问…

基于机器学习的工业用电量预测完整代码数据

视频讲解: 毕业设计:算法+系统基于机器学习的工业用电量预测完整代码数据_哔哩哔哩_bilibili 界面展示: 结果分析与展示: 代码: from sklearn import preprocessing import random from sklearn.model_selection import train_test_split from sklearn.preprocessing…

oracle基础-多表关联查询 备份

一、概述 在实际应用系统开发中会设计多个数据表&#xff0c;每个表的信息不是独立存在的&#xff0c;而是若干个表之间的信息存在一定的关系&#xff0c;当用户查询某一个表的信息时&#xff0c;很可能需要查询关联数据表的信息&#xff0c;这就是多表关联查询。SELECT语句自身…

【NR技术】 3GPP支持无人机服务的关键性能指标

1 性能指标概述 5G系统传输的数据包括安装在无人机上的硬件设备(如摄像头)收集的数据&#xff0c;例如图片、视频和文件。也可以传输一些软件计算或统计数据&#xff0c;例如无人机管理数据。5G系统传输的业务控制数据可基于应用触发&#xff0c;如无人机上设备的开关、旋转、升…

数字化解决方案的设计与实现:提升业务效率与用户体验

摘要&#xff1a;随着数字化时代的到来&#xff0c;越来越多的企业和组织开始寻求数字化解决方案来提升业务效率和改善用户体验。本文将探讨数字化解决方案的设计与实现过程&#xff0c;并介绍一些关键的技术和策略。 ## 引言 在当今竞争激烈的商业环境中&#xff0c;企业和组…

深度学习图像算法工程师--面试准备(2)

深度学习面试准备 深度学习图像算法工程师–面试准备&#xff08;1&#xff09; 深度学习图像算法工程师–面试准备&#xff08;2&#xff09; 文章目录 深度学习面试准备前言一、Batch Normalization(批归一化)1.1 具体步骤1.2 BN一般用在网络的哪个部分 二、Layer Normaliza…

个人健康管理系统|基于微信小程序的个人健康管理系统设计与实现(源码+数据库+文档)

个人健康管理小程序目录 目录 基于微信小程序的个人健康管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1、用户信息管理 2 运动教程管理 3、公告信息管理 4、论坛信息管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设…

Java对接腾讯云直播示例

首先是官网的文档地址 云直播 新手指南 可以发现它这个主要是按流量和功能收费的 价格总览 流量这里还只收下行的费用&#xff0c;就是只收观看消耗的流量费 其它的收费就是一些增值业务费 &#xff08;包括直播转码、直播录制、直播截图、直播审核、智能鉴黄、实时监播、移动直…

【JavaEE初阶 -- 多线程】

认识线程&#xff08;Thread&#xff09;Thread类及常见方法 1.认识线程&#xff08;Thread&#xff09;1.1 线程1.2 进程和线程的关系和区别1.3 Java的线程和操作系统线程的关系1.4 创建线程 2. Thread类及常用的方法2.1 Thread的常见构造方法2.2 Thread的几个常见属性2.3 启动…

java SSM厂房管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM厂房管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S…

10 | MySQL为什么有时候会选错索引?

前面我们介绍过索引&#xff0c;你已经知道了在 MySQL 中一张表其实是可以支持多个索引的。但是&#xff0c;你写 SQL 语句的时候&#xff0c;并没有主动指定使用哪个索引。也就是说&#xff0c;使用哪个索引是由 MySQL 来确定的。 不知道你有没有碰到过这种情况&#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-->&…