原文地址:TCP/IP源码学习(24)——tcp_write_xmit 作者:GFree_Wind
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
- void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
- int nonagle)
- {
- /* If we are closed, the bytes will have to remain here.
- * In time closedown will finish, we empty the write queue and
- * all will be happy.
- */
- /* 该socket已经关闭,那么直接返回 */
- if (unlikely(sk->sk_state == TCP_CLOSE))
- return;
/* 发送数据 */
- if (tcp_write_xmit(sk, cur_mss, nonagle, 0, GFP_ATOMIC))
- tcp_check_probe_timer(sk); //发送数据失败,使用probe timer进行检查。
- }
下面看函数
tcp_write_xmit
- static int 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);
- struct sk_buff *skb;
- unsigned int tso_segs, sent_pkts;
- int cwnd_quota;
- int result;
- sent_pkts = 0;
/* 检查是不是只发送一个skb buffer,即push one */
- if (!push_one) {
- /*
- 如果要发送多个skb,则需要检测MTU。
- 这时会检测MTU,希望MTU可以比之前的大,提高发送效率。
- */
- /* Do MTU probing. */
- result = tcp_mtu_probe(sk);
- if (!result) {
- return 0;
- } else if (result > 0) {
- sent_pkts = 1;
- }
- }
/* 检查是否还有skb要发送 */
- while ((skb = tcp_send_head(sk))) {
- unsigned int limit;
/*
初始化TSO
*/
- tso_segs = tcp_init_tso_segs(sk, skb, mss_now);
- BUG_ON(!tso_segs);
/* 检查congestion windows, 可以发送几个segment */
- cwnd_quota = tcp_cwnd_test(tp, skb);
- if (!cwnd_quota)
- break;
/* 检查发送窗口,是否可以容纳至少一个segment */
- if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now)))
- break;
- if (tso_segs == 1) {
- /* 根据nagle算法,计算是否需要发送数据 */
- if (unlikely(!tcp_nagle_test(tp, skb, mss_now,
- (tcp_skb_is_last(sk, skb) ?
- nonagle : TCP_NAGLE_PUSH))))
- break;
- } else {
- /* 当不止一个skb时,通过TSO计算是否需要延时发送 */
- if (!push_one && tcp_tso_should_defer(sk, skb))
- break;
- }
- limit = mss_now;
- /* 在TSO分片大于1的情况下,且TCP不是URG模式。通过MSS计算发送数据的limit */
- if (tso_segs > 1 && !tcp_urg_mode(tp))
- limit = tcp_mss_split_point(sk, skb, mss_now,
- cwnd_quota);
/* 当skb的长度大于限制时,需要调用tso_fragment分片 */
- if (skb->len > limit &&
- unlikely(tso_fragment(sk, skb, limit, mss_now, gfp)))
- break;
/* 更新tcp的时间戳 */
- TCP_SKB_CB(skb)->when = tcp_time_stamp;
/* 发送skb数据 */
- if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))
- break;
- /* Advance the send_head. This one is sent out.
- * This call will increment packets_out.
- */
- /* 更新统计,并启动重传计时器 */
- tcp_event_new_data_sent(sk, skb);
- tcp_minshall_update(tp, mss_now, skb);
- sent_pkts++;
- if (push_one)
- break;
- }
- if (likely(sent_pkts)) {
- tcp_cwnd_validate(sk);
- return 0;
- }
- return !tp->packets_out && tcp_send_head(sk);
- }
今天就学到这里。发现TCP的内部机制的复杂度,远胜于UDP。不仅仅是TCP协议所表现的那些,内部机制更为复杂。所以我可能会先再看看资料,把基础打牢,在继续学习tcp/ip的源码了。