内核中的UDP socket流程(10)——ip_append_data

简介: 作者:gfree.wind@gmail.com博客:linuxfocus.blog.chinaunix.net现在换一种风格,把一些对代码的解读直接写到代码段。那样看起来可能更好继续ip_append_data,    /* hh_len是hardware header...
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net

现在换一种风格,把一些对代码的解读直接写到代码段。那样看起来可能更好

继续ip_append_data,
  1.     /* hh_len是hardware header的长度,也就是2层帧头长度 */
  2.     hh_len = LL_RESERVED_SPACE(rt->dst.dev);
      
     /*fragheader是3层IP报文头长度(正常报文头+IP选项的长度)*/
  1.     fragheaderlen = sizeof(struct iphdr) (opt ? opt->optlen : 0);
  2.     /* maxfraglen为4层实际数据的最大长度(不含IP报文头),为MTU减去IP头长然后对齐 */
  3.     maxfraglen = ((mtu - fragheaderlen) & ~7) fragheaderlen;
     /* 这里对长度进行检查,因为最大的数据包长为65535,如果不满足条件,则出错返回 */
  1.     if (inet->cork.length+length > 0xFFFF - fragheaderlen) {
  2.         ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->inet_dport,
  3.              mtu-exthdrlen);
  4.         return -EMSGSIZE;
  5.     }
