socket的实践到内核--追踪socket的创建

来源:岁月联盟 编辑:zhuzhu 时间:2008-10-23
socket的实践到内核--追踪socket的创建内容简介:我们说过socket篇幅比较大,内容相对较多所以分的章节细一些,敬请阅读的朋友们注意,这节我们继续前面的socket创建的追踪,上一节我们最后追踪到_sock_create(),并且它的前面不是很重要只是对一些参数的检 我们说过socket篇幅比较大,内容相对较多所以分的章节细一些,敬请阅读的朋友们注意,这节我们继续前面的socket创建的追踪,上一节我们最后追踪到_sock_create(),并且它的前面不是很重要只是对一些参数的检验,我们接着这个函数往下看
/*
     *    Allocate the socket and allow the family to set things up. if
     *    the protocol is 0, the family is instructed to select an appropriate
     *    default.
     */
    sock = sock_alloc();
    if (!sock) {
        if (net_ratelimit())
            printk(KERN_WARNING "socket: no more sockets/n");
        return -ENFILE;    /* Not exactly a match, but its the
                 closest posix thing */
    }
    sock->type = type;
#if defined(CONFIG_KMOD)
    /* Attempt to load a protocol module if the find failed.
     *
     * 12/09/1996 Marcin: But! this makes REALLY only sense, if the user
     * requested real, full-featured networking support upon configuration.
     * Otherwise module support will break!
     */
    if (net_families[family] == NULL)
        request_module("net-pf-%d", family);
#endif
    rcu_read_lock();
    pf = rcu_dereference(net_families[family]);
    err = -EAFNOSUPPORT;
    if (!pf)
        goto out_release;
    /*
     * We will call the ->create function, that possibly is in a loadable
     * module, so we have to bump that loadable module refcnt first.
     */
    if (!try_module_get(pf->owner))
        goto out_release;
    /* Now protected by module ref count */
    rcu_read_unlock();
    err = pf->create(net, sock, protocol);
    if (err  0)
        goto out_module_put;
    /*
     * Now to bump the refcnt of the [loadable] module that owns this
     * socket at sock_release time we decrement its refcnt.
     */
    if (!try_module_get(sock->ops->owner))
        goto out_module_busy;
    /*
     * Now that we're done with the ->create function, the [loadable]
     * module can have its refcnt decremented
     */
    module_put(pf->owner);
    err = security_socket_post_create(sock, family, type, protocol, kern);
    if (err)
        goto out_sock_release;
    *res = sock;
    return 0;
out_module_busy:
    err = -EAFNOSUPPORT;
out_module_put:
    sock->ops = NULL;
    module_put(pf->owner);
out_sock_release:
    sock_release(sock);
    return err;
out_release:
    rcu_read_unlock();
    goto out_sock_release;
}
我们把后面部分的代码全贴在上面了,首先要分配一个socket
static struct socket *sock_alloc(void)
{
    struct inode *inode;
    struct socket *sock;
    inode = new_inode(sock_mnt->mnt_sb);
    if (!inode)
        return NULL;
    sock = SOCKET_I(inode);
    inode->i_mode = S_IFSOCK | S_IRWXUGO;
    inode->i_uid = current->fsuid;
    inode->i_gid = current->fsgid;
    get_cpu_var(sockets_in_use)++;
    put_cpu_var(sockets_in_use);
    return sock;
}
这里我们看到socket数据结构,这个定义位于include/linux/net.h的117行
struct socket {
    socket_state        state;
    unsigned long        flags;
    const struct proto_ops    *ops;
    struct fasync_struct    *fasync_list;
    struct file        *file;
    struct sock        *sk;
    wait_queue_head_t    wait;
    short            type;
};
结构内容暂且放一放,我们用时再说,况且不用在这里列出一堆说明也不利于记忆和学习,昨天有网友说ULK也就是深入理解内核书不错,我个人感觉他里面有大量的文字来描述结构体和函数作用甚至详细的参数作用,大量的篇幅都是用文字来描述,如果是英文版的看起更加吃力,我就是读了第三版的英文版,感觉不如读代码舒服来的痛快,与其说那本书是学习资料不如说更加象一本工具字典,现在我们分析一下sock_allock()这个函数,首先这里涉及到文件系统的inode,我们看到他调用new_inode(sock_mnt->mnt_sb);可以看到sock_mnt是上一节我们看到的socket文件系统的根节点,这里是在socket文件系统中分配一个节点,关于文件系统的内容将在以后的专门来分析,这里我们只需要知道他为我们在内存中分配了一个socket的inode节点即可,然后函数中接着是SOCKET_I(),这是一个内联函数,我们看一下他的内容
static inline struct socket *SOCKET_I(struct inode *inode)
{
    return &container_of(inode, struct socket_alloc, vfs_inode)->socket;
}
#define container_of(ptr, type, member) ({            /
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    /
    (type *)( (char *)__mptr - offsetof(type,member) );})
