socket send

用户态发送函数列表

1
2
3
4
5
6
7
8
9
10
11
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);

ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

int sendmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen,
unsigned int flags);

ssize_t write(int fd, const void *buf, size_t count);

发送函数之间差别

send 有连接协议发送数据使用,send第四个参数flags为0时候,等价于write

send(sockfd, buf, len, 0) 等价 write(sockfd, buf, len)

send是sendto一部分,send可被sendto替换

send(sockfd, buf, len, flags) 等价于 sendto(sockfd, buf, len, flags, NULL, 0)

sendto 无连接和有连接发包都可以使用

sendmsg 可替换上树所有的发包函数

1
2
3
4
5
6
7
8
9
struct msghdr {
void *msg_name; /* optional address */
socklen_t msg_namelen; /* size of address */
struct iovec *msg_iov; /* scatter/gather array */
size_t msg_iovlen; /* # elements in msg_iov */
void *msg_control; /* ancillary data, see below */
size_t msg_controllen; /* ancillary data buffer len */
int msg_flags; /* flags (unused) */
};

/proc/sys/net/core/optmem_max可控制每个socket的msg_control大小
sendmsg不使用msg_flags参数

send发包过程概述

  • 阻塞模式下
    调用send函数时候,比较要发送数据和套接字发送缓冲区长度(net.ipv4.tcp_wmem);如果发送缓冲区较小,函数直接返回SOCKET_ERR;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    if send_len <= tcp_wmem{
    if is sending{
    wait
    if network err
    return SCOKET_ERR
    }
    else{
    if len > tcp_wmem left{
    wait
    if network err
    return SCOKET_ERR
    }
    else{
    copy data to tcp buf
    if copy err
    return SCOKET_ERR
    return copy data size
    }
    }
    }

    剩余缓冲区能容纳发送数据,则直接将数据拷贝到缓冲区中,send直接返回。如果剩余缓冲区不足,发送端阻塞等待,对端在协议栈层接收到数据后会发送ack确认,发送方接收到ack后释放缓冲区空间;如果此时剩余缓冲区大小可放置要发送数据,则直接将数据拷入缓冲区,返回。
    Tips:阻塞模式下,数据发送正常,其返回的数据长度一定是发送数据的长度。

  • 非阻塞模式下
    send函数将数据拷入协议栈缓冲区,如果缓冲区不足,则send尽力拷贝,并返回拷贝大小;如果缓冲区满则返回-1,同时errno为EAGAIN,让发送端再次尝试发送数据。

发送缓冲区设置

socklen_t sendbuflen = 0;  
socklen_t len = sizeof(sendbuflen);  
getsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, (void*)&sendbuflen, &len);  
printf("default,sendbuf:%d\n", sendbuflen);      
sendbuflen = 10240;  
setsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, (void*)&sendbuflen, len);  
getsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, (void*)&sendbuflen, &len);  
printf("now,sendbuf:%d\n", sendbuflen); 

send发包实例解析

实际socket使用过程中,常用的是非阻塞模式,我们就以非阻塞模式为例进行分析,预设多种场景如下:

  • 场景1:发送端10k数据已经安全放入缓冲区,已实际发出2k(收到对端ack),接收端正在处理数据,此时发送端因为10k数据发送完毕,关闭了socket。

    场景分析:

    发送端关闭socket,主动fin告诉对端发送端数据发送完毕想关闭TCP连接,发送完fin后发送端处于fin wait1状态等待接收端ack确认;发送端协议栈剩余8k数据依然在独立发送,待数据发送完成后,协议栈才会把fin发给接收端;接收端在接收ack完10k数据后,且收到fin信号后,接收端回复ack确认fin信号,两者协商关闭socket。

  • 场景2:发送端预期发送10k数据,已将2k数据拷入缓冲区并实际发出拷入的2k数据(收到对端ack),接收端正在处理数据,此时发送端又发送了8k新数据;(缓冲区充足(8k新数据会被拷入缓冲区)情况我们不讨论)缓冲区不足时候会发生什么?

    场景分析

    新发送的10k数据会尽力拷入缓冲区,send返回拷入缓冲区数据长度2k,如果此时缓冲区剩余空间为0时候,客户端强制send数据,会收到EAGAIN信号;其实这种情况客户端正确处理方式是读出缓冲区可写信号再发送数据,而不是自己进行发送尝试。

  • 场景3:发送端10k数据已经安全放入缓冲区,已实际发出2k(收到对端ack),接收端正在处理接收到1k数据,处理完成后数据接收端关闭了socket,会发发生什么?

    场景分析

    • 数据发送端有监听机制,数据发送端用户态会得到接收端端关闭信号(socket可读信号),这时候用户正确打开方式是调用close关闭socket
    • 如果数据发送端未处理该关闭信号,且数据接收端没有rst强制关闭连接,数据发送端仍然可正常发送数据
    • 如果数据发送端未处理该关闭信号,但是数据接收端已经rst强制关闭连接,数据发送端仍然在send发送数据,send将返回-1
    • 如果是阻塞情况,但是因缓冲区满正在阻塞,如果接收端发送rst,阻塞发送端会退出阻塞返回,发送成功字节数,如果在此调用send,将返回-1
  • 场景4:发送端10k数据已经安全放入缓冲区,已实际发出2k(收到对端ack),接收端正在处理接收到1k数据,此时网络出现异常

    场景分析

    接收应用程序在处理完已收到的1k数据后,会继续从缓存区读取余下的1k数据,然后就表现为无数据可读的现象,这种情况需要应用程序来处理超时.一般做法是设定一个select等待的最大时间,如果超出这个时间依然没有数据可读,则认为socket已不可用.
    发送应用程序会不断的将余下的数据发送到网络上,但始终得不到确认,所以缓存区的可用空间持续为0,这种情况也需要应用程序来处理.如果不由应用程序来处理这种情况超时的情况,也可以通过tcp协议本身来处理,具体可以查看sysctl项中的:
    net.ipv4.tcp_keepalive_intvl
    net.ipv4.tcp_keepalive_probes
    net.ipv4.tcp_keepalive_time

