在IP层的接收,提到其调用的函数是inttcp_v4_rcv(struct sk_buff *skb),其参数是包含数据信息的sk_buff。这个函数定义于:
net/ipv4/tcp_ipv4.c
1961 int tcp_v4_rcv(struct sk_buff *skb)
1962 {
1963 const struct iphdr *iph; //ip层头标识字段
1964 const struct tcphdr *th; //tcp层头标识字段
1965 struct sock *sk;
1966 int ret;
1967 struct net *net = dev_net(skb->dev); //命名空间会使用到该结构体
//PACKET_HOST指示该数据包目的是本机,类似的还有PACKET_BROADCAST等
1969 if (skb->pkt_type != PACKET_HOST)
1970 goto discard_it;
1971
1972 /* Count it even if it's bad */
1973 TCP_INC_STATS_BH(net, TCP_MIB_INSEGS); // 统计信息更新
1974
/*判断segment的头长度是否合法,有三种情况:
1、sizeof(struct tcphdr) <= skb的tcp头长度;则满足segment的tcp头长度要求。通常都会满足该要求。
2、 sizeof(struct tcphdr) > skb头+数据长度之和;出错丢弃该包。
3、 介于1、2两种情况之间的长度,这种情况在数据包分片时会遇到。
*/
1975 if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
1976 goto discard_it;
1977
/*获得传输层头*/
1978 th = tcp_hdr(skb);
1979
/*再一次判断数据包的合法性,doff是tcp包中记录的该packet的首部长度,*/
1980 if (th->doff < sizeof(struct tcphdr) / 4)
1981 goto bad_packet;
1982 if (!pskb_may_pull(skb, th->doff * 4))
1983 goto discard_it;
1984
1985 /* An explanation is required here, I think.
1986 * Packet length and doff are validated by header prediction,
1987 * provided case of th->doff==0 is eliminated.
1988 * So, we defer the checks. */
/*skb->csum:存放硬件或者软件计算的payload的checksum不包括伪头,但是是否有意义由skb->ip_summed的值决定。
CHECKSUM_UNNECESSARY表示网卡或者协议栈已经计算和验证了L4层的头和校验值。也就是计算了tcp udp的伪头。
CHECKSUM_COMPLETE表示硬件进行了计算,计算结果存储在skb->csum中。
如果硬件完成了tcp的头校验,那么skb->ip_summed & CHECKSUM_UNNECESSARY,的值等于1,这就是skb_csum_unnecessary的返回值,否则调用tcp_v4_checksum_init判断硬件是否有该特性,如果有则使用硬件计算,如果没有只能多消耗点cpu完成计算了。
*/
1989 if (!skb_csum_unnecessary(skb) && tcp_v4_checksum_init(skb))
1990 goto csum_error;
1991
/*这里再一次获得tcp的头,由于tcp_hdr返回的是指针,并且在计算校验和时可能更改过地址,所以这里再一次赋值*/
1992 th = tcp_hdr(skb);
1993 iph = ip_hdr(skb);
/*TCP 控制块初始化,这里初始化使用的都是tcp头中字段信息*/
1994 TCP_SKB_CB(skb)->seq = ntohl(th->seq);
1995 TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
1996 skb->len - th->doff * 4);
1997 TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
1998 TCP_SKB_CB(skb)->when = 0;
1999 TCP_SKB_CB(skb)->ip_dsfield = ipv4_get_dsfield(iph);
2000 TCP_SKB_CB(skb)->sacked = 0;
2001
/*查找对应的skb,也就是用户空间使用socket()创建的套接字在内核中的对应的sock*/
2002 sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
2003 if (!sk)
2004 goto no_tcp_socket;
2005
2006 process:
/*如果这是主动关闭了套接字,则套接字的状态就是TCP_TIME_WAIT,被动的一端状态是TCP_CLOSE*/
2007 if (sk->sk_state == TCP_TIME_WAIT)
2008 goto do_time_wait;
2009
/*查看其生存时间TTL(time to live)是否超时,如果超时统计一下然后丢弃*/
2010 if (unlikely(iph->ttl < inet_sk(sk)->min_ttl)) {
2011 NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
2012 goto discard_and_relse;
2013 }
2014
/*当内核定义了CONFIG_XFRM变量时,会启用xfrm安全检查,对于不满足安检的丢弃。*/
2015 if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
2016 goto discard_and_relse;
/*和桥接防火墙相关的,如果配置实用桥接防火墙,则skb中的nfct、nfct_reasm、nf_bridge字段会被使用到*/
2017 nf_reset(skb);
2018
/*套接字过滤器,如果pkt_len是0,则要丢弃该包,如果skb->len小于pkt_len则要接收该数据包,继续往下走*到2022行。/
2019 if (sk_filter(sk, skb))
2020 goto discard_and_relse;
2021
/*dev是net_device结构体,在之一文章中对该结构体已经详细描述过了*/
2022 skb->dev = NULL;
2023
/*获得套接字的嵌套自旋锁*/
2024 bh_lock_sock_nested(sk);
2025 ret = 0;
/*sock_owned_by_user会判断(sk)->sk_lock.owned,该字段用于指示sock目前是否处于用户空间,在BH或者中断情况是没有被这个函数(进程)own的,之一的文章中的NAPI就会启用BH,BH中的数据是没有被用户对应的进程拥有*/
2026 if (!sock_owned_by_user(sk)) {
/*没有被own的情况处理,如果是支持DMA操作的,则net_dma_find_channel获得DMA通道号,然后调用tcp_v4_do_rcv进行实际的数据接收*/
2027 #ifdef CONFIG_NET_DMA
2028 struct tcp_sock *tp = tcp_sk(sk);
2029 if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
2030 tp->ucopy.dma_chan = net_dma_find_channel();
2031 if (tp->ucopy.dma_chan)
2032 ret = tcp_v4_do_rcv(sk, skb);
2033 else
2034 #endif
2035 {
2036 if (!tcp_prequeue(sk, skb))
2037 ret = tcp_v4_do_rcv(sk, skb);
2038 }
/*已经被own的情况处理,需要持有per-socket锁,sk_add_backlog注释见后面*/
2039 } else if (unlikely(sk_add_backlog(sk, skb,
2040 sk->sk_rcvbuf + sk->sk_sndbuf))) {
2041 bh_unlock_sock(sk);
2042 NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP);
2043 goto discard_and_relse;
2044 }
/*释放前面获得的锁*/
2045 bh_unlock_sock(sk);
2046
/*减少sock的引用计数*/
2047 sock_put(sk);
2048
2049 return ret;
2110 }
该函数调用sk_add_backlog()接收数据包,其位于include/linux/sock.h;
backlog队列比较特殊,需要持有锁才能操作该队列,为了降低延迟,使用了一个技巧rmem_alloc用于64为体系结构的一个填充,和backlog本身没有关系。
750 /* OOB backlog add */
751 static inline void __sk_add_backlog(struct sock *sk, struct sk_buff *skb)
752 {
753 /* dont let skb dst not refcounted, we are going to leave rcu lock */
754 skb_dst_force(skb);
755
/*将skb添加到backlog上,原链表为空添加方法*/
756 if (!sk->sk_backlog.tail)
757 sk->sk_backlog.head = skb;
758 else
/*原链表非空的添加方法*/
759 sk->sk_backlog.tail->next = skb;
760
761 sk->sk_backlog.tail = skb;
762 skb->next = NULL;
763 }
770 static inline bool sk_rcvqueues_full(const struct sock *sk, const struct sk_buff *skb,
771 unsigned int limit)
772 {
/* sk->sk_backlog.len为backlog队列长度,sk_rmem_alloc是上面提到的填充字段,qsize是发送队列和接收队列的长度之和*/
773 unsigned int qsize = sk->sk_backlog.len + atomic_read(&sk->sk_rmem_alloc);
774
775 return qsize > limit;
776 }
777
778 /* The per-socket spinlock must be held here. */
779 static inline __must_check int sk_add_backlog(struct sock *sk, struct sk_buff *skb,
780 unsigned int limit)
781 {
/*队列长度不合理,则执行783行*/
782 if (sk_rcvqueues_full(sk, skb, limit))
783 return -ENOBUFS;
784
785 __sk_add_backlog(sk, skb);
786 sk->sk_backlog.len += skb->truesize;
787 return 0;
788 }
到这里没有继续调用函数了,只看到数据放在了backlog上就没有继续显示调用其它函数进行进一步的接收工作。
接下来从上至下看,在第五章中提到 tcp_recvmsg完成将数据由应用程序真正送入传输层的工作。
1560 int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
1561 size_t len, int nonblock, int flags, int *addr_len)
1562 {
/*判断sock的状态,TCP_LISTEN用于被动接收*/
1579 if (sk->sk_state == TCP_LISTEN)
1580 goto out;
1581
/*应用设置的超时值在此设置,若未设置,则超时功能不启用*/
1582 timeo = sock_rcvtimeo(sk, nonblock);
1583
1584 /* 紧急数据处理,OOB是out of band,带外信号,规范中使用带外传输紧急和重要的数据*/
1585 if (flags & MSG_OOB)
1586 goto recv_urg;
/*//待拷贝的下一个序列号*/
1603 seq = &tp->copied_seq;
/*MSG_PEEK预读标志,MSG_PEEK标志会将套接字接收队列中的可读的数据拷贝到缓冲区,但不会使套接子接收队列中的数据减少,
下一次调用recv函数
//仍然能够读到相同数据
*/
1604 if (flags & MSG_PEEK) {
1605 peek_seq = tp->copied_seq;
1606 seq = &peek_seq;
1607 }
/*应用程序设置MSG_WAITALL时,target等于len用户空间要接收的数据长度,否则置为1*/
1609 target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
1633 do {
1634 u32 offset;
1648 skb_queue_walk(&sk->sk_receive_queue, skb) {
/*已经接收到的segment和当前接收到的segment的序列号的差值*/
//如果用户的缓冲区(即用户malloc的buf)长度够大,offset一般是0。
//即 “下次准备拷贝数据的序列号”==此时获取报文的起始序列号
//什么情况下offset >0呢?很简答,如果用户缓冲区12字节,而这个skb有120字节
//那么一次recv系统调用,只能获取skb中的前12个字节,下一次执行recv系统调用
//offset就是12了,表示从第12个字节开始读取数据,前12个字节已经读取了。
//那这个"已经读取12字节"这个消息,存在哪呢?
//在*seq = &tp->copied_seq;中
1658 offset = *seq - TCP_SKB_CB(skb)->seq;
1659 if (tcp_hdr(skb)->syn)
1660 offset--;
1661 if (offset < skb->len)
1662 goto found_ok_skb;
1663 if (tcp_hdr(skb)->fin)
1664 goto found_fin_ok;
1668 }
//执行到了这里,表明小循环中break了,既然break了,说明sk_receive_queue中
//已经没有skb可以读取了
//如果没有执行到这里说明前面的小循环中执行了goto,读到有用的skb,或者读到fin都会goto。
//没有skb可以读取,说明什么?
//可能性1:当用户第一次调用recv时,压根没有数据到来
//可能性2:skb->len一共20字节,假设用户调用一次 recv,读取12字节,再调用recv,
//读取12字节,此时skb由于上次已经被读取了12字节,只剩下8字节。
//于是代码的逻辑上,再会要求获取skb,来读取剩下的8字节。
//可能性1的情况下,copied == 0,肯定不会进这个if。后续将执行休眠
//可能性2的情况下,情况比较复杂。可能性2表明数据没有读够用户想要的len长度
//虽然进程上下文中,没有读够数据,但是可能我们在读数据的时候
//软中断把数据放到backlog队列中了,而backlog对队列中的数据或许恰好让我们读够数
//据。
//copied了数据的,copied肯定>=1,而target 是1或者len
//copied只能取0(可能性1),或者0~len(可能性2)
//copied >= target 表示我们取得我们想要的数据了,何必进行休眠,直接return
//如果copied 没有达到我们想要的数据,则看看sk_backlog是否为空
//空的话,尽力了,只能尝试休眠
//非空的话,还有一线希望,我们去sk_backlog找找数据,看看是否能够达到我们想要的
//数据大小
//我觉得copied == target是会出现的,但是出现的话,也不会进现在这个流程
//说明情况下copied == target
1670 /* Well, if we have backlog, try to process it now yet. */
1671
1672 if (copied >= target && !sk->sk_backlog.tail)
1673 break;
1674
1675 if (copied) {
//可能性2,拷贝了数据,但是没有拷贝到指定大小
1676 if (sk->sk_err ||
1677 sk->sk_state == TCP_CLOSE ||
1678 (sk->sk_shutdown & RCV_SHUTDOWN) ||
1679 !timeo ||
1680 signal_pending(current))
1681 break;
1682 } else {
//可能性1
1683 if (sock_flag(sk, SOCK_DONE))
1684 break;
1685
1686 if (sk->sk_err) {
1687 copied = sock_error(sk);
1688 break;
1689 }
1690
1691 if (sk->sk_shutdown & RCV_SHUTDOWN)
1692 break;
1693
1694 if (sk->sk_state == TCP_CLOSE) {
1695 if (!sock_flag(sk, SOCK_DONE)) {
1696 /* This occurs when user tries to read
1697 * from never connected socket.
1698 */
1699 copied = -ENOTCONN;
1700 break;
1701 }
1702 break;
1703 }
1704 //是否是阻塞的,不是,就return了。
1705 if (!timeo) {
1706 copied = -EAGAIN;
1707 break;
1708 }
1709
1710 if (signal_pending(current)) {
1711 copied = sock_intr_errno(timeo);
1712 break;
1713 }
1714 }
1716 tcp_cleanup_rbuf(sk, copied);
1717
1718 if (!sysctl_tcp_low_latency && tp->ucopy.task == user_recv) {
1719 /* Install new reader */
1720 if (!user_recv && !(flags & (MSG_TRUNC | MSG_PEEK))) {
1721 user_recv = current;
1722 tp->ucopy.task = user_recv;
1723 tp->ucopy.iov = msg->msg_iov;
1724 }
1725
1726 tp->ucopy.len = len;
1757 if (!skb_queue_empty(&tp->ucopy.prequeue))
1758 goto do_prequeue;
1759
1760 /* __ Set realtime policy in scheduler __ */
1761 }
1773 if (copied >= target) {
1774 /* Do not sleep, just process backlog. */
1775 release_sock(sk);
1776 lock_sock(sk);
1777 } else
//在此处睡眠了,将在tcp_prequeue函数中调用wake_up_interruptible_poll唤醒
1778 sk_wait_data(sk, &timeo);
//软中断会判断用户是正在读取检查并且睡眠了,如果是的话,就直接把数据拷贝
//到prequeue队列,然后唤醒睡眠的进程。因为进程睡眠,表示没有读到想要的字节数
//此时,软中断有数据到来,直接给进程,这样进程就能以最快的速度被唤醒。
1785 if (user_recv) {
1786 int chunk;
1787
1788 /* __ Restore normal policy in scheduler __ */
1789
1790 if ((chunk = len - tp->ucopy.len) != 0) {
1791 NET_ADD_STATS_USER(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMBACKLOG, chunk);
1792 len -= chunk;
1793 copied += chunk;
1794 }
1795
1796 if (tp->rcv_nxt == tp->copied_seq &&
1797 !skb_queue_empty(&tp->ucopy.prequeue)) {
1798 do_prequeue:
1799 tcp_prequeue_process(sk);
1800
1801 if ((chunk = len - tp->ucopy.len) != 0) {
1802 NET_ADD_STATS_USER(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMPREQUEUE, chunk);
1803 len -= chunk;
1804 copied += chunk;
1805 }
1806 }
1807 }
1808 if ((flags & MSG_PEEK) &&
1809 (peek_seq - copied - urg_hole != tp->copied_seq)) {
1810 net_dbg_ratelimited("TCP(%s:%d): Application bug, race in MSG_PEEK\n",
1811 current->comm,
1812 task_pid_nr(current));
1813 peek_seq = tp->copied_seq;
1814 }
1815 continue;
1816
//skb中还有多少聚聚没有拷贝。
//正如前面所说的,offset是上次已经拷贝了的,这次从offset开始接下去拷贝
1817 found_ok_skb:
1818 /* Ok so how much can we use? */
1819 used = skb->len - offset;
//很有可能used的大小,即skb剩余长度,依然大于用户的缓冲区大小(len)。所以依然
//只能拷贝len长度。一般来说,用户还得执行一次recv系统调用。直到skb中的数据读完
1820 if (len < used)
1821 used = len;
1822
1823 /* Do we have urgent data here? */
1824 if (tp->urg_data) {
1825 u32 urg_offset = tp->urg_seq - *seq;
1826 if (urg_offset < used) {
1827 if (!urg_offset) {
1828 if (!sock_flag(sk, SOCK_URGINLINE)) {
1829 ++*seq;
1830 urg_hole++;
1831 offset++;
1832 used--;
1833 if (!used)
1834 goto skip_copy;
1835 }
1836 } else
1837 used = urg_offset;
1838 }
1839 }
1840
1841 if (!(flags & MSG_TRUNC)) {
1870 {
//一般都会进这个if,进行数据的拷贝,把能够读到的数据,放到用户的缓冲区
1871 err = skb_copy_datagram_iovec(skb, offset,
1872 msg->msg_iov, used);
1873 if (err) {
1874 /* Exception. Bailout! */
1875 if (!copied)
1876 copied = -EFAULT;
1877 break;
1878 }
1879 }
1880 }
//更新标志位,seq 是指针,指向了tp->copied_seq
//used是我们有能力拷贝的数据大小,即已经拷贝到用户缓冲区的大小
//正如前面所说,如果用户的缓冲区很小,一次recv拷贝不玩skb中的数据,
//我们需要保存已经拷贝了的大小,下次recv时,从这个大小处继续拷贝。
//所以需要更新copied_seq。
1882 *seq += used;
1883 copied += used;
1884 len -= used;
1885
1886 tcp_rcv_space_adjust(sk);
1887
1888 skip_copy:
1889 if (tp->urg_data && after(tp->copied_seq, tp->urg_seq)) {
1890 tp->urg_data = 0;
1891 tcp_fast_path_check(sk);
1892 }
//这个就是判断我们是否拷贝完了skb中的数据,如果没有continue
//这种情况下,len经过 len -= used; ,已经变成0,所以continue的效果相当于
//退出了这个大循环。可以理解,你只能拷贝len长度,拷贝完之后,那就return了。
//还有一种情况used + offset == skb->len,表示skb拷贝完了。这时我们只需要释放skb
//下面会讲到
1893 if (used + offset < skb->len)
1894 continue;
//看看这个数据报文是否含有fin,含有fin,则goto到found_fin_ok
1896 if (tcp_hdr(skb)->fin)
1897 goto found_fin_ok;
//执行到这里,标明used + offset == skb->len,报文也拷贝完了,那就把skb摘链释放
1898 if (!(flags & MSG_PEEK)) {
1899 sk_eat_skb(sk, skb, copied_early);
1900 copied_early = false;
1901 }
//这个cintinue不一定是退出大循环,可能还会执行循环。
//假设用户设置缓冲区12字节,你skb->len长度20字节。
//第一次recv读取了12字节,skb剩下8,下一次调用recv再想读取12,
//但是只能读取到这8字节了。
//此时len 变量长度为4,那么这个continue依旧在这个循环中,
//函数还是再次从do开始,使用skb_queue_walk,找skb
//如果sk_receive_queue中skb仍旧有,那么继续读,直到len == 0
//如果没有skb了,我们怎么办?我们的len还有4字节怎么办?
//这得看用户设置的recv函数阻塞与否,即和timeo变量相关了。
1902 continue;
1903
1904 found_fin_ok:
1905 /* Process the FIN. */
1906 ++*seq;
1907 if (!(flags & MSG_PEEK)) {
//把skb从sk_receive_queue中摘链
1908 sk_eat_skb(sk, skb, copied_early);
1909 copied_early = false;
1910 }
1911 break;
1912 } while (len > 0);
1913
//到这里是大循环退出
//休眠过的进程,然后退出大循环 ,才满足 if (user_recv) 条件
1914 if (user_recv) {
1915 if (!skb_queue_empty(&tp->ucopy.prequeue)) {
1916 int chunk;
1917
1918 tp->ucopy.len = copied > 0 ? len : 0;
1919
1920 tcp_prequeue_process(sk);
1921
1922 if (copied > 0 && (chunk = len - tp->ucopy.len) != 0) {
1923 NET_ADD_STATS_USER(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMPREQUEUE, chunk);
1924 len -= chunk;
1925 copied += chunk;
1926 }
1927 }
1928 //数据读取完毕,清零
1929 tp->ucopy.task = NULL;
1930 tp->ucopy.len = 0;
1931 }
1943 /* According to UNIX98, msg_name/msg_namelen are ignored
1944 * on connected socket. I was just happy when found this 8) --ANK
1945 */
1946
1947 /* Clean up data we have read: This will do ACK frames. /
//很重要,将更新缓存,并且适当的时候发送ack
1948 tcp_cleanup_rbuf(sk, copied);
1949
1950 release_sock(sk);
1951 return copied;
1952
1953 out:
1954 release_sock(sk);
1955 return err;
1956
1957 recv_urg:
1958 err = tcp_recv_urg(sk, msg, len, flags);
1959 goto out;
1960
1961 recv_sndq:
1962 err = tcp_peek_sndq(sk, msg, len);
1963 goto out;
1964 }