Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1829154
  • 博文数量: 195
  • 博客积分: 4227
  • 博客等级: 上校
  • 技术积分: 2835
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-04 10:39
文章分类

全部博文(195)

文章存档

2013年(1)

2012年(26)

2011年(168)

分类: LINUX

2011-03-11 17:19:49

linux socket详解(1)

谨以此文纪念过往的岁月

一.前言
网络文件系统是一个很大的模块,其复杂程度不下于cdev,对于其学习需要很大的毅力,并且其并没有cdev那样的形象,其更加的抽象。

二.socket的创建
对于很多应用程序员而言,socket并不陌生,但是其在内核中具体的实现并不是很了解,而对于驱动工程师而言,这却是必须的,那就从socket的创建开始。以sys_socket开始,对于常用source insight而言,当我们去搜sys_socket时就不会找到该函数的定义。其具体的实现其实在SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol);我们剔除其对type以及flags的检测,其就包含两个函数sock_create
和sock_map_fd。闲话不说,咱们还是来看源码吧。你如果比照源码会发现下面的函数怎么缩水不少,不过核心还就是这东西。
static int __sock_create(struct net *net, int family, int type,
                         int protocol,struct socket **res, int kern)
{
    int err;
    struct socket *sock;
    const struct net_proto_family *pf;
    
    sock = sock_alloc();        --分配socket
    sock->type = type;
    pf = net_families[family];
    pf->create(net, sock, protocol);
    *res = sock;
    return 0;
}
那来看socket结构体。也许单独去理解该结构体比较麻烦,不过如果添加在源码中会好理解很多。
struct socket {
    socket_state        state;                     --socket 状态如:SS_CONNECTED
    short            type;                            --socket 类型如: SOCK_STREAM
    unsigned long        flags;              --socket 旗标如: SOCK_ASYNC_NOSPACE
    const struct proto_ops    *ops;       --协议特定的套接字操作。
    struct fasync_struct    *fasync_list; --异步唤醒list
    struct file        *file;                       --文件指针
    struct sock        *sk;                     --网络层对应的socket示例
    wait_queue_head_t    wait;          --等待队列
};
分配一个socket,其实最最主要的是创建一个inode,在文件系统中,file和inode比较难理解。
引用http://hi.baidu.com/lxsbupt/blog/item/14409e10b38f41f7c2ce7910.html中对节点的理解。
1)进程每打开一个文件,就会有一个file结构与之对应。同一个进程可以多次打开同一个文件而得到多个不同的file结构,file结构描述被打开文件的属性,如文件的当前偏移量等信息。
2)两个不同的file结构可以对应同一个dentry结构。进程多次打开同一个文件时,对应的只有一个dentry结构。Dentry结构存储目录项和对应文件(inode)的信息。
3)在存储介质中,每个文件对应唯一的inode结点,但是每个文件又可以有多个文件名。即可以通过不同   的文件名访问同一个文件。这里多个文件名对应一个文件的关系在数据结构中表示就是dentry和inode的关系。
4)Inode中不存储文件的名字,它只存储节点号;而dentry则保存有名字和与其对应的节点号,所以就可以通过不同的dentry访问同一个inode。
5)不同的dentry则是同个文件链接(ln命令)来实现的。
再引用http://blog.csdn.net/yalizhi123/archive/2010/08/16/5815661.aspx中对节点的描述。
         inode 译成中文就是索引节点。每个存储设备或存储设备的分区(存储设备是硬盘、软盘、U盘 ... ... )被格式化为文件系统后,应该有两部份,一部份是inode,另一部份是Block,Block是用来存储数据用的。而inode呢,就是用来存储这些数据的信息,这些信息包括文件大小、属主、归属的用户组、读写权限等。inode为每个文件进行信息索引,所以就有了inode的数值。操作系统根据指令,能通过inode值最快的找到相对应的文件。
       做个比喻,比如一本书,存储设备或分区就相当于这本书,Block相当于书中的每一页,inode 就相当于这本书前面的目录,一本书有很多的内容,如果想查找某部份的内容,我们可以先查目录,通过目录能最快的找到我们想要看的内容。
inode其实是对存储介质的每一个文件的描述。
static struct socket *sock_alloc(void)
{
    struct inode *inode;
    struct socket *sock;

