前言:在前面的文章中介紹了協議無關層和系統調用接口層。當用戶態程序調用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也就說到這里。
新聞熱點
疑難解答