Chinaunix首页 | 论坛 | 博客
  • 博客访问: 627957
  • 博文数量: 155
  • 博客积分: 5688
  • 博客等级: 大校
  • 技术积分: 2134
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-15 15:12
文章分类

全部博文(155)

文章存档

2011年(58)

2010年(97)

分类: 系统运维

2010-08-26 21:57:52

声明:本文为原创
#####请转贴时保留以下内容######
作者GTT
本文档归属http://oldtown.cublog.cn/.转载请注明出处!
请提出宝贵意见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被关联在一起了。知道其中一个就可以知道另外一个了。

 




 





阅读(1412) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~