    inode = new_inode(sock_mnt->mnt_sb); --创建一个新的节点,其参数为supper blk
    if (!inode)
        return NULL;
    sock = SOCKET_I(inode);              --根据inode查找socket,就是根据contain_of找到其所属
                                                              的socket_alloc,然后再指向sock
    inode->i_mode = S_IFSOCK | S_IRWXUGO; --设置节点的属性
    inode->i_uid = current->fsuid;        --其使用者id为当前进程的uid
    inode->i_gid = current->fsgid;
    get_cpu_var(sockets_in_use)++;       --增加sockets_in_use计数。
    put_cpu_var(sockets_in_use);
    return sock;
}
在下面的函数中会涉及两个重要的结构体inode和address_space,对于inode的说明网上很多,而address_space并不多。
struct address_space {
        struct inode        *host;                       --所属者
        struct radix_tree_root    page_tree;    --所有页的基树
        spinlock_t        tree_lock;                    --上面树的保护锁
        unsigned int        i_mmap_writable;  --计数VM_SHARED的映射数
        struct prio_tree_root    i_mmap;         --私有和共享的映射的树
        struct list_head    i_mmap_nonlinear;--VM_NONLINEAR映射链表
        spinlock_t        i_mmap_lock;              --树的保护锁
        unsigned int        truncate_count;
        unsigned long        nrpages;
        pgoff_t            writeback_index;
        const struct address_space_operations *a_ops;
        unsigned long        flags;        
        struct backing_dev_info *backing_dev_info;
        spinlock_t        private_lock;    
        struct list_head    private_list;
        struct address_space    *assoc_mapping;
}
下面的函数主要是创建一个inode,并对其中的部分成员初始化。
static struct inode *alloc_inode(struct super_block *sb)
{
    static const struct address_space_operations empty_aops;
    static struct inode_operations empty_iops;
    static const struct file_operations empty_fops;
    struct inode *inode;

    if (sb->s_op->alloc_inode)
        inode = sb->s_op->alloc_inode(sb);   --对于socket而言,其会调用sock_alloc_inode
    else
        inode = (struct inode *) kmem_cache_alloc(inode_cachep, GFP_KERNEL);

    if (inode) {
        struct address_space * const mapping = &inode->i_data;

        inode->i_sb = sb;     --节点supper blk
        inode->i_blkbits = sb->s_blocksize_bits;   --以位为单位的块大小
        inode->i_flags = 0;
        atomic_set(&inode->i_count, 1);  --引用计数
        inode->i_op = &empty_iops;       --索引节点操作表
        inode->i_fop = &empty_fops;       --默认的索引节点操作
        inode->i_nlink = 1;                         --硬链接数
        atomic_set(&inode->i_writecount, 0); --写计数
        inode->i_size = 0;                             --以字节为单位的文件大小
        inode->i_blocks = 0;                        --文件的块数
        inode->i_bytes = 0;                         --使用的字节数
        inode->i_generation = 0;               --索引节点版本号
        inode->i_pipe = NULL;                    --管道信息
        inode->i_bdev = NULL;                   --块设备驱动
        inode->i_cdev = NULL;                   --cdev驱动
        inode->i_rdev = 0;                          --即dev_t
        inode->dirtied_when = 0;              --首次修改时间

        spin_lock_init(&inode->i_lock);
        lockdep_set_class(&inode->i_lock, &sb->s_type->i_lock_key);

        mutex_init(&inode->i_mutex);
        lockdep_set_class(&inode->i_mutex, &sb->s_type->i_mutex_key);

        init_rwsem(&inode->i_alloc_sem);
        lockdep_set_class(&inode->i_alloc_sem, &sb->s_type->i_alloc_sem_key);

        mapping->a_ops = &empty_aops;  --映射的操作表
         mapping->host = inode;         --映射的所有者
        mapping->flags = 0;            --错误bits和gfp的标示
        mapping_set_gfp_mask(mapping, GFP_HIGHUSER_PAGECACHE);
        mapping->assoc_mapping = NULL;
        mapping->backing_dev_info = &default_backing_dev_info;
        mapping->writeback_index = 0;
        inode->i_private = NULL;
        inode->i_mapping = mapping;
    }
    return inode;
}

struct socket_alloc {
    struct socket socket;
    struct inode vfs_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; --socket状态为未连接
    ei->socket.flags = 0;          
    ei->socket.ops = NULL;
    ei->socket.sk = NULL;
    ei->socket.file = NULL;

    return &ei->vfs_inode;
}
再次回到alloc_socket中,我们创建了一个inode,就是完成了inode = new_inode(sock_mnt->mnt_sb)。

到此我们就完成了sock = sock_alloc();那下面就是根据不同的协议类型填充不同的socket。其实现代码如下:
pf = rcu_dereference(net_families[family]);
pf->create(net, sock, protocol);
我们传入的family为AF_INET,那这里面的create函数会指向inet_create.关于其具体的实现下个星期继续。

阅读(3377) | 评论(0) | 转发(1) |
0

上一篇:linux 网络设备驱动

下一篇:linux usb初始化

给主人留下些什么吧!~~