天天看點

TCP/IP源碼學習(47)——socket與VFS的關聯(1)

作者:[email protected]

部落格:blog.focus-linux.net   linuxfocus.blog.chinaunix.net 

本文的copyleft歸[email protected]所有,使用GPL釋出,可以自由拷貝,轉載。但轉載請保持文檔的完整性,注明原作者及原連結,嚴禁用于任何商業用途。

======================================================================================================

今天學習一下socket與VFS之間的關系。

對于socket程式設計,我們都知道socket也是一個檔案描述符,那麼socket與VFS之間究竟是如何關聯的呢?

首先,建立socket的函數,socket.c的函數SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)在成功建立了socket以後,通過函數sock_map_fd,将建立的struct socket結構映射為一個檔案描述符。

    /* sock為成功建立的struct socket *sock類型, 而retval為映射後的檔案描述符 */

    retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));

    if (retval 0)

        goto out_release;

進入sock_map_fd

int sock_map_fd(struct socket *sock, int flags)

{

    struct file *newfile;

    /* 将sock映射為一個檔案描述符fd */

    int fd = sock_alloc_file(sock, &newfile, flags);

     /* 映射成功後,将fd加入到目前程序的檔案描述符表中 */

    if (likely(fd >= 0))

        fd_install(fd, newfile);

    return fd;

}

進入sock_alloc_file,

static int sock_alloc_file(struct socket *sock, struct file **f, int flags)

    struct qstr name = { .name = "" };

    struct path path;

    struct file *file;

    int fd;

     /* 

     該函數名字稍微有點晦澀。看上去像是獲得fd_flags,但是實際上是獲得fd。

     我個人覺得名字叫做get_unused_fd_with_flags更好一些,這樣還是突出是獲得fd

     */

    fd = get_unused_fd_flags(flags);

    if (unlikely(fd 0))

        return fd;

     申請新的dentry,用socket對應的inode——該inode實際上是與socket同時申請下來的,參見struct socket_      alloc結構,

     初始化該dentry。

     sock_mnt為一個全局變量,為sockfs的檔案系統挂載點。

    path.dentry = d_alloc(sock_mnt->mnt_sb->s_root, &name);

    if (unlikely(!path.dentry)) {

        put_unused_fd(fd);

        return -ENOMEM;

    }

    path.mnt = mntget(sock_mnt);

     将sockfs的dentry操作函數,和檔案操作函數分别綁定到dentry和inode上。

     這樣即完成VFS的統一調用。

    path.dentry->d_op = &sockfs_dentry_operations;

    d_instantiate(path.dentry, SOCK_INODE(sock));

    SOCK_INODE(sock)->i_fop = &socket_file_ops;

     /* 申請file,并将前面的dentry path與file關聯起來 */

    file = alloc_file(&path, FMODE_READ | FMODE_WRITE,

         &socket_file_ops);

    if (unlikely(!file)) {

        /* drop dentry, keep inode */

        atomic_inc(&path.dentry->d_inode->i_count);

        path_put(&path);

        return -ENFILE;

    sock->file = file;

    file->f_flags = O_RDWR | (flags & O_NONBLOCK);

    file->f_pos = 0;

    file->private_data = sock;

    *f = file;

進入get_unused_fd_flags->alloc_fd

*/

int alloc_fd(unsigned start, unsigned flags)

    struct files_struct *files = current->files;

    unsigned int fd;

    int error;

    struct fdtable *fdt;

    spin_lock(&files->file_lock);

repeat:

    /* 得到該程序的檔案描述符表 */

    fdt = files_fdtable(files);

    /* 從start開始查找 */

    fd = start;

    /* 

    files->next_fd為上一次查找确定的下一個可用空閑的檔案描述符。

    那麼這次可以直接使用next_fd

    */

    if (fd files->next_fd)

        fd = files->next_fd;

     /* 當fd小于目前程序支援的最大的描述符号,那麼可以通過fds_bits位圖,從fd位開始查找,

     找到下一個0位,即下一個空閑描述符。

    if (fd fdt->max_fds)

        fd = find_next_zero_bit(fdt->open_fds->fds_bits,

                     fdt->max_fds, fd);

    /* 如需要則擴充檔案描述符表 */

    error = expand_files(files, fd);

    if (error 0)

        goto out;

    /*

     * If we needed to expand the fs array we

     * might have blocked - try again.

     */

    if (error)

        goto repeat;

     設定next_fd,用于下次加速查找空閑的fd。

     當start大于next_fd時,不會設定next_fd以避免檔案描述符的不連續

    if (start = files->next_fd)

        files->next_fd = fd + 1;

     /* 将fd添加到已打開的檔案描述符表中 */

    FD_SET(fd, fdt->open_fds);

    if (flags & O_CLOEXEC)

        FD_SET(fd, fdt->close_on_exec);

    else

        FD_CLR(fd, fdt->close_on_exec);

    error = fd;

#if 1

    /* Sanity check */

    if (rcu_dereference_raw(fdt->fd[fd]) != NULL) {

        printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd);

        rcu_assign_pointer(fdt->fd[fd], NULL);

#endif

out:

    spin_unlock(&files->file_lock);

    return error;

今天是socket如何挂載到VFS的流程,還剩下一小部分這個流程的代碼。下一次會将剩下的代碼學習完畢,以及如何從VFS到socket流程。

繼續閱讀