首页 > 系统 > Linux > 正文

Linux用户态数据发送和接收

2024-08-28 00:23:08
字体:
来源:转载
供稿:网友

前言:在前面的文章中介绍了协议无关层和系统调用接口层。当用户态程序调用sendto()和recvfrom()来发送和接收数据时,其中的过程是怎么样的呢?又是经过了几次数据拷贝呢?这篇重点说明这两个接口,接着上篇来说明数据传输的过程。

1. sendto()

在上一篇中,我们知道,当在应用中调用sendto()发送函数时,就会调用到系统调用sys_sendto,在socket.c文件中,我们找到了这个系统调用的实现。
SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len,
        unsigned, flags, struct sockaddr __user *, addr,
        int, addr_len)

首先根据文件描述符找到对应的socket结构;
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (!sock)
        goto out;

然后填充要发送的消息的消息头,包括用户态数据起始地址,长度等信息。
iov.iov_base = buff;
iov.iov_len = len;
msg.msg_name = NULL;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_namelen = 0;

之后把相关的地址信息copy到内核态

最后进行发送:
err = sock_sendmsg(sock, &msg, len);
          ||
    __sock_sendmsg(&iocb, sock, msg, size);
          ||
        sock->ops->sendmsg(iocb, sock, msg, size);

到这里,我们看到调用到了协议族注册的发送函数,如果是DGRAM类型的socket,对应的INET族的发送函数是--inet_sendmsg()。我们继续沿着这条线往下走,在INET族中,DGRAM类型对应的就是UDP协议,所以,最终会调用到udp_sendmsg()中。

接下来看一下这个函数,在这里不打算仔细说这个函数(暂时还真说不清。。。太庞大了!)。只关心用户数据是怎么拷贝到内核空间,组装成udp报文的。

关于函数最开始的很多工作,先跳过,直接到标签do_append_data处:
 首先确认了得到用户态数据包的处理函数,然后就开始添加数据头。
getfrag  =  is_udplite ?  udplite_getfrag : ip_generic_getfrag;
err = ip_append_data(sk, getfrag, msg->msg_iov, ulen,
        sizeof(struct udphdr), &ipc, &rt,
        corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);

其实,拷贝用户态的数据就在getfrag函数实现的。
static __inline__ int udplite_getfrag(void *from, char *to, int  offset,
                      int len, int odd, struct sk_buff *skb)
{
    return memcpy_fromiovecend(to, (struct iovec *) from, offset, len);
}

其他的就先不多说了,留到分析传输层UDP的时候再细说。sendto也就说到这里。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表