send特点

  • send只是将数据放入缓冲区中,并不是真正已经发给对方
  • 非阻塞发送字节可以是1-n,其发送多少完全依赖于剩余的发送缓冲区

socket发送函数解析

发送流程图

send
sendto
sendmmsg
sendmsg

上述流程调用过程如下:
->socketcall ->sock_sendmsg -> __sock_sendmsg -> sock->ops->sendmsg(inet_sendmsg)
->[tcp_prot]tcp_sendmsg

内核系统调用

send 、sendto、sendmsg、sendmmsg发送函数由glibc提供,声明于/usr/include/sys/socket.h
用户态在调用后会进入到sys_socketcall系统调用中,下面代码部分就是其入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
{
...
switch (call) {
...
case SYS_SEND:
err = sys_send(a0, (void __user *)a1, a[2], a[3]);
break;
case SYS_SENDTO:
err = sys_sendto(a0, (void __user *)a1, a[2], a[3],
(struct sockaddr __user *)a[4], a[5]);
break;
...
case SYS_SENDMSG:
err = sys_sendmsg(a0, (struct msghdr __user *)a1, a[2]);
break;
case SYS_SENDMMSG:
err = sys_sendmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3]);
break;
...
default:
err = -EINVAL;
break;
}
return err;
}

  • send 是sendto的一种特殊情况,(sendto发送地址为NULL发送地址长度为0)
1
2
3
4
5
SYSCALL_DEFINE4(send, int, fd, void __user *, buff, size_t, len,
unsigned int, flags)
{
return sys_sendto(fd, buff, len, flags, NULL, 0);
}
  • sendto -> sock_sendmsg -> __sock_sendmsg -> sock->ops->sendmsg(inet_sendmsg)
1
2
3
4
5
6
7
8
9
10
11
12
SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len,
unsigned int, flags, struct sockaddr __user *, addr,
int, addr_len)
{
...
err = sock_sendmsg(sock, &msg, len);

out_put:
fput_light(sock->file, fput_needed);
out:
return err;
}
  • sendmsg 和sendmmsg 完成用户态数据拷贝到内核态后,最终也是调用inet_sendmsg处理,在此就拿sendto情况详细分析

sendto源码实现分析

sendto -> sock_sendmsg -> “sock_sendmsg” ->”sock_sendmsg_nosec” -> sock->ops->sendmsg(inet_sendmsg)

  • 首先分析sock_sendmsg实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{
struct kiocb iocb;
struct sock_iocb siocb;
int ret;
/*异步IO控制块初始化*/
init_sync_kiocb(&iocb, NULL);
iocb.private = &siocb;
/*异步控制块调用完毕后,可调用__sock_sendmsg发送数据*/
ret = __sock_sendmsg(&iocb, sock, msg, size);
if (-EIOCBQUEUED == ret)
ret = wait_on_sync_kiocb(&iocb);
return ret;
}

static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size)
{
int err = security_socket_sendmsg(sock, msg, size);
/*调用__sock_sendmsg_nosec*/
return err ?: __sock_sendmsg_nosec(iocb, sock, msg, size);
}

