日期:2014-05-16  浏览次数:20795 次

Linux 内核网络协议栈 ------ tcp_ack 函数处理接收到的ACK包之后


注意 tcp_ack 是来处理接收到的ACK的,那么到底怎么去做呢?看下面:


先还上把tcp_sock的结构放在这里,下面一些数据的分析需要用到:

struct tcp_sock {
         /* inet_connection_sock has to be the first member of tcp_sock */
         struct inet_connection_sock     inet_conn;
         u16     tcp_header_len; /* Bytes of tcp header to send          */   // tcp头部长度
         u16     xmit_size_goal_segs; /* Goal for segmenting output packets */// 分段数据包的数量
 
/*
 *      Header prediction flags
 *      0x5?10 << 16 + snd_wnd in net byte order
 */
         __be32  pred_flags;  // 头部预置位(用于检测头部标识位处理ACK和PUSH之外还有没有其他位,从而判断是不是可以使用快速路径处理数据)
 
/*
 *      RFC793 variables by their proper names. This means you can
 *      read the code and the spec side by side (and laugh ...)
 *      See RFC793 and RFC1122. The RFC writes these in capitals.
 */
         u32     rcv_nxt;        /* What we want to receive next         */  // 下一个想要收到的第一个数据的字节编号
         u32     copied_seq;     /* Head of yet unread data              */  // 没还有读出的数据的头
         u32     rcv_wup;        /* rcv_nxt on last window update sent   */  // rcv_nxt在最后一个窗口更新时的值
         u32     snd_nxt;        /* Next sequence we send                */  // 下一个发送的第一个字节编号
 
         u32     snd_una;        /* First byte we want an ack for        */  // 对于发出的数据,都需要对方的ACK,这里标示当前需要被确认的第一个字节
         u32     snd_sml;        /* Last byte of the most recently transmitted small packet */ // 最近发送的小数据包的最后一个字节
         u32     rcv_tstamp;     /* timestamp of last received ACK (for keepalives) */ // 最后一次接收到ACK的时间戳
         u32     lsndtime;       /* timestamp of last sent data packet (for restart window) */ // 最后一次发送数据包时间戳
 
         u32     tsoffset;       /* timestamp offset */	// 时间戳偏移
 
         struct list_head tsq_node; /* anchor in tsq_tasklet.head list */ // 
         unsigned long   tsq_flags;

         // 注意下面这个ucopy:就是将用户数据从skb中拿出来放进去,然后传给应用进程!!!
         /* Data for direct copy to user */
         struct {
                 struct sk_buff_head     prequeue; // 预处理队列
                 struct task_struct      *task;    // 预处理进程
                 struct iovec            *iov;     // 用户程序(应用程序)接收数据的缓冲区
                 int                     memory;   // 用于预处理计数
                 int                     len;      // 预处理长度
#ifdef CONFIG_NET_DMA
                 /* members for async copy */
                 struct dma_chan         *dma_chan;
                 int                     wakeup;
                 struct dma_pinned_list  *pinned_list;
                 dma_cookie_t            dma_cookie;
#endif
        } ucopy;

         //  snd_wl1:记录发送窗口更新时,造成窗口更新的那个数据报的第一个序号。 它主要用于在下一次判断是否需要更新发送窗口。 
         u32     snd_wl1;        /* Sequence for window update           */ // 窗口更新序列号( 每一次收到确认之后都会改变 )
         u32     snd_wnd;        /* The window we expect to receive      */ // 我们期望收到的窗口
         u32     max_window;     /* Maximal window ever seen from peer   */ // 从对方接收到的最大窗口
         u32     mss_cache;      /* Cached effective mss, not including SACKS */ // 有效的MSS,SACKS不算
 
         u32     window_clamp;   /* Maximal window to advertise          */ // 对外公布的最大的窗口
         u32     rcv_ssthresh;   /* Current window clamp                 */ // 当前窗口值
  
         u16     advmss;         /* Advertised MSS                       */ // 对外公布的MSS
         u8      unused;
         u8      nonagle     : 4,/* Disable Nagle algorithm?             */ // Nagle算法是否有效
                 thin_lto    : 1,/* Use linear timeouts for thin streams */ // 使用线性超时处理
                 thin_dupack : 1,/* Fast retransmit on first dupack      */ // 收到第一个重复的ACK的时候是否快速重传
                 repair      : 1,