内核中的UDP socket流程(1)
作者:gfree.wind@gmail.com
相对于TCP,UDP协议要简单的多。所以我决定由简入繁,先从UDP协议入手。
前一遍文章已经确定了struct sk_buff被用于socket的接受和发送缓冲。那么为了摸清linux发送数据的流程,我们就可以顺着socket中的变量sk_write_queue来从最上层的发送函数开始,一直追溯到最底层。
在追溯的过程中,我一直向上遍历到socket层。那么就从UDP socket开始进行从上到下的展示。
socket的主要处理函数都在linux/net/socket.c的文件中。
对于UDP socket来说,用户空间涉及到的API,一般常用的是socket,sendto,recvfrom,close。当然,如果还有同样是用于发送的write,send,用于接受的read,recv等等,但都大同小异。
所以,这里只研究socket,send,recv和close
Linux中的API都是使用宏SYSCALL_DEFINEx作为前缀的,其中x是该API的参数的个数。在此我并不研究这个宏是如何实现的,因为那个不是TCP/IP的重点。————有兴趣的朋友可以自己去看该宏的定义。SYSCALL_DEFINE位于linux/include/linux/syscalls.h中。
这里我们只需要记住,其中的x是API参数的个数,而该宏的第一个参数,就是API的名字——这样我们就可以很方便的找到用户空间API对应的Linux内核中的代码了。
1. 这个是API socket的定义
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
|
2. 这个是API sendto的定义
SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len, unsigned, flags, struct sockaddr __user *, addr, int, addr_len) |
3. 这个是API recvfrom的定义
SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t size, unsigned, flags, struct sockaddr __user *, addr, int __user *, addr_len)
|
第4个API close与前面三个API不同。对于用户空间的程序来说, socket返回的值同样是被当做文件描述符fd的————对于Linux来说,这是linux的一大特征,用虚拟文件层去封装底层的复杂性。无论是设备,还是socket,都可以看做文件,而在代码实现上也是如此。因为我对VFS并不熟悉,只知道大概原理,所以再细的东西我也不清楚,以后可以研究一下。
那么对于socket来说,必然有一套符合文件操作的接口。请看下面的代码
static const struct file_operations socket_file_ops = { .owner = THIS_MODULE, .llseek = no_llseek, .aio_read = sock_aio_read, .aio_write = sock_aio_write, .poll = sock_poll, .unlocked_ioctl = sock_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_sock_ioctl, #endif .mmap = sock_mmap, .open = sock_no_open, /* special open code to disallow open via /proc */ .release = sock_close, .fasync = sock_fasync, .sendpage = sock_sendpage, .splice_write = generic_splice_sendpage, .splice_read = sock_splice_read, };
|
看到这里,我们都不需要去查看struct file_operations的定义,就可以判断出socket的close动作必然是调用了sock_close。
那么
4. 这个就是API close(对于socket)的定义
static int sock_close(struct inode *inode, struct file *filp)
|
好,明天再继续看具体的API