现在还是不明白inet->cork的用法,以后一定得搞明白。
  1.     /*
  2.     第一个分片(或者是正常的普通IP数据包),长度小于MTU(即不会被分片),输出数据的网卡支持校验和,且没     有其它的报文头(exthdrlen),如IPSec头等,如果条件为真,则标识此报文由硬件做校验和。
  3.     */
  4.     /*
  5.      * transhdrlen > 0 means that this is the first fragment and we wish
  6.      * it won't be fragmented in the future.
  7.      */
  8.     if (transhdrlen &&
  9.      length + fragheaderlen = mtu &&
  10.      rt->dst.dev->features & NETIF_F_V4_CSUM &&
  11.      !exthdrlen)
  12.         csummode = CHECKSUM_PARTIAL
     /* 得到上一个数据buf */
  1.     skb = skb_peek_tail(&sk->sk_write_queue);
  1.     inet->cork.length += length;
  2.     /* 
  3.     如果数据长度超过MTU,且是UDP协议,网卡支持UDP分片,那么就进行UDP分片。
  4.     这里的skb_is_gso(skb)判断,不知道是为了什么,留待以后研究。
  5.     */
  6.     if (((length > mtu) || (skb && skb_is_gso(skb))) &&
  7.      (sk->sk_protocol == IPPROTO_UDP) &&
  8.      (rt->dst.dev->features & NETIF_F_UFO)) {
  9.         /* 进行UDP分片 */
  10.         err = ip_ufo_append_data(sk, getfrag, from, length, hh_len,
  11.                      fragheaderlen, transhdrlen, mtu,
  12.                      flags);
  13.         if (err)
  14.             goto error;
  15.         return 0;
  16.     }

  17.     /* So, what's going on in the loop below?
  18.      *
  19.      * We use calculated fragment length to generate chained skb,
  20.      * each of segments is IP fragment ready for sending to network after
  21.      * adding appropriate IP header.
  22.      */
  23.     /*
  24.     之前没有任何的数据buffer
  25.     */
  26.     if (!skb)
  27.         goto alloc_new_skb;

  28.     while (length > 0) {
  29.         /* Check if the remaining data fits into current packet. */
  30.         /* 得到剩余空间 */
  31.         copy = mtu - skb->len;
  32.         /* 当剩余空间无法容纳当前数据时 */
  33.         if (copy length)
  34.             copy = maxfraglen - skb->len;
  35.         if (copy = 0) {
  36.             /* 数据超过最大分片长度,需要分片 */
  37.             char *data;
  38.             unsigned int datalen;
  39.             unsigned int fraglen;
  40.             unsigned int fraggap;
  41.             unsigned int alloclen;
  42.             struct sk_buff *skb_prev;
  43. alloc_new_skb:
  44.             /* 检查上一个buffer中遗留的数据长度 */
  45.             skb_prev = skb;
  46.             if (skb_prev)
  47.                 fraggap = skb_prev->len - maxfraglen;
  48.             else
  49.                 fraggap = 0;

  50.             /*
  51.              * If remaining data exceeds the mtu,
  52.              * we know we need more fragment(s).
  53.              */
  54.             /* 要发送的数据长度等于本次数据长度加上上次遗留的分片数据长度 */
  55.             datalen = length + fraggap;
  56.             /* 如果超过了最大分片数据长度,那么要发送的数据长度就为最大分片数据长度-IP头长*/
  57.             if (datalen > mtu - fragheaderlen)
  58.                 datalen = maxfraglen - fragheaderlen;
  59.             /* 这里的datalen就是真正的3层IP数据, 那么fraglen就是2层数据帧的数据长度 */
  60.             fraglen = datalen + fragheaderlen;
     
             /* 
             计算要申请的内存长度:
             flags被置上MSG_MORE位,表示还有其它分片,且网卡不支持Scatter/gather IO,那么申请内存长              度就为MTU,如果条件为假,申请长度就是真实的2层数据帧的数据长度。
             
     Scatter/gather IO特性说明如下:表示IO设备可以对物理上不连续的内存进行操纵,就不用进行不必              要的拷贝使之成为连续的内存块了。            
             */
  1.             if ((flags & MSG_MORE) &&
  2.              !(rt->dst.dev->features&NETIF_F_SG))
  3.                 alloclen = mtu;
  4.             else
  5.                 alloclen = datalen + fragheaderlen;

  6.             /* The last fragment gets additional space at tail.
  7.              * Note, with MSG_MORE we overallocate on fragments,
  8.              * because we have no idea what fragment will be
  9.              * the last.
  10.              */
  11.             if (datalen == length + fraggap)
  12.                 alloclen += rt->dst.trailer_len;
             /* 申请sk_buff */
  1.             if (transhdrlen) {
  2.                 skb = sock_alloc_send_skb(sk,
  3.                         alloclen + hh_len + 15,
  4.                         (flags & MSG_DONTWAIT), &err);
  5.             } else {
  6.                 skb = NULL;
  7.                 if (atomic_read(&sk->sk_wmem_alloc) =
  8.                  2 * sk->sk_sndbuf)
  9.                     skb = sock_wmalloc(sk,
  10.                              alloclen + hh_len + 15, 1,
  11.                              sk->sk_allocation);
  12.                 if (unlikely(skb == NULL))
  13.                     err = -ENOBUFS;
  14.                 else
  15.                     /* only the initial fragment is
  16.                      time stamped */
  17.                     ipc->shtx.flags = 0;
  18.             }
  19.             if (skb == NULL)
  20.                 goto error;

  21.             /*
  22.              *    Fill in the control structures
  23.              */
  24.             skb->ip_summed = csummode;
  25.             skb->csum = 0;
  26.             skb_reserve(skb, hh_len);
  27.             *skb_tx(skb) = ipc->shtx;

  28.             /*
  29.              *    Find where to start putting bytes.
  30.              */
  31.             /* 得到可以填充数据的起始位置 */
  32.             data = skb_put(skb, fraglen);
  33.             /* 设置IP头的位置 */
  34.             skb_set_network_header(skb, exthdrlen);
  35.             /* 得到传输层报文头的位置 */
  36.             skb->transport_header = (skb->network_header +
  37.                          fragheaderlen);
  38.             data += fragheaderlen;

  39.             if (fraggap) {
  40.                 /* 将上一个分片的数据先复制到buffer中 */
  41.                 skb->csum = skb_copy_and_csum_bits(
  42.                     skb_prev, maxfraglen,
  43.                     data + transhdrlen, fraggap, 0);
  44.                 /* 调整上一个sk_buff的校验和 */
  45.                 skb_prev->csum = csum_sub(skb_prev->csum,
  46.                              skb->csum);
  47.                 data += fraggap;
  48.                 /* 这个函数不知道具体用于什么 */
  49.                 pskb_trim_unique(skb_prev, maxfraglen);
  50.             }
             /* 计算要拷贝的长度 */
  1.             copy = datalen - transhdrlen - fraggap;
  2.             /* 拷贝数据 */
  3.             if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) 0) {
  4.                 err = -EFAULT;
  5.                 kfree_skb(skb);
  6.                 goto error;
  7.             }

  8.             offset += copy;
  9.             length -= datalen - fraggap;
  10.             transhdrlen = 0;
  11.             exthdrlen = 0;
  12.             csummode = CHECKSUM_NONE;

  13.             /*
  14.              * Put the packet on the pending queue.
  15.              */
  16.             __skb_queue_tail(&sk->sk_write_queue, skb);
  17.             continue;
  18.         }
  19.         /* 未完待续 */