这里我们看到他使用了一个宏container_of,这个宏我们需要分析一下,首先我们把他转换一下成下面这样
#define container_of(inode, struct socket_alloc, vfs_inode) ({            /
    const typeof( ((struct socket_alloc *)0)->vfs_inode ) *__mptr = (inode);    /
    (struct socket_alloc *)( (char *)__mptr - offsetof(struct socket_alloc,vfs_inode) );})
上面有一个数据结构我贴在下面
struct socket_alloc {
    struct socket socket;
    struct inode vfs_inode;
};
上面还引用了另一个宏,也贴在下面并代入翻译
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)翻译成offsetof(struct socket_alloc,vfs_inode) ((size_t)&((struct socket_alloc)0)->vfs_inode)
上面的整个宏我们分析一下,首先是(struct socket_alloc)0)->vfs_inode,这句有很多朋友不很理解,它就是假设数据结构socket_alloc在0地址处时取得vfs_inode的地址也实际上就是vfs_inode在数据结构的偏离,所以宏名称使用了offsetof(),这样我们就理解了offsetof()宏是取得vfs_inode在struct socket_alloc的相对偏离位置,即地址差,然后我们看container_of宏,这个宏首先是假设0地址处的socket_alloc结构中的vfs_inode指向我们创建的inode,然后用这个inode的地址减掉vfs_inode的偏离差,就得到了数据结构socket_alloc的起始地址,而起始地址我们看到在socket_alloc头部是socket的数据结构,因此我们也就得到了socket的地址,即指针。这个宏是非常关键的一个宏,肯定今后要经常在内核中出现,希望朋友们认真把我的描述研究一遍,做到真正的理解,如果还是不得其解那就请记住他的作用就是找到数据结构中的指定数据结构的头地址。sock_alloc()剩下的代码部分是为inode节点设置他的相关指示标记,使他在内存中标记为socket,以其创建进程的信息,还有增加内存中socket的使用记数。然后我们回到__sock_create()函数中继续往下分析,创建了一个socket以后,我们看到
    if (net_families[family] == NULL)
        request_module("net-pf-%d", family);
这段代码是在内核支持安装模块的条件下执行的,首先看到一个数组net_families[],这个数组是内核中全局的数组,每一个网域都要有一个net_proto_family结构,linux内核初始化过程中都会将支持的网域的这个结构登记到net_families[]数组中,如果没有安装就会通过request_module()试着安装,这里我们只看unix的,这个数据结构在net/unix/af_unix.c的2193行处
static struct net_proto_family unix_family_ops = {
    .family = PF_UNIX,
    .create = unix_create,
    .owner    = THIS_MODULE,
};
这个结构变量一会就会用到,我们先回到__sock_create()函数中继续看,下面是关于锁的内容我们以后论述,这里取得unix的网域协议结构也就是上面我们说的unix_family_ops,然后我们可以看到通过 pf->create(net, sock, protocol);来调用unix_create.时间关系.