sysctl_tcp_mem[0]:最小值

sysctl_tcp_mem[1]:压力值

sysctl_tcp_mem[2]:最大值

[java] 
static inline long sk_prot_mem_limits(const struct sock *sk, int index)
{ long *prot = sk->sk_prot->sysctl_mem; /* Cgroup相关 */ if (mem_cgroup_sockets_enabled && sk->sk_cgrp) prot = sk->sk_cgrp->sysctl_mem; return prot[index]; }

因缺少发送缓存而睡眠等待

在tcp_sendmsg()中,如果发送队列的总大小sk_wmem_queued大于等于发送缓存的上限sk_sndbuf,

或者发送缓存中尚未发送的数据量超过了用户的设置值,就进入睡眠等待。

如果申请发送缓存失败了,也会进行睡眠等待。

(1) 判断条件

sk_stream_memory_free()用来判断sock是否有剩余的发送缓存。

[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; }

(2) 睡眠等待

如果发送队列的总大小sk_wmem_queued大于等于发送缓存的上限sk_sndbuf,

或者发送缓存中尚未发送的数据量超过了用户的设置,就进入等待。

如果因为TCP层的内存不足,导致申请发送缓存失败了,也会进行睡眠等待。

Q:需要睡眠等待多长的时间呢?

需要分两种情况:

1. 等待的原因是TCP层的内存不足。

刚进入函数时,会判断sock的发送缓存是否达到了上限。

如果此时sock尚有发送缓存额度,说明是TCP层内存不足导致发送缓存申请失败的,

设置等待时间为一个2~202ms的伪随机数,超时后就结束等待。

2. 等待的原因是sock的发送缓存不足。

在睡眠的过程中,当有可用的发送缓存时,进程会被唤醒,从而结束等待。

否则达到超时时间后,返回错误。

[java] 
/* 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); /* 初始化等待任务 */ /* 如果sock还有发送缓存额度,说明是TCP层内存不足导致的。 * 初始化等待时间为一个2~202ms的伪随机数。 */ if (sk_stream_memory_free(sk)) current_timeo = vm_wait = (prandom_u32() % (HZ / 5)) + 2; while (1) { /* 设置异步发送时,发送缓存不够的标志 */ set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); /* 把等待任务加入到socket等待队列头部,把进程的状态设为TASK_INTERRUPTIBLE */ prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); /* 如果连接有错误,或者不允许发送数据了,那么返回-EPIPE */ if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN)) goto do_error; /* 如果是非阻塞的,或者等待超时了,返回-EAGAIN */ if (! *timeo_p) goto do_nonblock; /* 如果进程有待处理的信号,如果没有设置超时时间返回-ERESTARTSYS, * 否则返回-EINTR. */ if (signal_pending(current)) goto do_interrupte; clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); /* 如果sock已经有可用的发送缓存了。并满足以下任一条件: * 1. 此次等待是由于sock的发送缓存不足。 * 2. 此次等待是由于TCP层内存不足,经过了一次睡眠vm_wait设为0。 */ 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, ¤tt_timeo, sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN) || (sk_stream_memory_free(sk) && ! vm_wait)); sk->sk_write_pending--; /* 如果vm_wait不为0,睡眠2~202ms后,就把vm_wait清零了 */ if (vm_wait) { vm_wait -= current_timeo; current_timo = *timeo_p; if (current_timeo != MAX_SCHEDULE_TIMEOUT && (current_timeo -= vm_wait) < 0) current_timeo = 0; vm_wait = 0; } *timeo_p = current_timeo; /* 更新发送的超时等待时间 */ } out: /* 把等待任务从等待队列中删除,把当前进程的状态设为TASK_RUNNING */ finish_wait(sk_sleep(sk), &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; }




相关内容