加入收藏 | 设为首页 | 会员中心 | 我要投稿 PHP编程网 - 黄冈站长网 (http://www.0713zz.com/)- 数据应用、建站、人体识别、智能机器人、语音技术!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

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

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

分片一是第一个分片 ,包含UDP报文,在拷贝时要跳过,因为使用的是udp socket接收,只要报文内容就可以了。三张图片代表了三次调用 skb_copy_datagram_iovec()的情况,iov是存储内容的buff,最终结果是三个分片共4000字节拷贝到了iov中。

memcpy_toiovec()函数需要注意,不仅因为它改变了iovec的成员值,还因为最后的iov++。在udp socket的接收recvfrom() 中,msg.msg_iov = &iov,而iov定义成struct iovec iov,即传入参数iov实际只有一个的空间,那么在iov++后,iov将指 向非法的地址。这里只考虑udp使用时的情况,memcpy_toiovec()调用的前一句是,这里len是接收buff的长度:

if 

(copy > len)     
 copy = len;

而memcpy_toiovec()中又有int copy = min_t(unsigned int, iov->iov_len, len),这里len是上面 传入的copy,iov_len是接收buff长度,这两句保证了函数中copy值与len相等,即完成一次拷贝后,len-=copy会使len==0,虽 然iov++指向了非法内存,但由于while(len > 0)已退出,所以不会使用iov做任何事情。其次,函数中的iov++并不会对参数 iov产生影响,即函数完成iov还是传入的值。最后,拷贝完后会修改iov_len和iov_base的值,iov_len表示可用长度,iov_base 表示起始拷贝位置。

int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)     
{     
 while (len > 0) {     
  if (iov->iov_len) {
   int copy = min_t(unsigned int, iov->iov_len, len);     
   if (copy_to_user(iov->iov_base, kdata, copy))     
    return -EFAULT;     
   kdata += copy;     
   len -= copy;     
   iov->iov_len -= copy;     
   iov->iov_base += copy;     
  }     
  iov++;     
 }     
 return 0;     
}

skb_copy_and_csum_datagram_iovec()   拷贝skb内容到msg中,同时计算校验和

这个函数提高了校验和计 算效率,因为它合并了拷贝与计算操作,这样只要一次遍历操作就可以了。与skb_copy_datagram_iovec()相比,它在每次拷贝 skb内容时,计算下这次拷贝内容的校验和。

csum = csum_partial(skb->data, hlen, skb->csum);     
if (skb_copy_and_csum_datagram(skb, hlen, iov->iov_base, chunk, &csum))     
 goto fault;

UDP报文发送

发送时有两种调用方式:sys_send()和sys_sendto(),两者的区别在于sys_sendto()需 要给入目的地址的参数;而sys_send()调用前需要调用sys_connect()来绑定目的地址信息;两者的后续调用是相同的。如果调 用sys_sendto()发送,地址信息在sys_sendto()中从用户空间拷贝到内核空间,而报文内容在udp_sendmsg()中从用户空间拷贝 到内核空间。

sys_send() -> sys_sendto()

sys_sendto() -> sock_sendmsg() -> __sock_sendmsg() -> sock->ops->sendmsg()

                ==> inet_sendmsg() -> sk->sk_prot->sendmsg()

                ==> udp_sendmsg()

udp_sendmsg()的核心流程如下图所示,只列出了核心的函数调用了参数赋值,大致步骤是: 获取信息 -> 获取路由项rt -> 添加数据 -> 发送数据。

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

udp_sock结构体中的 pending用于标识当前udp_sock上是否有待发送数据,如果有的话,则直接goto do_append_data继续添加数据;否则先要做些初 始化工作,再才添加数据。实际上,pending!=0表示此调用前已经有数据在udp_sock中的,每次调和sendto()发送数据时, pending初始等于0;在添加数据时,设置up->pending = AF_INET。直到最后调用udp_push_pending_frames()将数据发送给 IP层或skb_queue_empty(&sk->sk_write_queue)发送链表上为空,这时设置up->pending = 0。因此,这里可以看到 ,报文发送时pending值的变化:

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

(编辑:PHP编程网 - 黄冈站长网)

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

热点阅读