深入剖析:Linux TCP 内核实现解析

1. TCP 协议简介

TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层协议。它负责在不可靠的网络上提供可靠的数据传输。

TCP 协议的内核实现是操作系统中非常重要的一部分。本文将重点深入剖析 Linux TCP 内核实现的细节,帮助读者更好地理解 TCP 协议的工作原理。

2. TCP 内核模块

2.1 TCP 核心模块

Linux 内核中 TCP 实现的核心模块是位于文件 net/ipv4/tcp_ipv4.c 中的tcp_v4 模块。它负责处理所有与 TCP 相关的网络数据包,包括连接建立、数据传输、连接关闭等过程。

在该模块中,主要包含以下几个重要的函数:

tcp_v4_rcv: 处理接收到的 TCP 数据包,并根据包的类型进行相应的处理。

tcp_v4_connect: 建立 TCP 连接,包括三次握手过程。

tcp_v4_send_check: 计算 TCP 数据包的校验和。

2.2 TCP Socket 模块

Linux TCP 内核实现中的另一个重要模块是tcp_sock,位于文件 net/ipv4/tcp.c 中。该模块定义了 TCP 连接相关的数据结构和函数,包括 TCP Socket 的创建、连接管理、数据传输等功能。

在该模块中,主要包含以下几个关键函数:

tcp_create_openreq_child: 创建一个 TCP Socket,并进行必要的初始化。

tcp_connect: 处理主动发起的连接请求。

tcp_sendmsg: 发送数据。

tcp_recvmsg: 接收数据。

tcp_close: 关闭 TCP 连接。

3. TCP 建立连接过程详解

3.1 SYN 表示连接请求

在 TCP 连接的建立过程中,客户端通过发送 SYN 数据包表示发起连接请求。Linux 内核中的 TCP 实现会在tcp_v4_rcv 函数中捕获到这个包,并调用 tcp_v4_connect 函数进行处理。

重要部分:

/* Handle incoming SYN packets for existing connections. */

static void tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)

{

...

/* Deal with the SYN only if our backlog counter can take it. */

if (sk_acceptq_is_full(sk)) {

refcnt_debug_release(&sk->sk_refcnt);

inet_csk_free_icsk(&sk->sk_inet);

sock_put(sk);

return;

}

...

}

上述代码片段展示了当接收到 SYN 包后,内核如何处理。其中sk_acceptq_is_full 函数用于判断接收队列是否已满。如果接收队列已满,连接请求将被拒绝。

3.2 TCP 三次握手过程

在 TCP 连接建立过程中,最重要的一步是三次握手。该过程涉及到客户端和服务端之间的互相确认,确保双方都可以正常通信。

在 Linux 内核实现中,tcp_v4_connect 函数负责处理三次握手过程:

static int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr,

int addr_len)

{

...

/* Try to find a source address */

err = inet_rcv_saddr(sk, &daddr, uaddr->sa_family);

...

/* Create a tcp_sock. */

child = tcp_create_openreq_child(sk, req, &err);

...

/* Send initial SYN. */

tcp_connect(struct sock *sk)

...

}

重要部分:

/* Sets up an initial connection request. */

int tcp_connect(struct sock *sk)

{

...

do {

req->num_retries++;

req->srtt_us -= nrto;

tcp_send_syn_data(sk, req);

} while (sk->sk_state != TCP_SYN_SENT);

...

}

上述代码片段展示了在tcp_v4_connect 函数中,如何发送 SYN 数据包进行三次握手的过程。其中的 tcp_send_syn_data 函数用于发送 SYN 数据包。

4. TCP 数据传输过程详解

4.1 数据发送

在 TCP 连接建立之后,数据的发送是非常重要的操作。Linux 内核中的tcp_sendmsg 函数负责处理数据发送的过程:

static int tcp_sendmsg(struct socket *sock, struct msghdr *msg, int size)

{

...

err = tcp_transmit_skb(sk, skb, flags, chunk, &err_val);

...

}

重要部分:

/* Send a data skb.

*/

static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,

int flags, bool clone_it, int *err)

{

...

/* If queue is empty and can more be sent now? */

if (tcp_send_head(sk) && !after(tcp_sk(sk)->write_seq, tcp_sk(sk)->pushed_seq))

tcp_push(sk, tcp_sk(sk)->pushed_seq);

...

}

上述代码片段展示了tcp_sendmsg 函数中如何调用 tcp_transmit_skb 函数进行数据发送。

4.2 数据接收

在 TCP 数据传输过程中,数据的接收同样也是非常重要的。Linux 内核中的tcp_recvmsg 函数负责处理数据接收的过程:

static int tcp_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,

int nonblock, int flags, int *addr_len)

{

...

/* Read data from receive buffer */

copied = tcp_read_sock(sk, msg, size, flags);

...

}

重要部分:

/* Read data from receive buffer and put it to msg. */

static int tcp_read_sock(struct sock *sk, struct msghdr *msg, size_t len,

int flags)

{

...

/* Pull data into process memory */

skb_splice_bits(&sk->sk_receive_queue, &iter, msg->msg_iter);

...

}

上述代码片段展示了tcp_recvmsg 函数中如何调用 tcp_read_sock 函数进行数据接收。

5. TCP 连接关闭过程详解

5.1 主动关闭连接

当主动关闭 TCP 连接时,Linux 内核中的tcp_close 函数负责处理关闭过程:

void tcp_close(struct sock *sk, long timeout)

{

...

/* Send FIN to the other end. */

tcp_send_active_reset(sk, sk->sk_shutdown == SHUTDOWN_MASK);

...

}

重要部分:

/* Send an active close reset packet.

*/

static void tcp_send_active_reset(struct sock *sk, bool apply_user_timeout)

{

...

/* Send RST */

tcp_send_reset(sk, TCP_RESET_CAUSE_USER, GFP_ATOMIC, TCPHDR_FIN | TCPHDR_ACK);

...

}

上述代码片段展示了tcp_close 函数中如何发送 FIN 数据包进行主动关闭的过程。其中的 tcp_send_active_reset 函数用于发送 RST 数据包。

5.2 被动关闭连接

当被动关闭 TCP 连接时,也需要进行相应的处理。在 Linux 内核中,tcp_v4_conn_request 函数中的以下代码段负责处理被动关闭连接的情况:

if (th->fin && !tcp_hdr(skb)->doff) {

if (!sock_flag(sk, SOCK_DEAD)) {

...

}

inet_csk_destroy_sock(sk);

goto discard;

}

上述代码片段展示了当接收到 FIN 数据包后的处理过程。当接收到 FIN 数据包时,内核会判断连接是否处于活动状态,如果是则调用 inet_csk_destroy_sock 函数进行连接关闭。

6. 总结

本文深入剖析了 Linux TCP 内核实现的细节,包括核心模块和 Socket 模块的功能及相关函数。同时,重点解释了 TCP 连接建立、数据传输和连接关闭的过程。

通过对内核代码的分析,我们可以更深入地理解 TCP 协议在 Linux 系统中的实现细节,对于网络应用程序的开发和网络故障的排除都具有重要的意义。

操作系统标签