TCP的发送系列 — 发送缓存的管理(二)(1)(3)
因有发送缓存可写事件而被唤醒
sk->sk_write_space的实例为sock_def_write_space()。
如果socket是SOCK_STREAM类型的,那么函数指针的值会更新为sk_stream_write_space()。
sk_stream_write_space()在TCP中的调用路径为:
tcp_rcv_established / tcp_rcv_state_process
tcp_data_snd_check
tcp_check_space
tcp_new_space
[java] static void tcp_check_space(struct sock *sk)
{ /* 如果发送队列中有skb被释放了 */ if (sock_flag(sk, SOCK_QUEUE_SHRUNK)) { sock_reset_flag(sk, SOCK_QUEUE_SHRUNK); /* 如果设置了同步发送时,发送缓存不足的标志 */ if (sk->sk_socket && test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) tcp_new_space(sk); /* 更新发送缓存 */ } }
[java] /* When incoming ACK allowed to free some skb from write_queue, * we remember this event in flag SOCK_QUEUE_SHRUNK and wake up socket * on the exit from tcp input handler. */ static void tcp_new_space(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); if (tcp_should_expand_sndbuf(sk)) { tcp_sndbuf_expand(sk); tp->snd_cwnd_stamp = tcp_time_stamp; } /* 检查是否需要触发有缓存可写事件 */ sk->sk_write_space(sk); }
[java] void sk_stream_write_space(struct sock *sk) { struct socket *sock = sk->sk_socket; struct socket_wq *wq; /* 等待队列和异步通知队列 */ /* 如果剩余的发送缓存不低于发送缓存上限的1/3,且尚未发送的数据不高于一定值时 */ if (sk_stream_is_writeable(sk) && sock) { clear_bit(SOCK_NOSPACE, &sock->flags); /* 清除发送缓存不够的标志 */ rcu_read_lock(); wq = rcu_dereference(sk->sk_wq); /* socket的等待队列和异步通知队列 */ if (wq_has_sleeper(wq)) /* 如果等待队列不为空,则唤醒一个睡眠进程 */ wake_up_interruptible_poll(&wq->wait, POLLOUT | POLLWRNORM | POLLWRBAND); /* 异步通知队列不为空,且允许发送数据时。 * 检测sock的发送队列是否曾经到达上限,如果有的话发送SIGIO信号,告知异步通知队列上 * 的进程有发送缓存可写。 */ if (wq && wq->fasync_list && !(sk->sk_shutdown & SEND_SHUTDOWN)) sock_wake_async(sock, SOCK_WAKE_SPACE, POLL_OUT); rcu_read_unlock(); } } #define wake_up_interruptible_poll(x, m) \ __wake_up(x, TASK_INTERRUPTIBLE, 1, (void *) (m))
如果剩余的发送缓存大于发送缓存上限的1/3,且尚未发送的数据少于一定值时,才会触发有发送
缓存可写的事件。
[java] view plaincopystatic inline bool sk_stream_is_writeable(const struct sock *sk) { return sk_stream_wspace(sk) >= sk_stream_min_wspace(sk) && sk_stream_memory_free(sk); } static inline int sk_stream_wspace(const struct sock *sk) { return sk->sk_sndbuf - sk->sk_wmem_queued; } static inline int sk_stream_min_wspace(const struct sock *sk) { return sk->sk_wmem_queued >> 1; } 检查尚未发送的数据是否已经够多了,如果超过了用户设置的值,就不用触发有发送缓存可写事件, 以免使用过多的内存。 [java] static inline bool sk_stream_memory_free(const struct sock *sk)
{ if (sk->sk_wmem_queued >= sk->sk_sndbuf) return false; return sk->sk_prot->stream_memory_free ? sk->sk_prot->stream_memory_free(sk) : true; } static inline bool tcp_stream_memory_free(const struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); u32 notsent_bytes = tp->write_seq - tp->snd_nxt; /* 尚未发送的数据大小 */ /* 当尚未发送的数据,少于配置的值时,才触发有发送缓存可写的事件。 * 这是为了避免发送缓存占用过多的内存。 */ return notsent_bytes < tcp_notsent_lowat(tp); }
如果有使用TCP_NOTSENT_LOWAT选项,则使用用户设置的值。
否则使用sysctl_tcp_notsent_lowat,默认为无穷大。
[java] static inline u32 tcp_notsent_lowat(const struct tcp_sock *tp)
{ return tp->notsent_lowat ?: sysctl_tcp_notsent_lowat; }
评论暂时关闭