缓冲区满时send 阻塞分析

来源:互联网 发布:分区数据恢复 编辑:程序博客网 时间:2024/06/08 14:44

1. Send 缓冲区满时阻塞代码:

Send底层调用函数tcp_sendmsg

 

tcp_sendmsg:

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

new_segment:

/* Allocate new segment. If the interface is SG,

 * allocate skb fitting to single page.

 */

if (!sk_stream_memory_free(sk))

goto wait_for_sndbuf;

 

 

  。。。。。。。。。。。。。。。。。。。。。

wait_for_sndbuf:

set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);

wait_for_memory:

if (copied)

tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);

 

if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)

goto do_error;

 

mss_now = tcp_send_mss(sk, &size_goal, flags);

}

}

 

out:

if (copied)

tcp_push(sk, flags, mss_now, tp->nonagle);

TCP_CHECK_TIMER(sk);

release_sock(sk);

return copied;

 

do_fault:

if (!skb->len) {

tcp_unlink_write_queue(skb, sk);

/* It is the one place in all of TCP, except connection

 * reset, where we can be unlinking the send_head.

 */

tcp_check_send_head(sk, skb);

sk_wmem_free_skb(sk, skb);

}

 

do_error:

if (copied)

goto out;

out_err:

err = sk_stream_error(sk, flags, err);

TCP_CHECK_TIMER(sk);

release_sock(sk);

return err;

}

 

 

 

sk_stream_wait_memory

/**

 * sk_stream_wait_memory - Wait for more memory for a socket

 * @sk: socket to wait for memory

 * @timeo_p: for how long

 */

int sk_stream_wait_memory(struct sock *sk, long *timeo_p)

{

int err = 0;

long vm_wait = 0;

long current_timeo = *timeo_p;

DEFINE_WAIT(wait);

 

if (sk_stream_memory_free(sk))

current_timeo = vm_wait = (net_random() % (HZ / 5)) + 2;

 

while (1) {

set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);

 

prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);

 

if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))   //如果调用close时,会唤醒调用send的任务,send会在此处返回

goto do_error;

if (!*timeo_p)

goto do_nonblock;

if (signal_pending(current))

goto do_interrupted;

clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);

if (sk_stream_memory_free(sk) && !vm_wait)

break;

 

set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);

sk->sk_write_pending++;

sk_wait_event(sk, ¤t_timeo, sk->sk_err ||

  (sk->sk_shutdown & SEND_SHUTDOWN) ||

  (sk_stream_memory_free(sk) &&

  !vm_wait));

sk->sk_write_pending--;

 

if (vm_wait) {

vm_wait -= current_timeo;

current_timeo = *timeo_p;

if (current_timeo != MAX_SCHEDULE_TIMEOUT &&

    (current_timeo -= vm_wait) < 0)

current_timeo = 0;

vm_wait = 0;

}

*timeo_p = current_timeo;

}

out:

finish_wait(sk->sk_sleep, &wait);

return err;

 

do_error:

err = -EPIPE;

goto out;

do_nonblock:

err = -EAGAIN;

goto out;

do_interrupted:

err = sock_intr_errno(*timeo_p);

goto out;

 

 

2. 链路出现故障时网络超时重传

2.1. Close 调用

Sock_close ->  sock_release -> inet_release -> tcp_close

 

Tcp_close

 

void tcp_close(struct sock *sk, long timeout)

调用tcp_send_fin ,启动超时定时器

 

 

2.2. 超时定时器超时处理:

 

tcp_retransmit_timer -> tcp_retransmit_timer  -> tcp_write_timeout

/* A write timeout has occurred. Process the after effects. */

static int tcp_write_timeout(struct sock *sk)

{

struct inet_connection_sock *icsk = inet_csk(sk);

int retry_until;

bool do_reset;

 

if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {

if (icsk->icsk_retransmits)

dst_negative_advice(&sk->sk_dst_cache);

retry_until = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries;

} else {

if (retransmits_timed_out(sk, sysctl_tcp_retries1)) {

/* Black hole detection */

tcp_mtu_probing(icsk, sk);

 

dst_negative_advice(&sk->sk_dst_cache);

}

 

retry_until = sysctl_tcp_retries2;

if (sock_flag(sk, SOCK_DEAD)) {

const int alive = (icsk->icsk_rto < TCP_RTO_MAX);

 

retry_until = tcp_orphan_retries(sk, alive);

do_reset = alive ||

   !retransmits_timed_out(sk, retry_until);

 

if (tcp_out_of_resources(sk, do_reset))

return 1;

}

}

 

if (retransmits_timed_out(sk, retry_until)) {

/* Has it gone just too far? */

tcp_write_err(sk);  //超时次数到

return 1;

}

return 0;

}

 

 

 

tcp_write_err -tcp_done

void tcp_done(struct sock *sk)

{

if (sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV)

TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_ATTEMPTFAILS);

 

tcp_set_state(sk, TCP_CLOSE);

tcp_clear_xmit_timers(sk);

 

sk->sk_shutdown = SHUTDOWN_MASK;

 

if (!sock_flag(sk, SOCK_DEAD))

sk->sk_state_change(sk); //调用sock_def_wakeup唤醒此socket的任务,如send任务会被唤醒

else

inet_csk_destroy_sock(sk);

}

 

 

调用close 没有马上关闭,且在客户端起来后一段时间自动关闭:

1. 网络出现问题,进入超时重传阶段,超时重传最大时间 一般对应13~30min

2.客户端起来后,服务器超时重传又发送了一次报文,超时时间到或者对端收到后发送reset (给已经重启的客户端发送报文,客户端回复reset 报文)


 

 



 

0 0
原创粉丝点击