【C语言】linux内核tcp_push函数

186 篇文章 1 订阅
67 篇文章 0 订阅
本文详细解析了Linux内核中tcp_push函数的作用,涉及TCP套接字数据发送的控制过程,包括获取和处理sk_buff结构、检查发送标志、紧急数据标记、自动Corking策略和Nagle算法的应用。
摘要由CSDN通过智能技术生成

一、讲解

这个 tcp_push 函数是在Linux内核的TCP网络栈实现中,用于推动TCP缓冲区中待发送数据包的传输。这段代码需要在具备操作系统和网络编程知识背景下来解释。下面我将分步骤用中文逐一讲解这个函数的作用:
1. struct tcp_sock *tp = tcp_sk(sk);:
   - 这行代码定义了一个指向 tcp_sock 结构体的指针 tp ,并将其初始化为与 sk 关联的TCP控制块。`sk` 参数是一个代表网络套接字的 sock 结构体的指针。
2. struct sk_buff *skb;:
   - skb 定义了一个指向 sk_buff 结构体的指针,`sk_buff` 通常用来在内核中表示网络数据包。
3. skb = tcp_write_queue_tail(sk);:
   - 这一行获取套接字写队列中的最后一个数据包(SKB)。如果写队列为空,则后面的代码不做任何操作。
4. if (!skb) return;:
   - 如果 skb 是空的,说明没有数据可以推送,函数就此返回,不再执行后续操作。
5. if (!(flags & MSG_MORE) || forced_push(tp)) tcp_mark_push(tp, skb);:
   - 这里检查 flags 参数,如果未设置 MSG_MORE 标志或者触发强制推送,就会在当前 skb 上标记PUSH标志。PUSH标志告诉对方TCP应立即传送这些数据。
6. tcp_mark_urg(tp, flags);:
   - 如果设置了紧急数据标志,则标记当前 skb 为紧急数据。
7. if (tcp_should_autocork(sk, skb, size_goal)) { ... }:
   - 这个条件判断是否应该自动“塞子”(Corking)当前连接,即临时阻塞数据发送以期待更多的输出聚合成更大的段以提升效率。如果是,则标记为被限流(`TSQ_THROTTLED`),并在统计时增加相应的技术。
8. if (flags & MSG_MORE) nonagle = TCP_NAGLE_CORK;:
   - 如果设置了 MSG_MORE 标志,表示还有更多数据要发送,那么启用Nagle算法进行Corking操作。
9. __tcp_push_pending_frames(sk, mss_now, nonagle);:
   - 最后,调用 __tcp_push_pending_frames 函数,实际上推动缓冲区中的数据帧进行传输。这里会计算每个数据段的大小,并考虑是否需要应用Nagle算法。
函数中的部分变量如`mss_now`用于指示最大段大小、`nonagle`指示是否应用Nagle算法以避免发送过多的小包,`size_goal`指示目标数据包大小,用于决定是否Corking。整个函数的目的是控制如何和何时将TCP套接字的写队列中的数据发送出去。

二、注释

static void tcp_push(struct sock *sk, int flags, int mss_now,
             int nonagle, int size_goal)
{
    // 将给定 sock 结构体转换为特定的 tcp_sock 结构体
    struct tcp_sock *tp = tcp_sk(sk);
    // 声明一个指向数据报的指针
    struct sk_buff *skb;

    // 获取 socket 写队列中最后一个数据报
    skb = tcp_write_queue_tail(sk);
    // 如果没有数据报,就返回
    if (!skb)
        return;
    // 检查是否需要强制发送数据或不设置 MSG_MORE 标志,如果是则标记 PUSH
    if (!(flags & MSG_MORE) || forced_push(tp))
        tcp_mark_push(tp, skb);

    // 标记是否有紧急数据需要发送,如果有设置相应的 TCP 状态
    tcp_mark_urg(tp, flags);

    // 如果需要自动 cork(自动合并小数据报),则进行处理
    if (tcp_should_autocork(sk, skb, size_goal)) {

        // 如果 TSQ_THROTTLED 标志还未设置,避免使用原子操作设置它
        if (!test_bit(TSQ_THROTTLED, &sk->sk_tsq_flags)) {
            // 增加 TCP 自动 corking 的统计信息
            NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAUTOCORKING);
            // 设置 TSQ_THROTTLED 标志
            set_bit(TSQ_THROTTLED, &sk->sk_tsq_flags);
        }
        // 有可能在我们设置 TSQ_THROTTLED 之前,数据包已经发送完成
        if (refcount_read(&sk->sk_wmem_alloc) > skb->truesize)
            return;
    }

    // 如果设置了 MSG_MORE 标志,就将 nonagle 设置为 TCP_NAGLE_CORK,以合并多个小的数据报
    if (flags & MSG_MORE)
        nonagle = TCP_NAGLE_CORK;

    // 推动未决的数据帧处理,对排队进行发送处理
    __tcp_push_pending_frames(sk, mss_now, nonagle);
}

