T i p^TTnlsfiToti Ipu H 11 i .Li
I n«Cif ri I
■ ep rJCPoH
i cp.interrupt
ip^queue.xait
iIp^output 图2 TCP协议栈在Linux上的实现
(以下文件路径都是在2628.7内核之下,在其它内核下有可能源码的位置不一样)
输岀过程: 1、(sock_sendmsg 在 kemel/net/socket.c) int sock_sendmsg(struct socket * sock, struct msghdr *msg, size_t size) { struct kiocb iocb; struct sock_iocb siocb; int ret;
init_sync_kiocb(&iocb, NULL); iocb.private = & siocb; ret = —sock_sendmsg(&iocb, sock, msg, size);
讦(-EIOCBQUEUED == ret)
t write ■ Sys^write
Sock_readv_wri tev .
read i i recv 1 ♦ Sya.recv
Sock.sendaBK sock.recvBsg • -T --------- Tcp^sendasg tcp.recvasg ] [ tc
p .rev. established
▼ r tcp^vi.do^rcv1 ”
tcp rcv-it«te proc«if 1 !
tcp^transait^skb tcp^v4^rcv
Ipjrcv netif^receive.skb
|| cp.Btert^iBit ! ldev queue.i»it ret = wait_on_s yn c_kiocb (&iocb); return ret; }
2、tcp_sendmsg 在 kernel/net/ipv4/tcp.c T int tcp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size) { struct sock *sk = sock->sk; struct iovec *iov; struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; int iovlen、flags; int mss_now, size_goal; int err, copied; long timeo;
lock_sock(sk); TCP_CHECK_TIMER(sk);
flags = msg->msg_flags; timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
/* Wait for a connection to finish. */ 讦((1 « sk->sk_state) & 〜(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) if ((err = sk_stream_wait_connect(sk, &timeo)) != 0) goto out_err;
/* This should be in poll */ clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); mss_now = tcp_current_mss(sk, !(flags&MSG_OOB)); size_goal = tp->x mi t_s ize_goal; /* Ok commence sending. */ iovlen = msg->msg_iovlen; iov = msg->msg_iov; copied = 0;
err = -EPIPE; if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
goto do_error;
while (—iovlen >= 0) { int seglen = iov->iov_len; unsigned char —user *from = iov->iov_base; iov++; while (seglen > 0) { int copy;
skb = tcp_write_queue_tail(sk); if (!tcp_send_head(sk) || (copy = size_goal - skb->len) <= 0) {
new_segment: /* Allocate new segment. If the interface is SG * allocate skb fitting to single page・ */ if (!sk_stream_memory_free(sk)) goto wait_fbr_sndbuf;
skb = sk_stream_alloc_skb(sk, select_size(sk), sk->sk_allocation); if(!skb) goto wait_for_memory;
/* * Check whether we can use HW checksum. */ if(sk->sk_route_caps & NETIF_F_ALL_CSUM) skb->ip_summed = CHECKSUM_PARTIAL;
skb_entail(sk, skb); copy = size_goal; 1
/* Try to append data to the end of skb. */ if (copy > seglen) copy = seglen;
/* Where to copy to? */ if (skb_tailroom(skb) > 0) { /* We have some space in skb head. Superb! */ if (copy > skb_tailroom(skb)) copy = skb_tailroom(skb); if ((err = skb_add_data(skb, from, copy)) != 0) goto do_fault; } else { int merge = 0; int i = skb_shinfo(skb)->nr_frags; struct page *page = TCP_PAGE(sk); int off=TCP_OFF(sk);
if (skb_can_coalesce(skb, i, page, off) && off != PAGE_SIZE) { /* We can extend the last page * fragment. */ merge = I; )else 讦(i == MAX_SKB_FRAGS || (!i&& ! (sk->sk_route_caps & NETIF_F_SG))) { /* Need to add new fragment and cannot * do this because interface is non-SG, * or because all the page slots are * busy. */ tcp_mark_push(tp, skb); goto new_segment; } else if (page) {
讦(off==PAGE_SIZE) { put_page(page); TCP_PAGE(sk) = page = NULL; off = 0; } } else off = 0;
讦(copy > PAGE_SIZE ・ off) copy = PAGE_SIZE - off; if (!sk_wmem_schedule(sk, copy)) goto wait_for_memory; if (!page) { /* Allocate new cache page. */ if (! (page = sk_stream_alloc_page(sk))) goto wait_for_memory;
/* Time to copy data. We are close to * the end! */ eiT = skb_copy_to_page(sk, from, skb, page, off, copy); if (err) { /* If this page was new, give it to the * socket so it does not get leaked. */ if(!TCP_PAGE(sk)) { TCP_PAGE(sk) = page; TCP_OFF(sk) = 0; } goto do_enor;
}
/* Update the skb. */ if (merge) { skb_shinfo(skb)->frags[i ・ l].size += copy; )else { skb_fill_page_desc(skb, i, page, off, copy); if (TCP_PAGE(sk)) { get_page(page); )else if (off + copy < PAGE.SIZE) {