static inline int __sock_sendmsg_nosec(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size)
{
struct sock_iocb *si = kiocb_to_siocb(iocb);

si->sock = sock;
si->scm = NULL;
si->msg = msg;
si->size = size;

/*调用inet_sendnsg*/
return sock->ops->sendmsg(iocb, sock, msg, size);
}

int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
size_t size)
{
struct sock *sk = sock->sk;

sock_rps_record_flow(sk);
/*如果连接没有分配本地端口且允许分配本地端口,我们就给连接绑定一个本地端口
*/
/* We may need to bind the socket. */
if (!inet_sk(sk)->inet_num && !sk->sk_prot->no_autobind &&
inet_autobind(sk))

return -EAGAIN;
/*传输层是TCP情况下,调用tcp_sendmsg()*/
return sk->sk_prot->sendmsg(iocb, sk, msg, size);
}
  • 其次分析inet_autobind ,获取可用端口并给,获取后的端口会赋值给inet->inet_sport/inet_num
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static int inet_autobind(struct sock *sk)
{
struct inet_sock *inet;
/* We may need to bind the socket. */
lock_sock(sk);
inet = inet_sk(sk);
if (!inet->inet_num) {
/*针对于TCP情况sk->sk_prot->get_port调用的是inet_csk_get_port
* inet_csk_get_port工作获取端口,并将其赋值给inet->inet_num
*/
if (sk->sk_prot->get_port(sk, 0)) {
release_sock(sk);
return -EAGAIN;
}
/*获取inet->inet_num赋值给inet->inet_sport*/
inet->inet_sport = htons(inet->inet_num);
}
release_sock(sk);
return 0;
}
  • 最后分析tcp_sendmsg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t size)
{
struct iovec *iov;
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb;
int iovlen, flags, err, copied = 0;
int mss_now = 0, size_goal, copied_syn = 0, offset = 0;
bool sg;
long timeo;

lock_sock(sk);

flags = msg->msg_flags;
if (flags & MSG_FASTOPEN) {
err = tcp_sendmsg_fastopen(sk, msg, &copied_syn, size);
if (err == -EINPROGRESS && copied_syn > 0)
goto out;
else if (err)
goto out_err;
offset = copied_syn;
}

/*
* 获取数据发送超时时间
*/
timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);

/* Wait for a connection to finish. One exception is TCP Fast Open
* (passive side) where data is allowed to be sent before a connection
* is fully established.
*/
/*
* TCP状态检查,ES和CLOSE_WAIT状态才能发送数据,其它状态都要等待连接建立起来
* 否则直接返回错误
*
* 随着协议栈进步,增加一种情况tcp_passive_fastopen即tcp被动快速打开时候,不区分当前TCP处于状态
*/
if (((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) &&
!tcp_passive_fastopen(sk)) {
/*等待连接建立,连接建立成功则返回0*/
if ((err = sk_stream_wait_connect(sk, &timeo)) != 0)
goto do_error;
}
/*开启repair功能处理*/
if (unlikely(tp->repair)) {
if (tp->repair_queue == TCP_RECV_QUEUE) {
copied = tcp_send_rcvq(sk, msg, size);
goto out_nopush;
}

err = -EINVAL;
if (tp->repair_queue == TCP_NO_QUEUE)
goto out_err;

/* 'common' sending to sendq */
}
/**/
/* This should be in poll */
clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);

/*获取发送mss*/
mss_now = tcp_send_mss(sk, &size_goal, flags);

/* Ok commence sending. */
iovlen = msg->msg_iovlen;//应用层要发送数据块个数
iov = msg->msg_iov;//要发送数据地址
copied = 0;//已经放到缓冲区的数据长度

err = -EPIPE;
/*在发送数据前,如果sk已经关闭或者出现err,直接返回 -EPIPE*/
if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
goto out_err;

/*网卡分散聚合*/
sg = !!(sk->sk_route_caps & NETIF_F_SG);