内核4.19函数位置:net\ipv4\tcp.c

ChatGPT

Linux 4.19内核中,并没有一个名为“tcp_push”的直接函数。在Linux内核中实现TCP数据发送的函数通常位于`net/ipv4/tcp.c`文件中,并涵盖了一套复杂的TCP协议实现,包括数据段的发送、接收、重传等逻辑。
然而,当你提到“tcp_push”时,你很可能是指的在TCP连接中推送数据到对端的过程。在Linux的TCP/IP堆栈中,这涉及到一系列的函数调用。数据的最终发送是通过下面的几个关键步骤来完成的:
1. 应用层调用`write()`或`send()`函数。
2. 内核的系统调用接口将这些调用转换为特定的socket操作函数。
3. 对应的TCP层函数,如`tcp_sendmsg()`,会被调用来处理数据段的发送。
4. 函数`tcp_write_xmit()`会处理将数据段放到网络队列中。
5. 最终,函数`tcp_push_one()`也许会被调用来推送单个的TCP段到网络层。
如果你是在寻找发送TCP数据的相关代码,我建议直接去Linux内核的源代码中查看`net/ipv4/tcp.c`文件。请记得,Linux内核的实现是根据TCP/IP协议栈的要求进行调整的,并不总是直观的和传统API(如POSIX API)有直接的对应关系。
由于内核代码可能非常复杂,并且在不同版本的内核中可能会有重组,查找特定函数的最好办法是在内核源代码目录使用如grep工具进行搜索。例如,你可以在Linux内核的源代码根目录下使用以下命令:

grep -rnw './' -e 'tcp_push_one'

这个命令会在当前目录及子目录中查找包含字符串`tcp_push_one`的所有文件,并显示匹配行的号码及内容。
若您在探索Linux内核的过程中有任何特定的问题或需要对特定代码段进行讨论,欢迎提出具体问题。


在Linux内核4.9版本中,`tcp_push` 函数并不是一个独立的函数。相反,完成TCP数据推送的逻辑被内嵌在了其他函数中,如`tcp_write_xmit`等。在进行TCP通信时,数据推送通常与`tcp_sendmsg`、`tcp_write_xmit`和`tcp_push_one`等函数相关,它们处理数据的发送以及确保在适当时候将数据推送到网络上。
tcp_sendmsg函数是在发送TCP数据时由系统调用层调用的函数,它处理用户空间发送的数据,并将其放进发送队列。
tcp_write_xmit函数则用于实际将之前排队的数据段传输到网络层,这个函数会处理拥塞窗口和路径MTU发现等问题。如果需要的话,`tcp_write_xmit`会触发数据的即时发送,相当于执行了"推送"操作。
tcp_push_one函数是在某些情况下用来发送单个TCP段的帮助函数,确保至少有一个段被推送到网络。
以下是一个简化的视角,来描述这些函数如何在一定程境下工作:

int tcp_sendmsg(...){
    // 处理发送的消息
    // ...
    tcp_push(one_segment_only);
    // ...
}

void tcp_write_xmit(...){
    // 准备数据发送
    // ...
    if(push_required) {
        // 真正推送到网络层
    }
}

void tcp_push_one(...)
{
    // 确保至少有一个TCP段被推送
    tcp_write_xmit(..., true);
}

请注意:上面的代码只是一个示意性的伪代码,并不是真正的内核代码,它不会在Linux内核中以这个形式存在。
在实际查看Linux内核源码的时候,你可以直接前往Linux内核的官方Git仓库或者使用在线源码浏览器来查找这些函数的实现。通常,通过函数名进行搜索能找到其详细的实现逻辑。而且,由于Linux内核代码经常变动,查看特定版本的内核来源代码总是一个好主意。 

在Linux内核中,并没有一个名为`tcp_push`的函数。不过,内核确实包含了处理TCP数据发送的逻辑,这通常涉及一个名为`tcp_push`的操作,这个术语通常是在讨论当内核需要发送TCP数据时所采取的策略。以下是一些可能相关的函数和概念:
1. tcp_sendmsg:这是内核中一个处理传出TCP数据的函数,它在用户空间的数据需要通过TCP连接发送时被调用。
2. tcp_write_xmit:在TCP协议栈中,当确保数据准备好发出,并且网络条件适宜时,这个函数负责把数据推送到网络层。
3. tcp_push_one:虽然不是一个标准函数,但在一些上下文中,这个术语可能与在单个TCP段上显式设置PUSH标志有关,迫使对方立即处理接收到的数据。
4. tcp_output:这个函数会触发TCP层来检查发送队列,如果有必要发送新的数据或重新传输丢失的段,这个函数则会起作用。
如果你指的是内核源码中的一个特定函数,你需要查阅最新的内核代码来了解其实现细节。内核中TCP的实现随着版本的更新而发生变化,因此可能需要参考特定版本的内核代码来获取准确信息。如果你需要关于如何在Linux内核中实现特定的TCP推送逻辑的帮助,那么最好直接查看最新版内核代码中的TCP协议栈实现。

