概述
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 }