分类: LINUX
2013-12-06 14:21:42
今天我们继续内核中的TCP/IP的socket的分析,同样按照我们第一节socket初始化(http://blog.chinaunix.net/u2/64681/showart.php?id=1680618 )那节的分析流程图和服务器端的练习代码继续我们的分析过程,我们看到声明了二个用于地址的结构变量,下边我们会看到这个地址数据结构的定义,下边练习程序要执行的路线是对socket地址绑定,我们来分析一下这个过程,我们看到在那节的练习中通过下面的代码来绑定地址给socket
bind(server_fd, (struct sockaddr *)&server_address, server_len); |
首先也是进入系统调用的总入口处sys_socketcall()系统调用处
case SYS_BIND: |
很明显我们要进入sys_bind()函数去执行绑定,朋友们注意我们分析的内核版本是
sys_socketcall()-->sys_bind()
|
这个函数的开头要找到上一节我们创建的socket。这是通过sockfd_lookup_light()来找到的,我们分析一下这个函数
sys_socketcall()-->sys_bind()-->
sockfd_lookup_light()
|
这里我们看到参数fd这实际上是从应用程序中传递过来的server_sockfd,那是从c库到系统调用一路传过来的,而server_sockfd是我们创建socket时的文件标识号,所以这里调用fget_light()从当前进程的files_struct结构中找到我们创建socket时的file文件指针并增加他的使用计数,关于找到file的指针,请朋友们参考相关的文件系统资料,我们这里重点围绕着socket的分析,我们看到接着调用sock_from_file函数,从名称上可以看出是根据file找到已经创建的socket
sys_socketcall()-->sys_bind()-->
sockfd_lookup_light()-->sock_from_file()
|
这里我们看一下上一节socket的创建(http://blog.chinaunix.net/u2/64681/showart.php?id=1685664 )文章的最后我们提到了非常重要的是file结构中的private_data指向了服务器端的socket,既然这里要对socket进行地址绑定所以首先是重新获取服务器端创建的socket
file->private_data = sock;
这样上面的找到socket的函数非常容易理解了,就是找到file是最关键的,而file结构中有指针指向我们创建的socket,可能有朋友被上面这里的名称sock所迷惑,这个结构变量是socket不是sock,内核中声名的这里sock的结构变量名为sk。回到sys_bind函数中,我们找到了socket下边继续往下看,看到执行了move_addr_to_kernel函数
sys_socketcall()-->sys_bind()-->
move_addr_to_kernel()
|
我们又看到了copy_from_user函数,并且kaddr就是kernel address的意思,同理,uaddr就是user address的意思,ulen就是user length的意思,这样copy_from_user的函数的作用我就不用说了,这里函数就是将我们在应用程序中创建的(struct sockaddr *)&server_address结构复制到内核空间里来,有朋友可能对内核空间和用户空间的概念模糊,请查看有关操作系统理论,这里我们看到我们在用户空间中声明的地址转换成了sockaddr结构指针,我们的server_address初始声明为struct sockaddr_in 类型,我们需要看一下相关的结构
|
上面的过程把我们练习中的地址结构变量通过move_addr_to_kernel()拷贝到局部数组变量char address[MAX_SOCK_ADDR]中。MAX_SOCK_ADDR
定义为128,
我们看一下练习中的声明的struct sockaddr_in server_address
/* Structure describing an Internet (IP) socket address. */ typedef unsigned short sa_family_t;
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
struct sockaddr_in {
sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
/* Pad to size of `struct sockaddr'. wumingxiaozu*/
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
这个结构正象注释中说明的那样用于IP的socket地址使用。而在练习中我们看到
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr("192.168.1.1
");
server_address.sin_port = 9266;
对照上面的结构我们发现还有一个__pad数组我们没看到对他的赋值和操作,这个数组主要作用填充保障我们的地址数据结构与通用的sockaddr数据结构保持相同的大小。此后我们在应用程序中设置的地址结构的数据都复制到了sys_bind()函数中的数组变量address中了,接着我们看到会执行
sock->ops->bind(sock,(struct sockaddr *) address, addrlen);
在这里应该明白了上面__pad的作用了,确实是为了将我们的数据能够正确对应到sockaddr结构的大小起填充作用的。我们回忆一下昨天讲到的socket的创建过程中对ops钩子结构的操作,在那里将socket的ops通过answer结构变量转接入了inet_stream_ops,所以这里会跳入这个钩子结构去执行,我们先看一下这个结构
const struct proto_ops inet_stream_ops = { |
结构中其他的部分我们暂且不要关心,只注意.bind= inet_bind这一句,也就是说会执行钩子函数inet_bind。篇幅所限,接下一篇