文心一言

在 Linux 4.19 内核中,tcp_push 函数是 TCP 协议栈的一部分,负责尝试发送 TCP 套接字上待发送的数据。该函数通常定义在 net/ipv4/tcp_output.c 文件中。

tcp_push 函数的主要目的是确保 TCP 发送队列中的数据被发送到网络上。它会遍历发送队列,并根据窗口大小、拥塞控制算法等因素来决定发送多少数据。如果可能,它会尝试发送尽可能多的数据段。

由于内核代码会随时间进行更新和优化,因此我无法为你提供 Linux 4.19 内核中 tcp_push 函数的完整代码。但是,我可以为你提供一个大致的框架和可能的代码片段。请注意,这只是一个示例,并不代表实际的代码实现。

/* tcp_output.c */  
  
void tcp_push(struct sock *sk, int flags)  
{  
    struct tcp_sock *tp = tcp_sk(sk);  
    struct sk_buff *skb;  
    struct tcp_skb_cb *tcb;  
    int forced_push = 0;  
  
    if (!tcp_send_head(sk))  
        return;  
  
    /* ... 省略了一些初始化和条件检查代码 ... */  
  
    do {  
        /* ... 遍历发送队列,准备发送数据 ... */  
  
        /* 尝试发送数据 */  
        if (tcp_transmit_skb(sk, skb, 1, sk->sk_allocation) == 0) {  
            /* 数据发送成功 */  
            tcp_skb_collapse_tso_segs(sk, skb);  
        }  
  
        /* ... 处理发送后的状态更新和错误处理 ... */  
  
        /* 继续处理下一个数据段 */  
        skb = tcp_write_queue_next(sk, skb);  
  
    } while (skb != tcp_send_head(sk));  
  
    /* ... 清理和结束操作 ... */  
}

这个函数会遍历 TCP 发送队列中的每个数据段(通常表示为 sk_buff 结构体),并尝试使用 tcp_transmit_skb 函数将数据发送到网络上。如果发送成功,它会更新相关的状态信息,并继续处理下一个数据段。

请注意,实际的 tcp_push 函数实现可能更加复杂,并包含更多的条件和错误处理逻辑。为了获取准确的实现细节,你需要查阅 Linux 4.19 内核的源代码,并查找 net/ipv4/tcp_output.c 文件中的 tcp_push 函数定义。

再次强调,直接修改内核代码通常是不推荐的,除非你完全了解你正在做什么,并且你有充分的理由这么做。在大多数情况下,你应该通过配置内核选项、加载内核模块或使用用户空间工具来修改内核的行为。

通义灵码

在Linux内核中,tcp_push() 函数是TCP协议栈的一个核心函数,用于将数据从发送缓冲区推送到网络接口进行发送。在Linux 4.19内核版本中,虽然没有提供确切的代码,但我们可以基于历史版本和Linux内核的一般设计原则来描述其大致功能。

tcp_push() 主要负责以下任务:

  1. 确定可发送的数据量

    • 根据TCP滑动窗口大小、拥塞控制算法(如慢启动、快速重传和快速恢复等)以及MSS(最大段大小)限制,计算出当前允许发送的最大数据量。
  2. 构造TCP段

    • 将待发送的数据封装到一个或多个TCP段中。如果数据量大于MSS,则需要进行分片。
  3. 更新TCP状态

    • 更新发送窗口、序列号(Sequence Number)等相关TCP头部字段,并维护TCP状态机。
  4. 处理选项

    • 如果有需要,会在TCP段中添加选项字段,例如时间戳、SACK块等。
  5. 调用IP层

    • 调用底层IP层的相关函数(如ip_queue_xmit() 或类似的函数),将构建好的TCP段传递下去,最终经过网络设备驱动进行实际的数据发送。
  6. 流量控制

    • 当必要时,函数还会根据TCP流量控制策略来决定是否需要延迟发送。

具体实现细节可以通过查阅Linux内核相应版本(如4.19)下的 net/ipv4/tcp_output.c 文件找到 tcp_push() 函数的源代码。由于内核代码不断迭代优化,不同版本之间可能存在一定的差异,但基本逻辑保持一致。

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

109702008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值