分类:
2011-10-28 17:05:50
原文地址:tcp/ip协议socket创建详解 作者:sjj0412
------------------------------------------
要分析套接字就不的不提到inode。套接字可以和文件关联,当然是和特殊的文件系统关联,这样就可象操作文件一样操作网络,给应用程序用户提供了极大的便利。既然可以是文件操作,就少不了INODE,由于要和socket关联,那么inode中应该有于socket相关的,事实上确实如此。
struct inode {
union {
struct minix_inode_info minix_i;
struct ext2_inode_info ext2_i;
struct ext3_inode_info ext3_i;
struct hpfs_inode_info hpfs_i;
struct ntfs_inode_info ntfs_i;
struct msdos_inode_info msdos_i;
struct umsdos_inode_info umsdos_i;
struct iso_inode_info isofs_i;
struct nfs_inode_info nfs_i;
struct sysv_inode_info sysv_i;
struct affs_inode_info affs_i;
struct ufs_inode_info ufs_i;
struct efs_inode_info efs_i;
struct romfs_inode_info romfs_i;
struct shmem_inode_info shmem_i;
struct coda_inode_info coda_i;
struct smb_inode_info smbfs_i;
struct hfs_inode_info hfs_i;
struct adfs_inode_info adfs_i;
struct qnx4_inode_info qnx4_i;
struct reiserfs_inode_info reiserfs_i;
struct bfs_inode_info bfs_i;
struct udf_inode_info udf_i;
struct ncp_inode_info ncpfs_i;
struct proc_inode_info proc_i;
struct socket socket_i;
struct usbdev_inode_info usbdev_i;
struct jffs2_inode_info jffs2_i;
void *generic_ip;
} u;
};
上面的u联合体中的socket_i就将socket和inode联系起来了。
有了上面的一点基础,现在我们就以tcp协议的套接字创建为例来分析socket创建过程。
当我们在应用程序调用API函数socket(AF_INET,SOCK_RAW,IPPROTO_TCP)时就会调用socket的系统调用进入统一的入口函数sys_socketcall,如果是创建套接字,就会调用sys_socket,sys_socket然后就调用sock_create,这个才是真正执行socket创建的函数。
Sys_socketcall->sys_socket->sock_create()-
int sock_create(int family, int type, int protocol, struct socket **res)
{
………
……..
if (!(sock = sock_alloc()))
{
printk(KERN_WARNING "socket: no more sockets\n");
i = -ENFILE; /* Not exactly a match, but its the
closest posix thing */
goto out;
}
……..
………
}
Sock_create函数调用socket_alloc创建socket,主要分配两个主要数据,一个是套接字socket,另一个是socket对应的inode。
Sys_socketcall->sys_socket->sock_create()->sock_alloc
struct socket *sock_alloc(void)
{
struct inode * inode;
struct socket * sock;
//为当前套接字分配Inode
inode = get_empty_inode();
if (!inode)
return NULL;
//为套接字对应的Inode指定其文件系统的超级块,这个超级块其实不存在
//sock_mnt在kern_mount中创建,是具体文件系统的全局变量。
inode->i_sb = sock_mnt->mnt_sb;
// 获取Inode对应的socket结构来初始化
sock = socki_lookup(inode);//
inode->i_mode = S_IFSOCK|S_IRWXUGO;
inode->i_sock = 1;
inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid;
sock->inode = inode;
init_waitqueue_head(&sock->wait);
sock->fasync_list = NULL;
sock->state = SS_UNCONNECTED;
sock->flags = 0;
sock->ops = NULL;
sock->sk = NULL;
sock->file = NULL;
sockets_in_use[smp_processor_id()].counter++;
return sock;
}
首先分配SOCK文件的INODE,然后将其与sockfs的sb联系起来,分配inode时其实已经分配了socket结构体,因为socket结构体是inode的成员,然后就初始化Inode和对应socket。Socket数据结构如下。
struct socket
{
socket_state state;
unsigned long flags;
struct proto_ops *ops;
//文件系统相关的接口file,inode
struct inode *inode;
struct fasync_struct *fasync_list; /* Asynchronous wake up list */
struct file *file; /* File back pointer for gc */
//网络关联的数据接口在sock中
struct sock *sk;
wait_queue_head_t wait;
short type;
unsigned char passcred;
};
我们知道socket是只是网络和文件系统关联的接口抽象,没有网络的相关信息,从socket的数据结构容易看出,所以还有另一个网络抽象的重要的数据结构sock。
因此所以肯定有一个创建sock的函数,这个函数就是在sock_alloc返回到sock_Create中调用的
Sock_create:(){
………
if ((i = net_families[family]->create(sock, protocol)) < 0)
{
sock_release(sock);
goto out;
}
……….
……….
}
调用具体family(ip,ipx等网络层协议)的create函数,net_families[]数组的元素代表一种网络层协议(ip,ipx)等的相关数据及操作,create函数就是其中一个操作。
在这里是调用inet_create,inet_create创建sock,并根据具体协议初始化它。
Sys_socketcall->sys_socket->sock_create()->inet_create
static int inet_create(struct socket *sock, int protocol)
{
struct sock *sk;
struct list_head *p;
struct inet_protosw *answer;
sock->state = SS_UNCONNECTED;
// sk_alloc实现sock结构分配
sk = sk_alloc(PF_INET, GFP_KERNEL, 1);
//,然后初始化sock结构sk
list_for_each(p, &inetsw[sock->type]) {
answer = list_entry(p, struct inet_protosw, list);
/* Check the non-wild match. */
if (protocol == answer->protocol) {
if (protocol != IPPROTO_IP)
break;
} else {
/* Check for the two wild cases. */
if (IPPROTO_IP == protocol) {
protocol = answer->protocol;
break;
}
if (IPPROTO_IP == answer->protocol)
break;
}
answer = NULL;
}
br_read_unlock_bh(BR_NETPROTO_LOCK);
if (!answer)
goto free_and_badtype;
if (answer->capability > 0 && !capable(answer->capability))
goto free_and_badperm;
if (!protocol)
goto free_and_noproto;
sock->ops = answer->ops;
sk->prot = answer->prot;
}
上面inetsw是inet_protosw数据结构数组,是很重要的数据结构,提供了具体传输层的一些函数集,是在net_init用inetsw_array赋值的。
Inet_init()
{
/* Register the socket-side information for inet_create. */
for(r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
INIT_LIST_HEAD(r);
for(q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
inet_register_protosw(q);
..........
}
static struct inet_protosw inetsw_array[] =
{
{
type: SOCK_STREAM,
protocol: IPPROTO_TCP,
//传输层级结构
prot: &tcp_prot,
//提供统一的
ops: &inet_stream_ops,
capability: -1,
no_check: 0,
flags: INET_PROTOSW_PERMANENT,
},
{
type: SOCK_DGRAM,
protocol: IPPROTO_UDP,
prot: &udp_prot,
ops: &inet_dgram_ops,
capability: -1,
no_check: UDP_CSUM_DEFAULT,
flags: INET_PROTOSW_PERMANENT,
},
{
type: SOCK_RAW,
protocol: IPPROTO_IP, /* wild card */
prot: &raw_prot,
ops: &inet_dgram_ops,
capability: CAP_NET_RAW,
no_check: UDP_CSUM_DEFAULT,
flags: INET_PROTOSW_REUSE,
}
};
至此数据初始化完成了,然后就调用具体传输层协议的初始化函数。
总结一下,整个创建过程创建了三个重要的数据结构,inode,socket,sock。
他们的关系图如下:
整个创建过程主要函数调用层次关系如下:
->alloc_sock() //创建初始的socket
>get_empty_inode//获取一个Inode-
>socket_look_up()//填充具体inode的socket并初始化
->inet_create() //完善socket结构体内容,如添加sock结构。
->alloc_sk // 创建sock结构,并初始化socket->ops=inetsw[](具体// 协议的函数),sock->proto,
->tcp_init