Linux内核分析 - 网络[十二]:UDP模块 - 收发
分片一是第一个分片 ,包含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 -> 添加数据 -> 发送数据。 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值的变化: (编辑:PHP编程网 - 黄冈站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |