TCP层sendmsg系统调用的实现分析

概述

sendmsg系统调用在tcp层的实现是tcp_sendmsg函数,该函数完成以下任务:从用户空间读取数据,拷贝到内核skb,将skb加入到发送队列的任务,调用发送函数;函数在执行过程中会锁定控制块,避免软中断在tcp层的影响;函数核心流程为,在发送数据时,查看是否能够将数据合并到发送队列中最后一个skb中,如果不能合并,则新申请一个skb;拷贝过程中,如果skb的线性区域有空间,则优先使用线性区域,线性区域空间不足,则使用分页区域;拷贝完成后,调用发送函数发送数据;

代码分析
  1 int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
  2 {
  3     struct tcp_sock *tp = tcp_sk(sk);
  4     struct sk_buff *skb;
  5     struct sockcm_cookie sockc;
  6     int flags, err, copied = 0;
  7     int mss_now = 0, size_goal, copied_syn = 0;
  8     bool process_backlog = false;
  9     bool sg;
 10     long timeo;
 11 
 12     /* 加锁,避免与软中断的冲突 */
 13     lock_sock(sk);
 14 
 15     /* 获取标记 */
 16     flags = msg->msg_flags;
 17 
 18     /* fastopen和defer */
 19     if (unlikely(flags & MSG_FASTOPEN || inet_sk(sk)->defer_connect)) {
 20         err = tcp_sendmsg_fastopen(sk, msg, &copied_syn, size);
 21         if (err == -EINPROGRESS && copied_syn > 0)
 22             goto out;
 23         else if (err)
 24             goto out_err;
 25     }
 26 
 27     /* 获取阻塞时间,非阻塞为0 */
 28     timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
 29 
 30     /* 限速检查 */
 31     tcp_rate_check_app_limited(sk);  /* is sending application-limited? */
 32 
 33     /* Wait for a connection to finish. One exception is TCP Fast Open
 34      * (passive side) where data is allowed to be sent before a connection
 35      * is fully established.
 36      */
 37     /* 等待连接完成状态,fastopen的被动打开方例外 */
 38     if (((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) &&
 39         !tcp_passive_fastopen(sk)) {
 40         /* 等待连接完成 */
 41         err = sk_stream_wait_connect(sk, &timeo);
 42         if (err != 0)
 43             goto do_error;
 44     }
 45 
 46     if (unlikely(tp->repair)) {
 47         if (tp->repair_queue == TCP_RECV_QUEUE) {
 48             copied = tcp_send_rcvq(sk, msg, size);
 49             goto out_nopush;
 50         }
 51 
 52         err = -EINVAL;
 53         if (tp->repair_queue == TCP_NO_QUEUE)
 54             goto out_err;
 55 
 56         /* 'common' sending to sendq */
 57     }
 58 
 59     sockc.tsflags = sk->sk_tsflags;
 60     if (msg->msg_controllen) {
 61         err = sock_cmsg_send(sk, msg, &sockc);
 62         if (unlikely(err)) {
 63             err = -EINVAL;
 64             goto out_err;
 65         }
 66     }
 67 
 68     /* This should be in poll */
 69     /* 清除异步队列已满标记 */
 70     sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);
 71 
 72     /* Ok commence sending. */
 73     copied = 0;
 74 
 75 restart:
 76     /* 获取mss,gso情况下size_goal记录总mss=页数*mss */
 77     mss_now = tcp_send_mss(sk, &size_goal, flags);
 78 
 79     err = -EPIPE;
 80     /* 已经关闭了发送端 */
 81     if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
 82         goto do_error;
 83 
 84     /* 网卡分散聚合标记 */
 85     sg = !!(sk->sk_route_caps & NETIF_F_SG);
 86 
 87     /* 遍历发送缓存 */
 88     while (msg_data_left(msg)) {
 89         int copy = 0;
 90         int max = size_goal;
 91 
 92         /* 拿到发送队列的尾部skb */
 93         skb = tcp_write_queue_tail(sk);
 94         /* 有skb未发送 */
 95         if (tcp_send_head(sk)) {
 96             /* 网卡不支持校验和,调整最大长度 */
 97             if (skb->ip_summed == CHECKSUM_NONE)
 98                 max = mss_now;
 99             /* 该skb还能够容纳的数据长度 */
100             copy = max - skb->len;
101         }
102 
103         /* 剩余空间为0,或者不能合并,分配一个新的skb */
104         if (copy <= 0 || !tcp_skb_can_collapse_to(skb)) {
105             bool first_skb;
106 
107 new_segment:
108             /* Allocate new segment. If the interface is SG,
109              * allocate skb fitting to single page.
110              */
111             /* 空闲内存不足,进入等待 */
112             if (!sk_stream_memory_free(sk))
113                 goto wait_for_sndbuf;
114 
115             if (process_backlog && sk_flush_backlog(sk)) {
116                 process_backlog = false;
117                 goto restart;
118             }
119             first_skb = skb_queue_empty(&sk->sk_write_queue);
120 
121             /* 分配skb */
122             skb = sk_stream_alloc_skb(sk,
123                           select_size(sk, sg, first_skb),
124                           sk->sk_allocation,
125                           first_skb);
126             /* 分配失败,等待 */
127             if (!skb)
128                 goto wait_for_memory;
129 
130             process_backlog = true;
131             /*
132              * Check whether we can use HW checksum.
133              */
134             /* 网卡允许计算校验和 */
135             if (sk_check_csum_caps(sk))
136                 skb->ip_summed = CHECKSUM_PARTIAL;
137 
138             /* 添加到发送队列 */
139             skb_entail(sk, skb);
140             copy = size_goal;
141             max = size_goal;
142 
143             /* All packets are restored as if they have
144              * already been sent. skb_mstamp isn't set to
145              * avoid wrong rtt estimation.
146              */
147             if (tp->repair)
148                 TCP_SKB_CB(skb)->sacked |= TCPCB_REPAIRED;
149         }
150 
151         /* Try to append data to the end of skb. */
152         /* 拷贝的数据不能超过实际数据块的长度 */
153         if (copy > msg_data_left(msg))
154             copy = msg_data_left(msg);
155 
156         /* Where to copy to? */
157         /* 线性区域还有空间 */
158         if (skb_availroom(skb) > 0) {
159             /* We have some space in skb head. Superb! */
160             /* 取要拷贝的数量和线性区域的较小值 */
161             copy = min_t(int, copy, skb_availroom(skb));
162             /* 从用户空间拷贝到内核 */
163             err = skb_add_data_nocache(sk, skb, &msg->msg_iter, copy);
164             if (err)
165                 goto do_fault;
166         }
167         /* 线性区域没有空间,则使用分页区 */
168         else {
169             bool merge = true;
170             /* 获取页数量 */
171             int i = skb_shinfo(skb)->nr_frags;
172             /* 获取缓存的页 */
173             struct page_frag *pfrag = sk_page_frag(sk);
174 
175             /* 检查是否有足够的空间,空间不足则申请新页,失败则等待 */
176             if (!sk_page_frag_refill(sk, pfrag))
177                 goto wait_for_memory;
178 
179             /* 不能合并 */
180             if (!skb_can_coalesce(skb, i, pfrag->page,
181                           pfrag->offset)) {
182                 /* 页数量超过限制 || 网卡不支持分散聚合*/
183                 if (i >= sysctl_max_skb_frags || !sg) {
184                     /* 增加push标记,尽快处理数据 */
185                     tcp_mark_push(tp, skb);
186                     /* 新申请一个skb */
187                     goto new_segment;
188                 }
189                 /* 不合并 */
190                 merge = false;
191             }
192 
193             /* 获取能够合并的空间 */
194             copy = min_t(int, copy, pfrag->size - pfrag->offset);
195 
196             /* 发送合法性检查 */
197             if (!sk_wmem_schedule(sk, copy))
198                 goto wait_for_memory;
199 
200             /* 拷贝数据到分页 */
201             err = skb_copy_to_page_nocache(sk, &msg->msg_iter, skb,
202                                pfrag->page,
203                                pfrag->offset,
204                                copy);
205             if (err)
206                 goto do_error;
207 
208             /* Update the skb. */
209             if (merge) {
210                 /* 合并的,需要增加数据量 */
211                 skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy);
212             } else {
213                 /* 非合并的,对新页进行初始化 */
214                 skb_fill_page_desc(skb, i, pfrag->page,
215                            pfrag->offset, copy);
216                 page_ref_inc(pfrag->page);
217             }
218 
219             /* 记录新的偏移 */
220             pfrag->offset += copy;
221         }
222 
223         /* 第一次拷贝,清除push标记 */
224         if (!copied)
225             TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_PSH;
226 
227         /* 更新发送队列的最后一个序号 */
228         tp->write_seq += copy;
229         /* 更新skb结束序号 */
230         TCP_SKB_CB(skb)->end_seq += copy;
231         tcp_skb_pcount_set(skb, 0);
232 
233         /* 更新拷贝数量 */
234         copied += copy;
235 
236         /* 数据都拷贝完成 */
237         if (!msg_data_left(msg)) {
238             if (unlikely(flags & MSG_EOR))
239                 TCP_SKB_CB(skb)->eor = 1;
240             goto out;
241         }
242 
243         /* 还能继续拷贝数据 带外数据 修复模式,继续拷贝 */
244         if (skb->len < max || (flags & MSG_OOB) || unlikely(tp->repair))
245             continue;
246 
247         /* 需要使用push标记 */
248         if (forced_push(tp)) {
249             /* 打psh标记 */
250             tcp_mark_push(tp, skb);
251             /* 发送队列中的多个skb */
252             __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH);
253         } 
254         /* 否则,有数据要发送,则发送一个skb */
255         else if (skb == tcp_send_head(sk))
256             tcp_push_one(sk, mss_now);
257         continue;
258 
259 wait_for_sndbuf:
260         /* 设置空间不足标记 */
261         set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
262 wait_for_memory:
263         /* 已经有拷贝数据到发送队列,则发送之 */
264         if (copied)
265             tcp_push(sk, flags & ~MSG_MORE, mss_now,
266                  TCP_NAGLE_PUSH, size_goal);
267 
268         /* 等待内存足够 */
269         err = sk_stream_wait_memory(sk, &timeo);
270         if (err != 0)
271             goto do_error;
272 
273         /* 重新计算mss */
274         mss_now = tcp_send_mss(sk, &size_goal, flags);
275     }
276 
277 out:
278     /* 已经拷贝数据到发送队列,则发送之 */
279     if (copied) {
280         tcp_tx_timestamp(sk, sockc.tsflags, tcp_write_queue_tail(sk));
281         tcp_push(sk, flags, mss_now, tp->nonagle, size_goal);
282     }
283 out_nopush:
284     release_sock(sk);
285     return copied + copied_syn;
286 
287 do_fault:
288     /* skb中没有数据,从发送队列中删除skb */
289     if (!skb->len) {
290         tcp_unlink_write_queue(skb, sk);
291         /* It is the one place in all of TCP, except connection
292          * reset, where we can be unlinking the send_head.
293          */
294         tcp_check_send_head(sk, skb);
295         sk_wmem_free_skb(sk, skb);
296     }
297 
298 do_error:
299     if (copied + copied_syn)
300         goto out;
301 out_err:
302     err = sk_stream_error(sk, flags, err);
303     /* make sure we wake any epoll edge trigger waiter */
304     if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 &&
305              err == -EAGAIN)) {
306         sk->sk_write_space(sk);
307         tcp_chrono_stop(sk, TCP_CHRONO_SNDBUF_LIMITED);
308     }
309     release_sock(sk);
310     return err;
311 }
原文地址:https://www.cnblogs.com/wanpengcoder/p/11752183.html