TCP协议初始化分析
在上一次实验中,已经对创建套接字的代码进行了简要分析,使用create_socket创建了套接字,这篇文章分析初始化的过程。
查看在函数中定义的tcp接口结构体,了解每个接口对应的处理函数。
struct proto tcp_prot = { .name = "TCP", .owner = THIS_MODULE, .close = tcp_close, .pre_connect = tcp_v4_pre_connect, .connect = tcp_v4_connect, .disconnect = tcp_disconnect, .acc ept = inet_csk_accept, .ioctl = tcp_ioctl, .init = tcp_v4_init_sock, .destroy = tcp_v4_destroy_sock, .shutdown = tcp_shutdown, .setsockopt = tcp_setsockopt, .getsockopt = tcp_getsockopt, .keepalive = tcp_set_keepalive, .recvmsg = tcp_recvmsg, .sendmsg = tcp_sendmsg, .sendpage = tcp_sendpage, .backlog_rcv = tcp_v4_do_rcv, .release_cb = tcp_release_cb, .hash = inet_hash, .unhash = inet_unhash, .get_port = inet_csk_get_port, .enter_memory_pressure = tcp_enter_memory_pressure, .leave_memory_pressure = tcp_leave_memory_pressure, .stream_memory_free = tcp_stream_memory_free, .sockets_allocated = &tcp_sockets_allocated, .orphan_count = &tcp_orphan_count, .memory_allocated = &tcp_memory_allocated, .memory_pressure = &tcp_memory_pressure, .sysctl_mem = sysctl_tcp_mem, .sysctl_wmem_offset = offsetof(struct net, ipv4.sysctl_tcp_wmem), .sysctl_rmem_offset = offsetof(struct net, ipv4.sysctl_tcp_rmem), .max_header = MAX_TCP_HEADER, .obj_size = sizeof(struct tcp_sock), .slab_flags = SLAB_TYPESAFE_BY_RCU, .twsk_prot = &tcp_timewait_sock_ops, .rsk_prot = &tcp_request_sock_ops, .h.hashinfo = &tcp_hashinfo, .no_autobind = true, #ifdef CONFIG_COMPAT .compat_setsockopt = compat_tcp_setsockopt, .compat_getsockopt = compat_tcp_getsockopt, #endif .diag_destroy = tcp_abort, };
1、tcp协议的初始化找到init接口,发现其对应的处理方法是tcp_v4_init_sock,是一个静态类。
static int tcp_v4_init_sock(struct sock *sk) { struct inet_connection_sock *icsk = inet_csk(sk); tcp_init_sock(sk); icsk->icsk_af_ops = &ipv4_specific; #ifdef CONFIG_TCP_MD5SIG tcp_sk(sk)->af_specific = &tcp_sock_ipv4_specific; #endif return 0; }
代码分成两段,在Init中使用的TCP MD5选项用于强化BGP协议的安全性,其基本原理是在TCP报文段的选项中携带MD5摘要。这个摘要的行为类似于这个报文的签名,其中包含这只有通信双方才能理解的信息。如果BGP协议使用TCP作为其传输层协议,使用MD5选项会有效减少安全隐患。
发现调用了tcp_init_sock,这里猜想tcp中ipv4的init函数是采用的都是net/ipv4文件夹下的tcp.c文件中的内容进行调用的。
查看socket.c中关于tcp_init_sock的源代码,发现大部分是对已创建的node以及tcp链进行了复制,仅有少部分调用了函数,可对tcp_init_xmit_timers,sk_sockets_allocated_inc函数分别进行分析。
void tcp_init_sock(struct sock *sk) { struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); tp->out_of_order_queue = RB_ROOT; sk->tcp_rtx_queue = RB_ROOT;
//对时间初始化 tcp_init_xmit_timers(sk); //初始化TCP链表头结点 INIT_LIST_HEAD(&tp->tsq_node); INIT_LIST_HEAD(&tp->tsorted_sent_queue); icsk->icsk_rto = TCP_TIMEOUT_INIT; tp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT); minmax_reset(&tp->rtt_min, tcp_jiffies32, ~0U); /* So many TCP implementations out there (incorrectly) count the * initial SYN frame in their delayed-ACK and congestion control * algorithms that we must have the following bandaid to talk * efficiently to them. -DaveM */ tp->snd_cwnd = TCP_INIT_CWND; /* There's a bubble in the pipe until at least the first ACK. */ tp->app_limited = ~0U; /* See draft-stevens-tcpca-spec-01 for discussion of the * initialization of these values. */ //将变量值进行初始化 tp->snd_ssthresh = TCP_INFINITE_SSTHRESH; tp->snd_cwnd_clamp = ~0; tp->mss_cache = TCP_MSS_DEFAULT; tp->reordering = sock_net(sk)->ipv4.sysctl_tcp_reordering; tcp_assign_congestion_control(sk); //偏移量置为0, tp->tsoffset = 0; tp->rack.reo_wnd_steps = 1; sk->sk_state = TCP_CLOSE; sk->sk_write_space = sk_stream_write_space; //set_flag将标志量初始化,将设置写权限,即能够在向需要啊发送的TCP数据中写入相应的数据。 sock_set_flag(sk, SOCK_USE_WRITE_QUEUE); icsk->icsk_sync_mss = tcp_sync_mss; //读写缓冲初始化 sk->sk_sndbuf = sock_net(sk)->ipv4.sysctl_tcp_wmem[1]; sk->sk_rcvbuf = sock_net(sk)->ipv4.sysctl_tcp_rmem[1]; sk_sockets_allocated_inc(sk); sk->sk_route_forced_caps = NETIF_F_GSO; } EXPORT_SYMBOL(tcp_init_sock);
发现其中调用了两个方法类,以下对两个方法类分别进行分析。
首先对计时器进行了初始化,众所周知,TCP协议是一个可靠的协议,因此需要借助计时器进行监检测双方接收或者发送包是否有超时,进而判断是否存在丢包现象。
tcp_init_xmit_timers(sk)使用的是时钟的初始化。
1.2Socket计时器初始化
void tcp_init_xmit_timers(struct sock *sk)
{
inet_csk_init_xmit_timers(sk, &tcp_write_timer, &tcp_delack_timer,
&tcp_keepalive_timer);
hrtimer_init(&tcp_sk(sk)->pacing_timer, CLOCK_MONOTONIC,
HRTIMER_MODE_ABS_PINNED_SOFT);
tcp_sk(sk)->pacing_timer.function = tcp_pace_kick;
hrtimer_init(&tcp_sk(sk)->compressed_ack_timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_PINNED_SOFT);
tcp_sk(sk)->compressed_ack_timer.function = tcp_compressed_ack_kick;
}
调用的set_up是一个宏。
#define timer_setup(timer, callback, flags) \
__init_timer((timer), (callback), (flags)
其目的是实现内核的定时机制的初始化。· 总结一下,看起来timer_setup(timer, callback, flags) 主要用是用callback和flags来给timer_list *timer 来赋值
因此inet_csk_xmit_timer调用内核计时器相关的初始化功能,将tcp写的时间进行了初始化,tcp延迟时间和tcp alive的时间。
2、第二项
Hrtimer_init使用的是在内核的kernel/time/hrtimer
hrtimer_init初始化定时器工作模式。
hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
vibe_timer.function = timer_func;
初始化了定时器工作模式
1.3sk_sockets_allocated_inc(sk)
Clone a socket,and lock its clone
Caller must unlock socket even in error path
调用net中的core函数,其中涉及到了很多锁的知识。
该函数是用来克隆,是为了防止socket初始化失败。
至此,对socket中init中较为重要的初始化函数进行了分析。