從上面的概念中可以得知,擁塞窗口可以間接反映網(wǎng)絡(luò)的狀況,進(jìn)而去限制發(fā)送窗口的大小。擁塞窗口作為網(wǎng)絡(luò)擁塞控制中核心變量之一,對(duì)網(wǎng)絡(luò)擁塞控制起到關(guān)鍵作用。在Linux內(nèi)核中,關(guān)于網(wǎng)絡(luò)的核心結(jié)構(gòu)體在:[Linux內(nèi)核網(wǎng)絡(luò)基礎(chǔ)-TCP相關(guān)的幾個(gè)關(guān)鍵結(jié)構(gòu)體-小記]中進(jìn)行了介紹,如下圖是四個(gè)核心結(jié)構(gòu)體,四個(gè)結(jié)構(gòu)的關(guān)系具有面向?qū)ο蟮奶卣?,通過層層繼承,實(shí)現(xiàn)了類的復(fù)用;內(nèi)核中網(wǎng)絡(luò)相關(guān)的很多函數(shù),參數(shù)往往都是struct sock,函數(shù)內(nèi)部依照不同的業(yè)務(wù)邏輯,將struct sock轉(zhuǎn)換為不同的業(yè)務(wù)結(jié)構(gòu)。
struct tcp_sock
從struct inet_connection_sock
結(jié)構(gòu)體的基礎(chǔ)上繼承而來,在struct inet_connection_sock
上增加了一些tcp協(xié)議相關(guān)的字段,如滑動(dòng)窗口協(xié)議,擁塞算法等一些TCP專有的屬性。由于這種繼承關(guān)系,可以互相轉(zhuǎn)換,如下舉例兩種轉(zhuǎn)換方式,第一種是struct sock轉(zhuǎn)換為struct tcp_sock,第二種是struct sock轉(zhuǎn)換成struct inet_connection_sock。。下面將struct tcp_sock展開可以看到與網(wǎng)絡(luò)擁塞控制相關(guān)的字段。
static inline struct tcp_sock *tcp_sk(const struct sock *sk)
{
return (struct tcp_sock *)sk;
}
static inline struct inet_connection_sock *inet_csk(const struct sock *sk)
{
return (struct inet_connection_sock *)sk;
}
struct tcp_sock中定義的關(guān)于網(wǎng)絡(luò)擁塞控制相關(guān)的字段如下所示:
struct tcp_sock {//在 inet_connection_sock 基礎(chǔ)上增加了 滑動(dòng)窗口 擁塞控制算法等tcp 專有 屬性
__be32 pred_flags;/*首部預(yù)測(cè)標(biāo)志 在接收到 syn 跟新窗口 等時(shí)設(shè)置此標(biāo)志 ,
此標(biāo)志和時(shí)間戳 序號(hào)等 用于判斷執(zhí)行 快速還是慢速路徑*/
u64 bytes_received; /* RFC4898 tcpEStatsAppHCThruOctetsReceived
* sum(delta(rcv_nxt)), or how many bytes
* were acked.
*/
u32 segs_in; /* RFC4898 tcpEStatsPerfSegsIn
* total number of segments in.
*/
u32 rcv_nxt; /* What we want to receive next 等待接收的下一個(gè)序列號(hào) */
u32 copied_seq; /* Head of yet unread data */
/* rcv_nxt on last window update sent最早接收但沒有確認(rèn)的序號(hào), 也就是接收窗口的左端,
在發(fā)送ack的時(shí)候, rcv_nxt更新 因此rcv_wup 更新比rcv_nxt 滯后一些 */
u32 rcv_wup;
u32 snd_nxt; /* Next sequence we send 等待發(fā)送的下一個(gè)序列號(hào) */
u32 segs_out; /* RFC4898 tcpEStatsPerfSegsOut
* The total number of segments sent.
*/
u64 bytes_acked; /* RFC4898 tcpEStatsAppHCThruOctetsAcked
* sum(delta(snd_una)), or how many bytes
* were acked.
*/
struct u64_stats_sync syncp; /* protects 64bit vars (cf tcp_get_info()) */
u32 snd_una; /* First byte we want an ack for 最早一個(gè)未被確認(rèn)的序號(hào) */
u32 snd_sml; /* Last byte of the most recently transmitted small packet 最近發(fā)送一個(gè)小于mss的最后 一個(gè)字節(jié)序列號(hào)
在成功發(fā)送, 如果報(bào)文小于mss,跟新這個(gè)字段 主要用來判斷是否啟用 nagle 算法*/
u32 rcv_tstamp; /* timestamp of last received ACK (for keepalives) 最近一次收到ack的時(shí)間 用于 tcp ?;?/
u32 lsndtime; /* timestamp of last sent data packet (for restart window) 最近一次發(fā)送 數(shù)據(jù)包時(shí)間*/
u32 last_oow_ack_time; /* timestamp of last out-of-window ACK */
u32 tsoffset; /* timestamp offset */
struct list_head tsq_node; /* anchor in tsq_tasklet.head list */
unsigned long tsq_flags;
/* Data for direct copy to user cp 數(shù)據(jù)到用戶進(jìn)程的控制塊 有用戶緩存以及其長(zhǎng)度 prequeue 隊(duì)列 其內(nèi)存*/
struct {
struct sk_buff_head prequeue // tcp 段 緩沖到此隊(duì)列 知道進(jìn)程主動(dòng)讀取才真正的處理;
struct task_struct *task;
struct msghdr *msg;
int memory;// prequeue 當(dāng)前消耗的內(nèi)存
int len;// 用戶緩存中 當(dāng)前可以使用的緩存大小
} ucopy;
u32 snd_wl1; /* Sequence for window update記錄跟新發(fā)送窗口的那個(gè)ack 段號(hào) 用來判斷是否 需要跟新窗口
如果后續(xù)收到ack大于snd_wll 則表示需要更新 窗口*/
u32 snd_wnd; /* The window we expect to receive 接收方 提供的窗口大小 也就是發(fā)送方窗口大小 */
u32 max_window; /* Maximal window ever seen from peer 接收方通告的最大窗口 */
u32 mss_cache; /* Cached effective mss, not including SACKS 發(fā)送方當(dāng)前有效的mss*/
u32 window_clamp; /* Maximal window to advertise 滑動(dòng)窗口最大值 */
u32 rcv_ssthresh; /* Current window clamp 當(dāng)前接收窗口的閾值 */
......
u32 snd_ssthresh; /* Slow start size threshold 擁塞控制 滿啟動(dòng)閾值 */
u32 snd_cwnd; /* Sending congestion window 當(dāng)前擁塞窗口大小 ---發(fā)送的擁塞窗口 */
u32 snd_cwnd_cnt; /* Linear increase counter 自從上次調(diào)整擁塞窗口后 到目前位置接收到的
總ack段數(shù) 如果該字段為0 表示調(diào)整擁塞窗口但是沒有收到ack,調(diào)整擁塞窗口之后 收到ack段就回讓
snd_cwnd_cnt 加1 */
u32 snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this snd_cwnd 的最大值*/
u32 snd_cwnd_used;//記錄已經(jīng)從隊(duì)列發(fā)送而沒有被ack的段數(shù)
u32 snd_cwnd_stamp;//記錄最近一次檢驗(yàn)cwnd 的時(shí)間; 擁塞期間 每次會(huì)檢驗(yàn)cwnd而調(diào)節(jié)擁塞窗口 ,
//在非擁塞期間,為了防止應(yīng)用層序造成擁塞窗口失效 因此在發(fā)送后 有必要檢測(cè)cwnd
u32 prior_cwnd; /* Congestion window at start of Recovery.在進(jìn)入 Recovery 狀態(tài)時(shí)的擁塞窗口 */
u32 prr_delivered; /* Number of newly delivered packets to在恢復(fù)階段給接收者新發(fā)送包的數(shù)量
* receiver in Recovery. */
u32 prr_out; /* Total number of pkts sent during Recovery.在恢復(fù)階段一共發(fā)送的包的數(shù)量 */
u32 rcv_wnd; /* Current receiver window 當(dāng)前接收窗口的大小 */
u32 write_seq; /* Tail(+1) of data held in tcp send buffer 已加入發(fā)送隊(duì)列中的最后一個(gè)字節(jié)序號(hào)*/
u32 notsent_lowat; /* TCP_NOTSENT_LOWAT */
u32 pushed_seq; /* Last pushed seq, required to talk to windows */
u32 lost_out; /* Lost packets丟失的數(shù)據(jù)報(bào) */
u32 sacked_out; /* SACK'd packets啟用 SACK 時(shí),通過 SACK 的 TCP 選項(xiàng)標(biāo)識(shí)已接收到的段的數(shù)量。
不啟用 SACK 時(shí),標(biāo)識(shí)接收到的重復(fù)確認(rèn)的次數(shù),該值在接收到確認(rèn)新數(shù)據(jù)段時(shí)被清除。 */
u32 fackets_out; /* FACK'd packets FACK'd packets 記錄 SND.UNA 與 (SACK 選項(xiàng)中目前接收方收到的段中最高序號(hào)段) 之間的段數(shù)。FACK
用 SACK 選項(xiàng)來計(jì)算丟失在網(wǎng)絡(luò)中上的段數(shù) lost_out=fackets_out-sacked_out left_out=fackets_out */
/* from STCP, retrans queue hinting */
struct sk_buff* lost_skb_hint; /*在重傳隊(duì)列中, 緩存下次要標(biāo)志的段*/
struct sk_buff *retransmit_skb_hint;/* 表示將要重傳的起始包*/
/* OOO segments go in this list. Note that socket lock must be held,
* as we do not use sk_buff_head lock.
*/
struct sk_buff_head out_of_order_queue;
/* SACKs data, these 2 need to be together (see tcp_options_write) */
struct tcp_sack_block duplicate_sack[1]; /* D-SACK block */
struct tcp_sack_block selective_acks[4]; /* The SACKS themselves*/
struct tcp_sack_block recv_sack_cache[4];
struct sk_buff *highest_sack; /* skb just after the highest
* skb with SACKed bit set
* (validity guaranteed only if
* sacked_out > 0)
*/
int lost_cnt_hint;/* 已經(jīng)標(biāo)志了多少個(gè)段 */
u32 retransmit_high; /* L-bits may be on up to this seqno 表示將要重傳的起始包 */
u32 prior_ssthresh; /* ssthresh saved at recovery start表示前一個(gè)snd_ssthresh得大小 */
u32 high_seq; /* snd_nxt at onset of congestion擁塞開始時(shí),snd_nxt的大----開始擁塞的時(shí)候下一個(gè)要發(fā)送的序號(hào)字節(jié)*/
u32 retrans_stamp; /* Timestamp of the last retransmit,
* also used in SYN-SENT to remember stamp of
* the first SYN. */
u32 undo_marker; /* snd_una upon a new recovery episode. 在使用 F-RTO 算法進(jìn)行發(fā)送超時(shí)處理,或進(jìn)入 Recovery 進(jìn)行重傳,
或進(jìn)入 Loss 開始慢啟動(dòng)時(shí),記錄當(dāng)時(shí) SND.UNA, 標(biāo)記重傳起始點(diǎn)。它是檢測(cè)是否可以進(jìn)行擁塞控制撤銷的條件之一,一般在完成
擁塞撤銷操作或進(jìn)入擁塞控制 Loss 狀態(tài)后會(huì)清零。*/
int undo_retrans; /* number of undoable retransmissions. 在恢復(fù)擁塞控制之前可進(jìn)行撤銷的重傳段數(shù)。
在進(jìn)入 FTRO 算法或 擁塞狀態(tài) Loss 時(shí),清零,在重傳時(shí)計(jì)數(shù),是檢測(cè)是否可以進(jìn)行擁塞撤銷的條件之一。*/
u32 total_retrans; /* Total retransmits for entire connection */
u32 urg_seq; /* Seq of received urgent pointer 緊急數(shù)據(jù)的序號(hào) 所在段的序號(hào)和緊急指針相加獲得*/
unsigned int keepalive_time; /* time before keep alive takes place */
unsigned int keepalive_intvl; /* time interval between keep alive probes */
int linger2;
/* Receiver side RTT estimation */
struct {
u32 rtt;
u32 seq;
u32 time;
} rcv_rtt_est;
/* Receiver queue space */
struct {
int space;
u32 seq;
u32 time;
} rcvq_space;
/* TCP-specific MTU probe information. */
struct {
u32 probe_seq_start;
u32 probe_seq_end;
} mtu_probe;
u32 mtu_info; /* We received an ICMP_FRAG_NEEDED / ICMPV6_PKT_TOOBIG
* while socket was owned by user.
*/
#ifdef CONFIG_TCP_MD5SIG
const struct tcp_sock_af_ops *af_specific;
struct tcp_md5sig_info __rcu *md5sig_info;
#endif
struct tcp_fastopen_request *fastopen_req;
struct request_sock *fastopen_rsk;
u32 *saved_syn;
};
-
Linux
+關(guān)注
關(guān)注
87文章
11292瀏覽量
209322 -
網(wǎng)絡(luò)
+關(guān)注
關(guān)注
14文章
7553瀏覽量
88729 -
控制算法
+關(guān)注
關(guān)注
4文章
166瀏覽量
21712
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論