TCP/IP源码学习(47)——socket与VFS的关联
2014-07-17 09:55
525 查看
TCP/IP源码学习(47)——socket与VFS的关联(1)
摘自:http://blog.chinaunix.net/uid-23629988-id-3080166.html
作者:gfree.wind@gmail.com
博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net
本文的copyleft归gfree.wind@gmail.com所有,使用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);
put_unused_fd(fd);
return -ENFILE;
}
sock->file = file;
file->f_flags = O_RDWR | (flags & O_NONBLOCK);
file->f_pos = 0;
file->private_data = sock;
*f = file;
return fd;
}
进入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流程
TCP/IP源码学习(48)——socket与VFS的关联(2)
摘自:http://blog.chinaunix.net/uid-23629988-id-3080166.html
作者:gfree.wind@gmail.com
博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
继续昨天的学习。
昨天学习alloc_fd时,还有一个函数expand_files没有进入跟踪。
int expand_files(struct files_struct *files, int nr)
{
struct fdtable *fdt;
fdt = files_fdtable(files);
/*
* N.B. For clone
tasks sharing a files structure, this test
* will limit the total number of files that can be opened.
*/
/* 检查是否超过当前进程限定的最大可打开文件数 */
if (nr >= rlimit(RLIMIT_NOFILE))
return -EMFILE;
/* Do we need to expand? */
/*
如果nr小于max_fds,即目前的文件表的个数已经超过了nr, 所以无需扩展。
这也说明,文件表只会增大,不会减小。
*/
if (nr < fdt->max_fds)
return 0;
/* Can we expand? */
/*
检查是否超过了系统限定的最大可打开文件数
注意前面的检查为当前进程的打开文件数,此处的检查为系统可打开的文件数——所有进程
*/
if (nr >= sysctl_nr_open)
return -EMFILE;
/* All good, so we try */
/* 真正去做expand*/
return expand_fdtable(files, nr);
}
进入expand_fdtable
static int expand_fdtable(struct files_struct *files, int nr)
__releases(files->file_lock)
__acquires(files->file_lock)
{
struct fdtable *new_fdt, *cur_fdt;
spin_unlock(&files->file_lock);
/* 申请新的文件表 */
new_fdt = alloc_fdtable(nr);
spin_lock(&files->file_lock);
if (!new_fdt)
return -ENOMEM;
/*
* extremely unlikely race - sysctl_nr_open decreased between the check in
* caller and alloc_fdtable(). Cheaper to catch
it here...
*/
/* 如注释所示,由于竞争,有可能在申请nr个数的新文件表时,修改了sysctl_nr_open,导致新的文件表个数 小于我们所需要的。所以,这里需要对new_fdt->max_fds和nr进行判断。如果小于nr,那么expand失败 */
if (unlikely(new_fdt->max_fds <= nr)) {
__free_fdtable(new_fdt);
return -EMFILE;
}
/*
* Check again since another task may have expanded the fd table while
* we dropped the lock
*/
cur_fdt = files_fdtable(files);
/* 如注释所示,有可能另外一个进程已经扩展了文件表,所以这里再次判断 */
if (nr >= cur_fdt->max_fds) {
/* 复制文件表 */
/* Continue as planned */
copy_fdtable(new_fdt, cur_fdt);
rcu_assign_pointer(files->fdt, new_fdt);
/*
对于文件表结构struct files_struct,其成员变量fd_array为一个大小为NR_OPEN_DEFAULT的数 组。这是一种常用的技巧。一般来说,进程的打开的文件不会太多,所以可以直接使用一个比较小的数组,这 样可以提高效率避免二次分配,同时由于数组较小,并不会太浪费空间。当文件个数超过数组大小的时候,再 重新申请内存。*/
if (cur_fdt->max_fds > NR_OPEN_DEFAULT)
free_fdtable(cur_fdt);
} else {
/* Somebody else expanded, so
undo our attempt */
__free_fdtable(new_fdt);
}
return 1;
}
expand_files结束之后,alloc_fd也学习完了,即宏get_unused_fd_flags也就结束了,那么我们就再次回到了函数sock_alloc_file,继续学习其调用的其它函数。
TCP/IP源码学习(47)——socket与VFS的关联(1)
摘自:http://blog.chinaunix.net/uid-23629988-id-3080166.html
摘自:http://blog.chinaunix.net/uid-23629988-id-3080166.html
作者:gfree.wind@gmail.com
博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net
本文的copyleft归gfree.wind@gmail.com所有,使用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);
put_unused_fd(fd);
return -ENFILE;
}
sock->file = file;
file->f_flags = O_RDWR | (flags & O_NONBLOCK);
file->f_pos = 0;
file->private_data = sock;
*f = file;
return fd;
}
进入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流程
TCP/IP源码学习(48)——socket与VFS的关联(2)
摘自:http://blog.chinaunix.net/uid-23629988-id-3080166.html
作者:gfree.wind@gmail.com
博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
继续昨天的学习。
昨天学习alloc_fd时,还有一个函数expand_files没有进入跟踪。
int expand_files(struct files_struct *files, int nr)
{
struct fdtable *fdt;
fdt = files_fdtable(files);
/*
* N.B. For clone
tasks sharing a files structure, this test
* will limit the total number of files that can be opened.
*/
/* 检查是否超过当前进程限定的最大可打开文件数 */
if (nr >= rlimit(RLIMIT_NOFILE))
return -EMFILE;
/* Do we need to expand? */
/*
如果nr小于max_fds,即目前的文件表的个数已经超过了nr, 所以无需扩展。
这也说明,文件表只会增大,不会减小。
*/
if (nr < fdt->max_fds)
return 0;
/* Can we expand? */
/*
检查是否超过了系统限定的最大可打开文件数
注意前面的检查为当前进程的打开文件数,此处的检查为系统可打开的文件数——所有进程
*/
if (nr >= sysctl_nr_open)
return -EMFILE;
/* All good, so we try */
/* 真正去做expand*/
return expand_fdtable(files, nr);
}
进入expand_fdtable
static int expand_fdtable(struct files_struct *files, int nr)
__releases(files->file_lock)
__acquires(files->file_lock)
{
struct fdtable *new_fdt, *cur_fdt;
spin_unlock(&files->file_lock);
/* 申请新的文件表 */
new_fdt = alloc_fdtable(nr);
spin_lock(&files->file_lock);
if (!new_fdt)
return -ENOMEM;
/*
* extremely unlikely race - sysctl_nr_open decreased between the check in
* caller and alloc_fdtable(). Cheaper to catch
it here...
*/
/* 如注释所示,由于竞争,有可能在申请nr个数的新文件表时,修改了sysctl_nr_open,导致新的文件表个数 小于我们所需要的。所以,这里需要对new_fdt->max_fds和nr进行判断。如果小于nr,那么expand失败 */
if (unlikely(new_fdt->max_fds <= nr)) {
__free_fdtable(new_fdt);
return -EMFILE;
}
/*
* Check again since another task may have expanded the fd table while
* we dropped the lock
*/
cur_fdt = files_fdtable(files);
/* 如注释所示,有可能另外一个进程已经扩展了文件表,所以这里再次判断 */
if (nr >= cur_fdt->max_fds) {
/* 复制文件表 */
/* Continue as planned */
copy_fdtable(new_fdt, cur_fdt);
rcu_assign_pointer(files->fdt, new_fdt);
/*
对于文件表结构struct files_struct,其成员变量fd_array为一个大小为NR_OPEN_DEFAULT的数 组。这是一种常用的技巧。一般来说,进程的打开的文件不会太多,所以可以直接使用一个比较小的数组,这 样可以提高效率避免二次分配,同时由于数组较小,并不会太浪费空间。当文件个数超过数组大小的时候,再 重新申请内存。*/
if (cur_fdt->max_fds > NR_OPEN_DEFAULT)
free_fdtable(cur_fdt);
} else {
/* Somebody else expanded, so
undo our attempt */
__free_fdtable(new_fdt);
}
return 1;
}
expand_files结束之后,alloc_fd也学习完了,即宏get_unused_fd_flags也就结束了,那么我们就再次回到了函数sock_alloc_file,继续学习其调用的其它函数。
TCP/IP源码学习(47)——socket与VFS的关联(1)
摘自:http://blog.chinaunix.net/uid-23629988-id-3080166.html
相关文章推荐
- TCP/IP源码学习(47)——socket与VFS的关联(1) 2 http://blog.chinaunix.net/uid-23629988-id-3080166.html
- TCP/IP源码学习(53)——TCP的连接过程的实现(2)
- Linux TCP/IP 协议栈学习(4)—— Linux Socket (Part II)
- TCP/IP源码学习(52)——TCP的连接过程的实现(1)
- TCP/IP源码学习(52)——TCP的连接过程的实现(1)
- 三个源码包libnet、libnids、libpcap学习TCP/IP
- 【DAY18】Socket编程,ROSE建模与TCP/IP的学习笔记
- unix学习笔记-----王保明学习笔记-----socket编程进阶---tcpip的11中状态
- TCP/IP源码学习(54)——TCP的连接过程的实现(3)
- socket实现tcp/ip服务端源码,无线程
- TCP/IP学习(31)——TCP接收数据包如何选择socket
- Redis源码学习之【Tcp Socket封装】
- Java TCP/IP Socket 编程学习笔记系列 +数据结构
- TCP/IP socket 编程 学习笔记
- Linux TCP/IP 协议栈学习(3)—— Linux Socket (Part I)
- 简述Socket,IP,TCP三次握手,HTTP协议(有助于理解、记忆和学习)
- 简述Socket,IP,TCP三次握手,HTTP协议(有助于理解、记忆和学习)
- web开发 php之 socket(TCP/IP)原理 学习、理解和应用
- TCP/IP源码学习(58)——inet_select_addr函数分析
- 【tcp-ip学习总结】socket编程基础/网络编程基础