while (--iovlen >= 0) {

/*获取用户态数据长度和数据指针并指向下一个用户态要发送数据块*/
size_t seglen = iov->iov_len;
unsigned char __user *from = iov->iov_base;
iov++;

/*TCP fast open涉及*/
if (unlikely(offset > 0)) { /* Skip bytes copied in SYN */
if (offset >= seglen) {
offset -= seglen;
continue;
}
seglen -= offset;
from += offset;
offset = 0;
}

while (seglen > 0) {
int copy = 0;
int max = size_goal;
/*从发送队列尾部取skb,尝试将用户态数据放入skb->data剩余空间*/
skb = tcp_write_queue_tail(sk);
if (tcp_send_head(sk)) {

/*另一种mss情况,GSO*/
if (skb->ip_summed == CHECKSUM_NONE)
max = mss_now;
copy = max - skb->len;
}

if (copy <= 0) {/*skb已经装满数据,后续会申请新的skb来发送数据*/
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;

/*申请内存大小为select_size(线性数据区+协议头),申请失败或者不合法,睡眠等待*/
skb = sk_stream_alloc_skb(sk,
select_size(sk, sg),
sk->sk_allocation);
if (!skb)
goto wait_for_memory;

/*
* Check whether we can use HW checksum.
* 检查释放网卡硬件释放可以计算校验和
*/
if (sk->sk_route_caps & NETIF_F_CSUM_MASK)
skb->ip_summed = CHECKSUM_PARTIAL;

/*将新分配的skb入sk_write_queue数据发送队列*/
skb_entail(sk, skb);
copy = size_goal;
max = size_goal;

/* All packets are restored as if they have
* already been sent. skb_mstamp isn't set to
* avoid wrong rtt estimation.
* TCP repair
*/
if (tp->repair)
TCP_SKB_CB(skb)->sacked |= TCPCB_REPAIRED;
}

/* Try to append data to the end of skb. */
if (copy > seglen)
copy = seglen;

/* Where to copy to? */
/*如果数据还有线性区间,直接将数据拷入冰计算校验和*/
if (skb_availroom(skb) > 0) {
/* We have some space in skb head. Superb! */
copy = min_t(int, copy, skb_availroom(skb));
err = skb_add_data_nocache(sk, skb, from, copy);
if (err)
goto do_fault;
} else {/*如果没有了线性空间*/
/*
* 数据会被复制到分页中
*
*/
bool merge = true;
/*取得当前SKB的分片段数*/
int i = skb_shinfo(skb)->nr_frags;
struct page_frag *pfrag = sk_page_frag(sk);
/*检查分也可用空间,如果没有就申请新的页,如果系统内存不足就睡眠等待*/
if (!sk_page_frag_refill(sk, pfrag))
goto wait_for_memory;

/*如果不能将数据最佳到最后一个分片*/
if (!skb_can_coalesce(skb, i, pfrag->page,
pfrag->offset)) {
/*分页已经达到最大规格,将当前数据发出去,跳到new_segment重新申请skb*/
if (i == MAX_SKB_FRAGS || !sg) {
tcp_mark_push(tp, skb);
goto new_segment;
}
merge = false;
}

copy = min_t(int, copy, pfrag->size - pfrag->offset);

/*系统对发送缓冲区申请合法性判断*/
if (!sk_wmem_schedule(sk, copy))
goto wait_for_memory;
/*拷贝用户空间数据,同时计算校验和,更新数据skb长度和缓存*/
err = skb_copy_to_page_nocache(sk, from, skb,
pfrag->page,
pfrag->offset,
copy);
if (err)
goto do_error;

/* Update the skb. */
/*最后一个分页可以放数据数据页被放入了,就更新分也大小记录*/
if (merge) {
skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy);
} else {
/*如果不能分页就新增页,并初始化*/
skb_fill_page_desc(skb, i, pfrag->page,
pfrag->offset, copy);
get_page(pfrag->page);
}
pfrag->offset += copy;
}

/*如果复制数据长度为0,不用加PSH标记*/
if (!copied)
TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_PSH;
/*更新发送队列中最后一个序号,数据包的最后一个序号*/
tp->write_seq += copy;
TCP_SKB_CB(skb)->end_seq += copy;
skb_shinfo(skb)->gso_segs = 0;

/*已经拷入了copy大小数据,用户态指针后移且更新已经拷贝数据增加*/
from += copy;
copied += copy;

/*所有数据处理完毕,直接退出*/
if ((seglen -= copy) == 0 && iovlen == 0)
goto out;

/*如果skbb还可以继续填充数据或者是带外数据或者是有REPAIR选项,继续使用skb*/
if (skb->len < max || (flags & MSG_OOB) || unlikely(tp->repair))
continue;

/*检查释放必须立即发送,即检查自上次发送后产生的数据是否已经超过对方通告过的最大接收窗口的一半。如果必须发送则设置紧急数据标示,然后将数据发出去*/
if (forced_push(tp)) {
tcp_mark_push(tp, skb);
__tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH);
} else if (skb == tcp_send_head(sk))
/*数据不必立即发送,且数据上只存在这段数据,则将这段数据发出*/
tcp_push_one(sk, mss_now);

