• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆-159  評論-223  文章-30  trackbacks-0
            引言
               在Unix的世界里,萬物皆文件,通過虛擬文件系統VFS,程序可以用標準的Unix系統調用對不同的文件系統,甚至不同介質上的文件系統進行讀寫操作。對于網絡套接字socket也是如此,除了專屬的Berkeley Sockets API,還支持一些標準的文件IO系統調用如read(v)、write(v)和close等。那么為什么socket也支持文件IO系統調用呢?在Linux上,這是通過套接口偽文件系統sockfs來實現的,因為sockfs實現了VFS中的4種主要對象:超級塊super block、索引節點inode、目錄項對象dentry和文件對象file,當執行文件IO系統調用時,VFS就將請求轉發給sockfs,而sockfs就調用特定的協議實現,層次結構如下圖:
               本文以linux 2.6.34實現為基礎,本篇闡述初始化和Socket創建兩部分的實現,下篇闡述Socket操作和銷毀兩部分的實現。

            初始化
               在內核引導時初始化網絡子系統,進而調用sock_init,該函數主要步驟如下:創建inode緩存,注冊和裝載sockfs,定義在net/socket.c中。
            1static int __init sock_init(void)
            2{
            3    
            4    init_inodecache();
            5    register_filesystem(&sock_fs_type);
            6    sock_mnt = kern_mount(&sock_fs_type);
            7    
            8}
               
               創建inode緩存
               init_inodecache為socket_alloc對象創建SLAB緩存,名稱為sock_inode_cachep,socket_alloc定義在include/net/sock.h中。
            1struct socket_alloc {
            2    struct socket socket;
            3    struct inode vfs_inode;
            4}
            ;
               socket_alloc由socket和inode結構2部分組成,這樣就方便了在套接字與inode對象間雙向定位。

               注冊sockfs
               調用VFS的函數register_filesystem實現注冊,sock_fs_type定義在net/socket.c中。
            1static struct file_system_type sock_fs_type = {
            2    .name =        "sockfs",
            3    .get_sb =    sockfs_get_sb,
            4    .kill_sb =    kill_anon_super,
            5}
            ;
               sock_fs_type包含了文件系統sockfs的名稱、創建和銷毀super block的函數,其中sockfs_get_sb實現在net/socket.c中。
            1static int sockfs_get_sb(struct file_system_type *fs_type,int flags, const char *dev_name, void *data,struct vfsmount *mnt)
            2{
            3    return get_sb_pseudo(fs_type, "socket:"&sockfs_ops, SOCKFS_MAGIC, mnt);
            4}
               它在kern_mount內被執行,通過調用get_sb_pseudo創建了一個super block(包含一個對應dentry及一個關聯inode):操作對象為sockfs_ops,根目錄名稱為socket:,對應的根索引節點編號為1。
               sockfs_ops定義在net/socket.c中。
            1static const struct super_operations sockfs_ops = {
            2    .alloc_inode =    sock_alloc_inode,
            3    .destroy_inode = sock_destroy_inode,
            4    .statfs =    simple_statfs,
            5}
            ;
               sock_alloc_inode用于分配inode對象,將在socket創建過程中被調用;sock_destroy_inode用于釋放inode對象,將在socket銷毀過程中被調用;simple_statfs用于獲取sockfs文件系統的狀態信息。
               
               裝載sockfs
               由kern_mount函數實現裝載一個偽文件系統(當然,它沒有裝載點),返回一個static vfsmount對象sock_mnt。

               經過以上步驟后,所創建的VFS對象關系如下圖:
                對于根目錄項,不用進行路徑轉換,因此dentry的d_op為空(未畫出);對于偽文件系統,操作索引對象沒有意義,所以inode的i_op為空(未畫出)。

            Socket創建


               系統調用socket、accept和socketpair是用戶空間創建socket的幾種方法,其核心調用鏈如下圖:
               從上圖可知共同的核心就3個過程:先構造inode,再構造對應的file,最后安裝file到當前進程中(即關聯映射到一個未用的文件描述符),下面就這3個過程進行詳細說明。

               構造inode
               由sock_alloc函數實現,定義在net/socket.c中。
             1static struct socket *sock_alloc(void)
             2{
             3    struct inode *inode;
             4    struct socket *sock;
             5
             6    inode = new_inode(sock_mnt->mnt_sb);
             7        
             8    sock = SOCKET_I(inode);
             9            
            10    inode->i_mode = S_IFSOCK | S_IRWXUGO;
            11    inode->i_uid = current_fsuid();
            12    inode->i_gid = current_fsgid();
            13        
            14    return sock;
            15}
               先調用new_inode創建inode對象,再設置它的類型為S_IFSOCK,由此可知inode對應的文件類型為套接字。new_inode是文件系統的一個接口函數,用于創建一個inode對象,定義在fs/inode.c中,它調用了sockfs超級塊的操作對象即sockfs_ops的sock_alloc_inode方法,由于sock_alloc_inode實際創建的是socket_alloc復合對象,因此要使用SOCKET_I宏從inode中取出關聯的socket對象用于返回。

               構造file
               有了inode對象后,接下來就要構造對應的file對象了,由sock_alloc_file實現,定義在net/socket.c中。
             1static int sock_alloc_file(struct socket *sock, struct file **f, int flags)
             2{
             3    struct qstr name = { .name = "" };
             4    struct path path;
             5    struct file *file;
             6    int fd;
             7
             8    fd = get_unused_fd_flags(flags);
             9        
            10    path.dentry = d_alloc(sock_mnt->mnt_sb->s_root, &name);
            11        
            12    path.mnt = mntget(sock_mnt);
            13
            14    path.dentry->d_op = &sockfs_dentry_operations;
            15    d_instantiate(path.dentry, SOCK_INODE(sock));
            16    SOCK_INODE(sock)->i_fop = &socket_file_ops;
            17
            18    file = alloc_file(&path, FMODE_READ | FMODE_WRITE, &socket_file_ops);
            19    
            20    sock->file = file;
            21    file->f_flags = O_RDWR | (flags & O_NONBLOCK);
            22    file->f_pos = 0;
            23    file->private_data = sock;
            24
            25    *= file;
            26    return fd;
            27}
               sock為上一過程返回的套接字對象,該函數主要做了以下幾件事:
               1)得到空閑的文件描述符fd,實際上就是fd數組的索引,準備作為返回值。
               2)先初始化路徑path:其目錄項的父目錄項為超級塊對應的根目錄,名稱為空,操作對象為sockfs_dentry_operations,對應的索引節點對象為sock套接字關聯的索引節點對象,即SOCK_INODE(sock);裝載點為sock_mnt。  
               sockfs_dentry_operations定義在net/socket.c中。
            1static const struct dentry_operations sockfs_dentry_operations = {
            2    .d_dname  = sockfs_dname,
            3}
            ;
               sockfs_dname會被d_path調用,用于計算socket對象的目錄項名稱。
               3)設置索引節點的文件操作對象為socket_file_ops,定義在net/socket.c中。
            1static const struct file_operations socket_file_ops = {
            2    
            3    .aio_read =    sock_aio_read,
            4    .aio_write =    sock_aio_write,
            5    
            6    .open =        sock_no_open,    /* special open code to disallow open via /proc */
            7    .release =    sock_close,
            8    
            9}
            ;
               4)調用alloc_file,以path和socket_file_ops為輸入參數,這樣返回得到的file便與sock的inode關聯上了,并且操作對象為socket_file_ops,最后設置到輸出參數f中。
               5)建立file與socket的一一映射關系。
               
               安裝file
               由fd_install實現,定義在fs/open.c中。
             1void fd_install(unsigned int fd, struct file *file)
             2{
             3    struct files_struct *files = current->files;
             4    struct fdtable *fdt;
             5    spin_lock(&files->file_lock);
             6    fdt = files_fdtable(files);
             7    BUG_ON(fdt->fd[fd] != NULL);
             8    rcu_assign_pointer(fdt->fd[fd], file);
             9    spin_unlock(&files->file_lock);
            10}
               fd和file分別為上一過程返回的空閑文件描述符和文件對象,使RCU技術來設置file到當前進程的fd數組中。
             
               經過以上過程后,所創建的VFS對象關系圖如下
               fd為file*數組的索引而不是成員字段;vfsmount與初始化之VFS對象關系圖中的vfsmount是同一個對象,即sock_mnt;對于偽文件系統,操作索引對象沒有意義,所以inode的i_op為空(未畫出)。
            posted on 2015-05-03 16:31 春秋十二月 閱讀(8637) 評論(0)  編輯 收藏 引用 所屬分類: Network
            久久午夜无码鲁丝片午夜精品| 日日躁夜夜躁狠狠久久AV| 久久精品无码专区免费东京热 | 久久精品亚洲福利| 久久精品国内一区二区三区| 99久久精品免费看国产一区二区三区 | 久久夜色撩人精品国产| 久久久久久A亚洲欧洲AV冫| 久久久国产精品| 一级女性全黄久久生活片免费 | 亚洲精品无码久久久久去q| 久久天天躁狠狠躁夜夜avapp| 国内精品久久国产| 色老头网站久久网| 青草国产精品久久久久久| 精品人妻久久久久久888| 久久国产精品-久久精品| 99久久婷婷国产一区二区| 精品无码久久久久久久动漫| 久久综合伊人77777麻豆| 伊人久久大香线蕉综合热线| 国产美女亚洲精品久久久综合| 日韩精品无码久久久久久| 久久国产乱子精品免费女| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 2020久久精品国产免费| 久久久综合九色合综国产| 久久男人中文字幕资源站| 伊人久久大香线蕉亚洲| 99久久er这里只有精品18| 久久九九久精品国产免费直播| 国内精品伊人久久久久妇| 久久午夜电影网| 久久免费看黄a级毛片| 99久久免费只有精品国产| 伊人久久大香线蕉亚洲| 久久精品国产亚洲av瑜伽| 久久综合给合久久国产免费 | 色综合久久88色综合天天| 久久久精品人妻一区二区三区蜜桃| 99久久婷婷国产综合亚洲|