发现到了这个阶段,代码不太容易看懂了,因为有一些机制和概念仍然没有搞清楚,比如cork,还有buffer的组织。这估计要等到把UDP的这些代码看完,然后好好总结一下,才能明白




目录
相关文章
|
1月前
|
存储 Python
Python网络编程基础(Socket编程) UDP 发送和接收数据
【4月更文挑战第10天】对于UDP客户端而言,发送数据是一个相对简单的过程。首先,你需要构建一个要发送的数据报,这通常是一个字节串(bytes)。然后,你可以调用socket对象的`sendto`方法,将数据报发送到指定的服务器地址和端口。
|
1月前
|
存储 Python
Python网络编程基础(Socket编程)UDP客户端编程
【4月更文挑战第9天】在UDP通信中,客户端负责发送数据到服务器,并接收来自服务器的响应。与服务器不同,客户端通常不需要绑定到特定的地址和端口,因为它可以临时使用任何可用的端口来发送数据。下面,我们将详细讲解UDP客户端编程的基本步骤。
|
1月前
|
网络协议 Python
Python网络编程基础(Socket编程)创建UDP socket对象
【4月更文挑战第8天】在Python中创建UDP服务器涉及使用`socket`模块创建socket对象,如`udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)`,然后绑定到特定IP地址和端口,如`udp_socket.bind(('localhost', 12345))`。服务器通过`recvfrom`在无限循环中监听和接收数据报。这只是基础,实际应用还需处理接收、解析、响应及错误处理等。接下来可学习如何利用socket对象进行数据交互以构建完整服务器。
|
1月前
|
网络协议 网络性能优化 开发者
Python网络编程基础(Socket编程)UDP Socket编程
【4月更文挑战第8天】Python网络编程中,UDP与TCP协议各有特点。TCP提供可靠连接,确保数据顺序与完整性,适合文件传输等;UDP则无连接,速度快,常用于实时音视频,牺牲了数据可靠性。Python的socket库支持两者,开发者可根据需求选择。
|
3天前
|
算法 Linux 调度
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
9 1
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
|
18天前
|
存储 网络协议 关系型数据库
Python从入门到精通:2.3.2数据库操作与网络编程——学习socket编程,实现简单的TCP/UDP通信
Python从入门到精通:2.3.2数据库操作与网络编程——学习socket编程,实现简单的TCP/UDP通信
|
1月前
|
Python
Python网络编程基础(Socket编程)UDP服务器编程
【4月更文挑战第8天】Python UDP服务器编程使用socket库创建UDP套接字,绑定到特定地址(如localhost:8000),通过`recvfrom`接收客户端数据报,显示数据长度、地址和内容。无连接的UDP协议使得服务器无法主动发送数据,通常需应用层实现请求-响应机制。当完成时,用`close`关闭套接字。
|
1月前
|
安全 Java 数据处理
Python网络编程基础(Socket编程)多线程/多进程服务器编程
【4月更文挑战第11天】在网络编程中,随着客户端数量的增加,服务器的处理能力成为了一个重要的考量因素。为了处理多个客户端的并发请求,我们通常需要采用多线程或多进程的方式。在本章中,我们将探讨多线程/多进程服务器编程的概念,并通过一个多线程服务器的示例来演示其实现。
|
1月前
|
程序员 开发者 Python
Python网络编程基础(Socket编程) 错误处理和异常处理的最佳实践
【4月更文挑战第11天】在网络编程中,错误处理和异常管理不仅是为了程序的健壮性,也是为了提供清晰的用户反馈以及优雅的故障恢复。在前面的章节中,我们讨论了如何使用`try-except`语句来处理网络错误。现在,我们将深入探讨错误处理和异常处理的最佳实践。
|
1月前
|
Python
Python网络编程基础(Socket编程) 使用try-except处理网络错误
【4月更文挑战第11天】在网络编程中,错误处理和异常管理是非常重要的部分。网络操作经常因为各种原因而失败,比如网络断开、服务器无响应、地址不正确等。因此,学会如何使用Python的异常处理机制来捕获和处理这些错误,是编写健壮的网络应用的关键。