加入收藏 | 设为首页 | 会员中心 | 我要投稿 云计算网_泰州站长网 (http://www.0523zz.com/)- 视觉智能、AI应用、CDN、行业物联网、智能数字人!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

Linux内核分析 - 网络[十二]:UDP模块 - 收发

发布时间:2016-09-26 03:21:42 所属栏目:Linux 来源:站长网
导读:副标题#e# 内核版本:2.6.34 UDP报文接收 UDP报文的接收可以分为两个部分:协议栈收到udp报文,插入相应队列中;用户 调用recvfrom()或recv()系统调用从队列中取出报文,这里的队列就是sk-sk_receive_queue,它是报文中转的纽带,两部 分的联系如下图所示。

下面来重点看核心操作的三个函数:

__skb_recv_datagram()   从sk_receive_queue上取一个skb

核心代 码段如下,skb_peek()从sk->sk_receive_queue中取出一个skb,如果有的话,则返回skb,作为用户此次接收的报文,当然 还有对skb的后续处理,但该函数只是取出一个skb;如果还没有的话,则使用wait_for_packet()等待报文到来,其中参数timeo 代表等待的时间,如果使用非阻塞接收的话,timeo会设置为0(即当前没有skb的话则直接返回,不进行等待),否则设置为sk- >sk_rcvtimeo。

do {     
 ……     
 skb = skb_peek(&sk->sk_receive_queue);     
 if (skb) {     
  *peeked = skb->peeked;     
  if (flags & MSG_PEEK) {     
   skb->peeked = 1;     
   atomic_inc(&skb->users);     
  } else 
   __skb_unlink(skb, &sk->sk_receive_queue);     
 }     
 if (skb)     
  return skb;     
……     
} while (!wait_for_packet(sk, err, &timeo));

skb_copy_datagram_iovec()   拷贝skb内容到msg中

拷贝可以分三部分:线性地址空间的拷贝,聚合/发散地址空间的拷贝,非线性地址空间的拷贝。第二部分需要硬件的支持,这 里讨论另两部分。

在skb的buff中的是线性地址空间,在skb的frag_list上的是非线性地址空间;当没有分片发生的,用线性 地址空间就足够了,但是当报文过长而分片时,第一个分片会使用线性地址空间,其余的分片将被链到skb的frag_list上,即非 线性地址空间,具体可以参考”ipv4模块”中分片部分。

拷贝报文内容时,就要将线性和非线性空间的内容都拷贝过去。下 面是拷贝线性地址空间的代码段,start是报文的线性部分长度(skb->len-skb->datalen),copy是线性地址空间的大小, offset是相对skb的偏移(即此次拷贝从哪里开始),以udp报文为例,这几个值如下图所示。memcpy_toiovec()拷贝内核到to中, 要注意的是它改变了to的成员变量。

int start = skb_headlen(skb);     
int i, copy = start - offset;     
if (copy > 0) {     
 if (copy > len)     
  copy = len;     
 if (memcpy_toiovec(to, skb->data + offset, copy))     
  goto fault;     
 if ((len -= copy) == 0)     
  return 0;     
 offset += copy;     
}

Linux内核分析 - 网络[十二]:UDP模块 - 收发

下面 是拷贝非线性地址空间的代码段,遍历skb的frag_list链表,对上面的每个分片,拷贝内容到to中,这里start, end的值不重要 ,重要的是它们的差值end-start,表示了当前分片frag_iter的长度,使用skb_copy_datagram_iovec()拷贝当前分片内容,即 把每个分片都作为单独报文来处理。不过对于分片,感觉只有拷贝的第一部分和第二部分,在IP层分片重组时,并没有将分片链 在分片的frag_list上的情况,而都链在头分片的frag_list上。

skb_walk_frags(skb, frag_iter) {     
 int end;     
 end = start + frag_iter->len;     
 if ((copy = end - offset) > 0) {     
  if (copy > len)     
   copy = len;     
  if (skb_copy_datagram_iovec(frag_iter,     
    offset - start, to, copy))     
   goto fault;     
  if ((len -= copy) == 0)     
   return 0;     
  offset += copy;     
 }     
 start = end;     
}

(编辑:云计算网_泰州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读