TCP 의 ACK 확인 시리즈 - 빠 른 확인
커 널 버 전: 3.15.2
나의 블 로그:http://blog.csdn.net/zhangskd
빠 른 확인 모드
(1) 빠 른 확인 모드 진입
빠 른 확인 모드 로 고 를 설정 하고 빠 른 확인 모드 에서 보 낼 수 있 는 ACK 수 를 설정 합 니 다.
static void tcp_enter_quickack_mode (struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
tcp_incr_quickack(sk); /* ACK */
icsk->icsk_ack.pingpong = 0; /* */
icsk->icsk_ack.ato = TCP_ATO_MIN; /* ACK */
}
빠 른 확인 모드 에서 보 낼 수 있 는 ACK 수량 은 제한 되 어 있 으 며, 구체 적 인 한 도 는 icsk - > icsk 입 니 다.ack.quick。
그래서 빠 른 확인 모드 에 들 어 갈 때 빠 른 속도 로 보 낼 수 있 는 ACK 수량 을 설정 해 야 합 니 다. 보통 반 개의 수신 창의 데 이 터 량 을 빠르게 확인 할 수 있 습 니 다.
그러나 최대 16 개 를 넘 지 못 해 최소 2 개 로 집계 됐다.
static void tcp_incr_quickack (struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
/* */
unsigned int quickacks = tcp_sk(sk)->rcv_wnd / (2 * icsk->icsk_ack.rcv_mss);
if (quickacks == 0)
quckacks = 2; /* 2 */
if (quickacks > icsk->icsk_ack.quick)
icsk->icsk_ack.quick = min(quickacks, TCP_MAX_QUICKACKS); /* 16 */
}
/* Maximal number of ACKs sent quickly to accelerate slow-start. */
#define TCP_MAX_QUICKACKS 16U
(2) 빠 른 확인 모드 에 있 는 지 확인한다.
신속 확인 표지 가 설정 되 어 있 고 신속 확인 모드 에서 보 낼 수 있 는 ACK 수량 이 0 이 아니라면 연결 이 신속 확인 모드 에 있 는 것 으로 판단 하고,
바로 ACK 발송 을 허용 합 니 다.
/* Send ACKs quickly, if "quick" count is not exhausted and the session is not interactive. */
static inline bool tcp_in_quickack_mode (const struct sock *sk)
{
const struct inet_connectionsock *icsk = inet_csk(sk);
/* ACK 0, */
return icsk->icsk_ack.quick && ! icsk->icsk_ack.pingpong;
}
빠 른 ACK 발송
tcp 에서rcv_established () 에 서 는 TCP 의 첫 번 째 예측 을 통과 하지 않 으 면 접 수 된 메 시 지 를 느 린 속도 로 처리 합 니 다.
받 은 메 시 지 를 처리 한 후 tcp 를 호출 합 니 다.data_snd_check () 는 데 이 터 를 보 내야 하 는 지, 캐 시 를 확대 해 야 하 는 지 확인 합 니 다.
그리고 tcp 호출ack_snd_check () 은 ACK 를 보 내야 하 는 지, 빠 른 확인 을 사용 해 야 하 는 지, 지연 확인 을 사용 해 야 하 는 지 확인 합 니 다.
마찬가지 로 TCP 첫 번 째 예측 을 통 해 빠 른 경로 에서 도 를 호출 합 니 다.tcp_ack_snd_check () 에서 빠 른 확인 이나 지연 확인 을 보 냅 니 다.
static inline void tcp_ack_snd_check(struct sock *sk)
{
/* ACK */
if (! inet_csk_ack_scheduled(sk)) {
/* We sent a data segment already. */
return;
}
__tcp_ack_snd_check(sk, 1); /* */
}
이때 다음 조건 에 부합 하면 즉시 ACK 를 발송 할 수 있 습 니 다. 즉, 빠 른 확인 을 할 수 있 습 니 다.
1. 수신 버퍼 중 하나 이상 의 전체 사이즈 데이터 세그먼트 가 NOT ACKed 이 고 수신 창 이 커 졌 습 니 다.
그래서 보통 두 개의 패 킷 을 받 은 후에 모든 패 킷 을 확인 하 는 것 이 아니 라 ACK 를 보 냅 니 다.
2. 이 때 는 빠 른 확인 모드 에 있 습 니 다.
3. 난 서 대기 열 이 비어 있 지 않 습 니 다.
/* Check if sending an ack is needed. */
static void __tcp_ack_snd_check (struct sock *sk, int ofo_possible)
{
struct tcp_sock *tp = tcp_sk(sk);
/* , ACK:
* 1. NOT ACKed, 。
* 2. 。
* 3. 。
*/
/* More than one full frame received... */
if (((tp->rcv_nxt - tp->rcv_wup) > inet_csk(sk)->icsk_ack.rcv_mss &&
/* ... and right edge of window advances far enough.
* (tcp_recvmsg() will send ACK otherwise). Or ...
*/
__tcp_select_window(sk) >= tp->rcv_wnd) ||
/* We ACK each frame or ... */
tcp_in_quickack_mode(sk) ||
/* We have out of order data. */
(ofo_possible && skb_peek(&tp->out_of_order_queue))) {
/* Then ack it now. */
tcp_send_ack(sk); /* ACK */
} else {
/* Else, send delayed ack. */
tcp_send_delayed_ack(sk); /* ACK , blog:) */
}
}
ACK 의 발송 함 수 는 tcpsend_ack (), 전송 에 실패 하면 ACK 지연 타이머 가 시 작 됩 니 다.
/* This routine sends an ack and also updates the window. */
void tcp_send_ack (struct sock *sk)
{
struct sk_buff *buff;
/* If we have been reset, we may not send again. */
if (sk->sk_state == TCP_CLOSE)
return;
/* We are not putting this on the write queue, so tcp_transmit_skb()
* will set the ownership to this sock.
*/
buff = alloc_skb(MAX_TCP_HEADER, sk_gfp_atomic(sk, GFP_ATOMIC));
if (buff == NULL) { /* skb */
inet_csk_schedule_ack(sk); /* , ACK */
inet_csk(sk)->icsk_ack.ato = TCP_ATO_MIN; /* ATO */
/* , 200ms */
inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, TCP_DELACK_MAX, TCP_RTO_MAX);
return;
}
/* Reserve space for headers and prepare control bits. */
skb_reserve(buff, MAX_TCP_HEADER); /* */
/* skb */
tcp_init_nondata_skb(buff, tcp_acceptable_seq(sk), TCPHDR_ACK);
/* Send it off, this clears delayed acks for us. */
TCP_SKB_CB(buff)->when = tcp_time_stamp;
tcp_transmit_skb(sk, buff, 0, sk_gfp_atomic(sk, GFP_ATOMIC));
}
/* , ACK 。*/
static inline void inet_csk_schedule_ack (struct sock *sk)
{
inet_csk(sk)->icsk_ack.pending |= ICSK_ACK_SCHED;
}
/* Maximal time to delay before sending an ACK.
* Delayed ACK , 200ms
*/
#define TCP_DELACK_MAX ((unsigned) (HZ/5))
/* Delayed ACK , 40ms */
#define TCP_DELACK_MIN ((unsigned) (HZ/25))
TCP_QUICKACK 옵션
TCP_QUICKACK 은 지연 확인 없 이 본 사 이 트 를 즉시 ACK 로 보 내 는 데 사 용 됩 니 다.
주의해 야 할 것 은 이 옵션 이 오래 지속 되 는 것 이 아니 라 지연 확인 모드 에 들 어 갈 수 있다 는 것 이다.
따라서 계속 빠 른 확인 이 필요 하 다 면 수신 함 수 를 호출 할 때마다 옵션 설정 을 해 야 합 니 다.
int quickack = 1; /* 빠 른 확인 을 사용 합 니 다. 할당 값 이 0 이면 사용 지연 확인 * /
setsockopt(fd, SOL_TCP, TCP_QUICKACK, &quickack, sizeof(quickack));
#define TCP_QUICKACK 12 /* Block / reenable quick acks */
static int do_tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval,
unsigned int optlen)
{
...
case TCP_QUICKACK:
if (! val) {
icsk->icsk_ack.pingpong = 1; /* */
} else {
icsk->icsk_ack.pingpong = 0; /* */
/* ACK , */
if (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT) &&
inet_csk_ack_scheduled(sk)) {
icsk->icsk_ack.pending |= ICSK_ACK_PUSHED; /* */
/* , ACK。
* , ICSK_ACK_PUSHED , ,
* ACK。
*/
tcp_cleanup_rbuf(sk, 1);
/* , 。
* 0 1, :)
*/
if (! (val & 1))
icsk->icsk_ack.pingpong = 1;
}
}
break;
...
}
수신 대기 열 에 데이터 가 사용자 공간 으로 복 사 될 때 tcp 를 호출 합 니 다.cleanup_rbuf () 는 즉시 ACK 를 보 낼 지 여 부 를 판단 합 니 다.
(1) 현재 발송 해 야 할 ACK 가 있 으 면 다음 조건 중 하 나 를 만족 시 키 면 바로 발송 할 수 있 습 니 다.
1. icsk->icsk_ack. blocked 는 1 입 니 다. 이전에 Delayed ACK 가 사용자 프로 세 스 에 의 해 차단 되 었 습 니 다.
2. 수신 버퍼 중 하나 이상 의 전체 사이즈 데이터 세그먼트 는 여전히 NOT ACKed 입 니 다.
3. 이번 사용자 공간 에 복 사 된 데 이 터 는 0 보다 많 고 다음 과 같은 조건 중 하 나 를 만족 시 킵 니 다.
3.1 ICSK 설정ACK_PUSHED 2 로고
3.2 ICSK 설정ACK_PUSHED 로고, 빠 른 확인 모드 에 있 음
(2) 원래 보 내야 할 ACK 가 없 었 다 면 현재 의 수신 창 이 현저히 커 졌 고 바로 ACK 알림 대 단 을 보 내야 합 니 다.
여기 서 현저 한 증 가 는 새로운 수신 창 크기 가 0 이 아니 라 원래 수신 창의 잉여 량 보다 배로 늘 어 난 것 을 말한다.
/* Clean up the receive buffer for full frames taken by the user,
* then send an ACK if necessary. COPIED is the number of bytes
* tcp_recvmsg has given to the user so far, it speeds up the calculation
* of whether or not we must ACK for the sake of a window update.
*/
void tcp_cleanup_rbuf (struct sock *sk, int copied)
{
struct tcp_sock *tp = tcp_sk(sk);
bool time_to_ack = false;
/* */
struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
/* copied_seq: Head of yet unread data, 。
* , 。
*/
WARN(skb && !before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq),
"cleanup rbuf bug: copied %X seq %X rcvnxt %X
", tp->copied_seq,
TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt);
/* ACK , */
if (inet_csk_ack_scheduled(sk)) {
const struct inet_connection_sock *icsk = inet_csk(sk);
/* 1. Delayed ACKs frequently hit locked sockets during bulk receive.
* 2. Once-per-two-segments ACK was not sent by tcp_input.c.
* 3. copied >0 and ICSK_ACK_PUSHED2 set.
* 4. copied > 0 and ICSK_ACK_PUSHED and in quickack mode.
*/
if (icsk->icsk_ack.blocked || tp->rcv_nxt - tp->rcv_wup > icsk->icsk_ack.rcv_mss ||
(copied > 0 && ((icsk->icsk_ack.pending & ICSK_ACK_PUSHED2) ||
((icsk->icsk_ack.pending & ICSK_ACK_PUSHED) && ! icsk->icsk_ack.pingpong)) &&
! atomic_read(&sk->sk_rmem_alloc)))
time_to_ack = true;
}
/* We send an ACK if we can now advertise a non-zero window which has
* been raised "significantly" - at least twice bigger.
* Even if window raised up to infinity, do not send window open ACK in states,
* where we will not receive more. It is useless.
*/
if (copied > 0 && ! time_to_ack && ! (sk->sk_shutdown & RCV_SHUTDOWN)) {
__u32 rcv_window_now = tcp_receive_window(tp); /* */
/* */
if (2 * rcv_window_now <= tp->window_clamp) {
/* , 。
* , , 。
*/
__u32 new_window = __tcp_select_window(sk);
/* Send ACK now, if this read freed lots of space in our buffer.
* Certainly, new_window is new window.
* We can advertise it now, if it is not less than current one.
* "Lots" means "at least twice" here.
*/
/* 0, , 。
* , ACK 。
*/
if (new_window && new_window >= 2 * rcv_window_now)
time_to_ack = true;
}
}
if (time_to_ack)
tcp_send_ack(sk); /* ACK */
}
/* */
/* Compute the actual receive window we are currently advertising.
* Rcv_nxt can be after the window if our peer push more data than
* the offered window.
*/
static inline u32 tcp_receive_window (const struct tcp_sock *tp)
{
s32 win = tp->rcv_wup + tp->rcv_wnd - tp->rcv_nxt;
if (win < 0)
win = 0;
return (u32) win;
}
다음으로 전송:https://www.cnblogs.com/aiwz/p/6333256.html
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.