Linux socket系统调用(二)----sock_alloc()

注:本文分析基于3.10.107内核版本

当我们在进行网络编程时,socket系统调用是必不可少的一个步骤。socket系统调用返回的是一个fd,即一个文件描述符。其实它就只是一个int类型的数值,我们为什么能像操作文件一样进行读写呢?这就是VFS的功劳了,同时协议栈为了适配VFS虚拟文件系统实现了sockfs,最终使得我们可以像操作文件一样操作文件描述符。

而实现socket和文件系统绑定的操作,就是在socket系统调用中实现的,里面最重要的一步就是调用sock_alloc()函数。sock_alloc()里体现了linux一切皆文件(Everything is a file)理念,即使用文件系统来管理socket,这也是VFS所要达到的效果。

static struct socket *sock_alloc(void)
{
    struct inode *inode;
    struct socket *sock;

    inode = new_inode_pseudo(sock_mnt->mnt_sb);//创建一个inode,用于将sock和文件系统相关联
    if (!inode)
        return NULL;

    sock = SOCKET_I(inode);//根据inode获取socket_alloc结构体中socket成员
...
    return sock;
}

我们追踪一下sock_alloc()函数的调用路径:

sock_alloc(sock_mnt->mnt_sb) –> new_inode_pseudo() –> alloc_inode() –> sb->s_op->alloc_inode()

那这个s_op->alloc_inode()到底是哪来的呢?

根据new_inode_pseudo()函数的入参sock_mnt->mnt_sb,我们可以找到该变量的定义:

static struct dentry *sockfs_mount(struct file_system_type *fs_type,
             int flags, const char *dev_name, void *data)
{
    return mount_pseudo(fs_type, "socket:", &sockfs_ops,
        &sockfs_dentry_operations, SOCKFS_MAGIC);
}

static struct vfsmount *sock_mnt __read_mostly;

static struct file_system_type sock_fs_type = {
    .name =     "sockfs",
    .mount =    sockfs_mount,
    .kill_sb =  kill_anon_super,
};

我顺带把等会要使用到的结构体和函数一并写上。sock_mnt变量的初始化是在sock_init()函数。

static int __init sock_init(void)
{
...
    err = register_filesystem(&sock_fs_type);
    if (err)
        goto out_fs;
    sock_mnt = kern_mount(&sock_fs_type);
...
}

到这可以知道sock_fs_type->mount指向sockfs_mount(),请记住这点。下面我们跟踪kern_mount()函数的调用路径:

kern_mount(&sock_fs_type) –> kern_mount_data() –> vfs_kern_mount() –> mount_fs() –> type->mount()

这里type->mount()便是调用sockfs_mount()函数。sockfs_mount()函数调用mount_pseudo()。

/*
 * Common helper for pseudo-filesystems (sockfs, pipefs, bdev - stuff that
 * will never be mountable)
 */
struct dentry *mount_pseudo(struct file_system_type *fs_type, char *name,
    const struct super_operations *ops,
    const struct dentry_operations *dops, unsigned long magic)
{
...
    s = sget(fs_type, NULL, set_anon_super, MS_NOUSER, NULL);
...
    s->s_op = ops ? ops : &simple_super_operations;
...
}

从这个函数可以看出s_op就是ops,也就是sockfs_ops,

static const struct super_operations sockfs_ops = {
    .alloc_inode    = sock_alloc_inode,
    .destroy_inode  = sock_destroy_inode,
    .statfs     = simple_statfs,
};

因此刚才sock_alloc()最终调用的就是sock_alloc_inode。

static struct inode *sock_alloc_inode(struct super_block *sb)
{
    struct socket_alloc *ei;
    struct socket_wq *wq;

    ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL);
    if (!ei)
        return NULL;
    wq = kmalloc(sizeof(*wq), GFP_KERNEL);//为这个socket创建一个等待队列
    if (!wq) {
        kmem_cache_free(sock_inode_cachep, ei);
        return NULL;
    }
    init_waitqueue_head(&wq->wait);
    wq->fasync_list = NULL;
    RCU_INIT_POINTER(ei->socket.wq, wq);

    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;//返回inode节点,用于初始化等操作
}

这里我们看到struct socket_alloc,这个结构体,

struct socket_alloc {
    struct socket socket;
    struct inode vfs_inode;
};

从这个结构体我们就能看到文件系统相关的结构体了。到这我们就明白内核是怎么把socket和文件系统相关联的了,这也就是sockfs实现的关键。

回过头我们来理一下sock_alloc()所做的事。它分配一个socket_alloc结构体,将sockfs相关属性填充在socket_alloc结构体的vfs_inode变量中,以限定后续对这个sock文件允许的操作。这也为后面通过文件描述符操作socket奠定基础。同时sock_alloc()最终返回的是socket_alloc结构体的socket变量,因此用户层面并不感知内核如何将socket和文件系统绑定。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值