声明:本文为原创
#####请转贴时保留以下内容######
作者:GTT
请提出宝贵意见Mail:mtloveft@hotmail.com
Linux Version:2.6.33
提示:本文是关于linux 如何实现socket 通信的介绍!
下面就介绍SYS_SOCKET这个系统调用,也就是创建socket的过程。
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol) { int retval; struct socket *sock; int flags;
/* Check the SOCK_* constants for consistency. */ BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC); BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK); BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK); BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);
flags = type & ~SOCK_TYPE_MASK; if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; type &= SOCK_TYPE_MASK;
if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;
retval = sock_create(family, type, protocol, &sock); if (retval < 0) goto out;
retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK)); if (retval < 0) goto out_release;
out: /* It may be already another descriptor 8) Not kernel problem. */ return retval;
out_release: sock_release(sock); return retval; }
|
sock_create()的作用就是创建一个socket
sock_map_fd()函数为新建的socket在当前进程current中申请文件和文件描述符,进行关联。
先看看为user space 提供的方法
int socket(int domain, int type, int protocol)
|
domain 是protocol family 即协议族
type 是通信方式, SOCK_STREAM, SOCK_DGRAM, SOCK_RAW
protocl 是协议
user space 调用socket()最终会返回一个文件描述符
kernel 又是怎么分配这个文件文件描述符的呢?
想了解kernel如何实现socket的,文件系统知识是不可少的。
因为socket的实现就是一种特殊的文件系统的实现。
文件系统的类型是sockfs
定义如下
static struct file_system_type sock_fs_type = { .name = "sockfs", .get_sb = sockfs_get_sb, .kill_sb = kill_anon_super, };
|
sockfs 文件系统的注册过程如下
static int __init sock_init(void) { /* Initialize sock SLAB cache. */ sk_init();
/* Initialize skbuff SLAB cache */ skb_init();
/* Initialize the protocols module. */ init_inodecache(); register_filesystem(&sock_fs_type); sock_mnt = kern_mount(&sock_fs_type);
/* The real protocol initialization is performed in later initcalls. */ #ifdef CONFIG_NETFILTER netfilter_init(); #endif
return 0; }
|
你也许有些疑惑,这个方法有在什么时候被调用的呢?
它的定义下面有另外一行代码
core_initcall(sock_init); /* early initcall */
也就是在kernel 初始化的时候被调用,关于系统初始化
又是一大堆东西,暂时可以不考虑,只要知道系统初始化
时被调用就可以了。不过我以前画过一点儿关于初始化的
处理流程,可以稍微参考一下
下面的文章,地址
register_filesystem就把sockfs注册到file_systems这个链表上了。
至于怎么注册的?请看下面的文章
sockfs是虚拟文件系统,没有实际的物理介质,只存在于内存中。
然后调用kern_mount分配一个vfsmount结构即sock_mnt。这样同时
super block 等也生成了。
kern_mount的分析请参看下面文章
对于linux kernel,创建一个socket就是在sockfs文件系统里分配
相应的inode ,其实这个inode 和struct socket在内存上是连续的,也就关联了起来。
至于文件描述符,他是由curren即当前进程分配的一个
struct file ,它是一个跟inode 间接关联的。所以最终文件描述符和
socket关联起来了。
说的简单,代码倒是一大堆。慢慢分析。
先来看看sockfs的mount过程
sockfs中取得super block 的方法是sockfs_get_sb
所以mount 过程中会调用到他
static int sockfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt) { return get_sb_pseudo(fs_type, "socket:", &sockfs_ops, SOCKFS_MAGIC, mnt); }
|
因为直接调用get_sb_pseudo,所以直接看它的代码
/* * Common helper for pseudo-filesystems (sockfs, pipefs, bdev - stuff that * will never be mountable) */ int get_sb_pseudo(struct file_system_type *fs_type, char *name, const struct super_operations *ops, unsigned long magic, struct vfsmount *mnt) { struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL); struct dentry *dentry; struct inode *root; struct qstr d_name = {.name = name, .len = strlen(name)};
if (IS_ERR(s)) return PTR_ERR(s);
s->s_flags = MS_NOUSER; s->s_maxbytes = MAX_LFS_FILESIZE; s->s_blocksize = PAGE_SIZE; s->s_blocksize_bits = PAGE_SHIFT; s->s_magic = magic; s->s_op = ops ? ops : &simple_super_operations; s->s_time_gran = 1; root = new_inode(s); if (!root) goto Enomem; /* * since this is the first inode, make it number 1. New inodes created * after this must take care not to collide with it (by passing * max_reserved of 1 to iunique). */ root->i_ino = 1; root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR; root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME; dentry = d_alloc(NULL, &d_name); if (!dentry) { iput(root); goto Enomem; } dentry->d_sb = s; dentry->d_parent = dentry; d_instantiate(dentry, root); s->s_root = dentry; s->s_flags |= MS_ACTIVE; simple_set_mnt(mnt, s); return 0;
Enomem: deactivate_locked_super(s); return -ENOMEM; }
|
get_sb_pseudo的参数如下
第一个参数,fs_type = sock_fs_type即sockfs
第二个参数,"socket:"用于sockfs 的root的dentry的名称
第三个参数, sockfs_ops是sockfs 的super block 的操作VFT
,定义为
static const struct super_operations sockfs_ops = { .alloc_inode = sock_alloc_inode, .destroy_inode = sock_destroy_inode, .statfs = simple_statfs, };
|
第四个参数,sockfs 的magic number
第五个参数,即vfsmount,
所以sockfs 里分配inode 的方法是sock_alloc_inode
以后每次分配inode,都调用次方法
static struct inode *sock_alloc_inode(struct super_block *sb) { struct socket_alloc *ei;
ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL); if (!ei) return NULL; init_waitqueue_head(&ei->socket.wait);
ei->socket.fasync_list = NULL; ei->socket.state = SS_UNCONNECTED; ei->socket.flags = 0; ei->socket.ops = NULL; ei->socket.sk = NULL; ei->socket.file = NULL;
return &ei->vfs_inode; }
|
socket_alloc的结构很有特点,就是把socket 和inode 关联了起来。
每次从cache里分配都是有两个结构。
struct socket_alloc { struct socket socket; struct inode vfs_inode; };
|
看结构就知道了,inode和socket被关联在一起了。知道其中一个就可以知道另外一个了。
阅读(1469) | 评论(0) | 转发(0) |