Header Prediction:基于效率的考虑,将包的处理后续阶段分为fast path和slow path两种,前者用于普通的包,后者用于特殊的包;该header prediction即用于区分两种包的流向。
1.(tcp_flag_word(th) & TCP_HP_BITS) == tp->pred_flags 判断标志位是不是正常情况;tcp_flag_word返回指向tcphdr的第三个32位基址(即length前面),而TCP_HP_BITS是把 PSH标志位给屏蔽掉即该位值不影响流向;所以总的来说pred_flag应该等于0xS?10 << 16 + snd_wnd(那么pred_flag是在tcp_fast_path_check或tcp_fast_path_on中更新值的)
2.TCP_SKB_CB(skb)->seq == tp->rcv_nxt 判断所收包是否为我们正想要接收的,非乱序包
3.*ptr != htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP) 若包中没有正常的timestamp选项则转入slow path
timestamp选项处理: 从包中的ts选项中获取数据,以此刷新tp->rx_opt的saw_tstamp,rcv_tsval,rcv_tsecr域;ts选项含三个 32bit,其中后两个分别记录着tsval和tsecr;(注意,ts_recent并不在此处更新,在后面的tcp_store_ts_recent 中更新)
struct tcp_options_received: 定义在tcp.h中,其中saw_tstamp表明timestamp选项是否有效,ts_recent_stamp是我们最近一次更新 ts_recent的时间,ts_recent是下一次回显的时戳一般等于下次发包中的rcv_tsecr;rcv_tsval是该data从发端发出时的时戳值,rcv_tsecr是回显时间戳(即该ack对应的data或者该data对应的上次ack中的ts_tsval值),(注意两端时钟无需同步;当ack被收端推迟时,所回复的ack中的timestamp指向所回复包群中的第一个确认包 “When an incoming segment belongs to the current window, but arrives out of order (which implies that an earlier segment was lost), the timestamp of the earlier segment is returned as soon as it arrives, rather than the timestamp of the segment that arrived out of order.”这条细节未看明白$)从包中的时间戳选项中记录这两个值
static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) /linux/net/ipv4/tcp_input.c #2491
//处理接受到的ack,内容非常复杂
首先介绍一下ack可以携带的各个FLAG:
12345678910111213
FLAG_DATA: Incoming frame contained data.
FLAG_WIN_UPDATE: Incoming ACK was a window update
FLAG_DATA_ACKED: This ACK acknowledged new data.
FLAG_RETRANS_DATA_ACKED:Some of which was retransmitted.
FLAG_SYN_ACKED: This ACK acknowledged SYN.
FLAG_DATA_SACKED: New SACK.
FLAG_ECE: ECE in this ACK.
FLAG_DATA_LOST: SACK detected data lossage.
FLAG_SLOWPATH: Do not skip RFC checks for window update.
FLAG_ACKED: (FLAG_DATA_ACKED|FLAG_SYN_ACKED)
FLAG_NOT_DUP: (FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED)
FLAG_CA_ALERT: (FLAG_DATA_SACKED|FLAG_ECE)
FLAG_FORWARD_PROGRESS: (FLAG_ACKED|FLAG_DATA_SACKED)
//如果sack的数据段的个数为0,则说明我们要忽略调cache,此时可以看到cache指向recv_sack_cache的末尾。
if (!tp->sacked_out) {
/* It's already past, so skip checking against it */
cache = tp->recv_sack_cache + ARRAY_SIZE(tp->recv_sack_cache);
} else {
//否则取出cache,然后跳过空的块。
cache = tp->recv_sack_cache;
/* Skip empty blocks in at head of the cache */
while (tcp_sack_cache_ok(tp, cache) && !cache->start_seq &&
!cache->end_seq)
//跳过空的块。
cache++;
}
* Tag InFlight Description
* 0 1 - orig segment is in flight.
* S 0 - nothing flies, orig reached receiver.
* L 0 - nothing flies, orig lost by net.
* R 2 - both orig and retransmit are in flight.
* L|R 1 - orig is lost, retransmit is in flight.
* S|R 1 - orig reached receiver, retrans is still in flight.
这里Tag也就是上面所说的三种类型,而InFlight也就是表示还在网络中的段的个数。
然后重传队列中的skb的状态变迁是通过下面这几种事件来触发的:
1234567891011
1. New ACK (+SACK) arrives. (tcp_sacktag_write_queue())
* 2. Retransmission. (tcp_retransmit_skb(), tcp_xmit_retransmit_queue())
* 3. Loss detection event of one of three flavors:
* A. Scoreboard estimator decided the packet is lost.
* A'. Reno "three dupacks" marks head of queue lost.
* A''. Its FACK modfication, head until snd.fack is lost.
* B. SACK arrives sacking data transmitted after never retransmitted
* hole was sent out.
* C. SACK arrives sacking SND.NXT at the moment, when the
* segment was retransmitted.
* 4. D-SACK added new rule: D-SACK changes any tag to S.
Controls Appropriate Byte Count defined in RFC3465. If set to 0 then does congestion avoid once per ACK. 1 is conservative value, and 2 is more aggressive. The default value is 1.
假设某svn中心库上的某个项目foo中只有一个源码文件foo.c:
* 我在使用git-svn clone检出版本时,foo.c当时只有一个commit版本信息:"svn v1";
* clone出来后,我在本地git库中修改foo.c,并通过git commit提交到本地git库中,版本为"git v1";
* 不过与此同时另外一个同事也在修改foo.c这个文件,并已经将他的修改提交到了svn库中,版本为"svn v2";
* 此时我使用git-svn dcommit尝试提交我的改动,git-svn提示我:
Committing to svn://10.10.1.1:80/foo …
M foo.c
事务过时: 过期: ”foo/foo.c“在事务“260-1” at /usr/lib/git-core/git-svn line 570
* 使用git-svn rebase获取svn服务器上的最新foo.c,导致与foo.c冲突,不过此时svn版本信息已经添加到本地git库中(通过git log可以查看),git-svn rebase提示你在解决foo.c的冲突后,运行git rebase –continue完成rebase操作;
* 打开foo.c,修改代码,解决冲突;
* 执行git rebase –continue,git提示我:
You must edit all merge conflicts and then
mark them as resolved using git add
* 执行git add foo.c,告知git已完成冲突解决;
* 再次执行git rebase –continue,提示"Applying: git v1",此时"git v1"版本又一次成功加入本地版本库,你可通过git log查看;
* 执行git-svn dcommit将foo.c的改动同步到svn中心库,到此算是完成一次冲突解决。