continue;

wait_for_sndbuf:
/*套接口缓冲区大小超过限制,此时无法再申请skb放数据,我们设置socket满标志*/
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);

/*系统内存不足处理*/
wait_for_memory:
/*skb分配失败了,已经拷入发送队列数据,直接调用tcp_push发出去
~MSG_MORE表示无更多数据
TCP_NAGLE_PUSH 选项调用NAGLE,尽量减少小字节发送数据
*/
if (copied)
tcp_push(sk, flags & ~MSG_MORE, mss_now,
TCP_NAGLE_PUSH, size_goal);
/*等待内存空闲,超过timeo时间后返回错误*/
if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
goto do_error;
/*啊,内存来了,重新获取MSS和TSO,继续将用户态数据拷入缓冲区*/
mss_now = tcp_send_mss(sk, &size_goal, flags);
}
}

out:
/*如果数据已经拷入发送队列,则立即发送*/
if (copied)
tcp_push(sk, flags, mss_now, tp->nonagle, size_goal);
out_nopush:
release_sock(sk);
return copied + copied_syn;

do_fault:
/*复制数据异常时才进入这里
* skb无负载数据,从发送队列上去除,并更新发送队列等参数*/
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:
/*如果已经复制了部分数据,即使发生了错误也可以发送,跳到out就是去发送数据去了*/
if (copied + copied_syn)
goto out;
out_err:
err = sk_stream_error(sk, flags, err);
release_sock(sk);
return err;
}

tcp_sendmsg()做了以下事情:

  1. 如果使用了TCP Fast Open,则会在发送SYN包的同时携带上数据。
  2. 如果连接尚未建立好,不处于ESTABLISHED或者CLOSE_WAIT状态,
    那么进程进行睡眠,等待三次握手的完成。
  3. 获取当前的MSS、网络设备支持的最大数据长度size_goal。
    如果支持GSO,size_goal会是MSS的整数倍。
  4. 遍历用户层的数据块数组:
    4.1 获取发送队列的最后一个skb,如果是尚未发送的,且长度尚未达到size_goal,
    那么可以往此skb继续追加数据。
    
    4.2 否则需要申请一个新的skb来装载数据。
    4.2.1 如果发送队列的总大小sk_wmem_queued大于等于发送缓存的上限sk_sndbuf,
    或者发送缓存中尚未发送的数据量超过了用户的设置值:
    设置同步发送时发送缓存不够的标志。
    如果此时已有数据复制到发送队列了,就尝试立即发送。
    等待发送缓存,直到sock有发送缓存可写事件唤醒进程,或者等待超时。
    
    4.2.2 申请一个skb,其线性数据区的大小为:
    通过select_size()得到的线性数据区中TCP负荷的大小 + 最大的协议头长度。
    如果申请skb失败了,或者虽然申请skb成功,但是从系统层面判断此次申请不合法,
    等待可用内存,等待时间为2~202ms之间的一个随机数。
    
    4.2.3 如果以上两步成功了,就更新skb的TCP控制块字段,把skb加入到sock发送队列的尾部,
    增加发送队列的大小,减小预分配缓存的大小。
    
    4.3 接下来就是拷贝消息头中的数据到skb中了。
    如果skb的线性数据区还有剩余空间,就复制数据到线性数据区中,同时计算校验和。
    
    4.4 如果skb的线性数据区已经用完了,那么就使用分页区:
    4.4.1 检查分页是否有可用空间,如果没有就申请新的page。如果申请失败,说明系统内存不足。
    之后会设置TCP内存压力标志,减小发送缓冲区的上限,睡眠等待内存。
    
    4.4.2 判断能否往最后一个分页追加数据。不能追加时,检查分页数是否达到了上限、
    或网卡不支持分散聚合。如果是的话,就为此skb设置PSH标志。
    然后跳转到4.2处申请新的skb,来继续填装数据。
    
    4.4.3 从系统层面判断此次分页发送缓存的申请是否合法。
    4.4.4 拷贝用户空间的数据到skb的分页中,同时计算校验和。
    更新skb的长度字段,更新sock的发送队列大小和预分配缓存。
    
    4.4.5 如果把数据追加到最后一个分页了,更新最后一个分页的数据大小。否则初始化新的分页。
    4.5 拷贝成功后更新:发送队列的最后一个序号、skb的结束序号、已经拷贝到发送队列的数据量。
    4.6 尽可能的将发送队列中的skb发送出去。

参考

http://blog.csdn.net/zhangskd/article/details/48207553