Linux套接字与虚拟文件系统(1):初始化和创建
引言
在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中。
1
static
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
}

2

3


4

5

6

7


8

创建inode缓存
init_inodecache为socket_alloc对象创建SLAB缓存,名称为sock_inode_cachep,socket_alloc定义在include/net/sock.h中。
1
struct
socket_alloc
{
2
struct socket socket;
3
struct inode vfs_inode;
4
}
;

2

3

4

socket_alloc由socket和inode结构2部分组成,这样就方便了在套接字与inode对象间双向定位。
注册sockfs
调用VFS的函数register_filesystem实现注册,sock_fs_type定义在net/socket.c中。
注册sockfs
调用VFS的函数register_filesystem实现注册,sock_fs_type定义在net/socket.c中。
1
static
struct
file_system_type
sock_fs_type
=
{
2
.name = "sockfs",
3
.get_sb = sockfs_get_sb,
4
.kill_sb = kill_anon_super,
5
}
;

2

3

4

5

1
static
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
}

2

3

4

sockfs_ops定义在net/socket.c中。
1
static
const
struct
super_operations
sockfs_ops
=
{
2
.alloc_inode = sock_alloc_inode,
3
.destroy_inode = sock_destroy_inode,
4
.statfs = simple_statfs,
5
}
;

2

3

4

5

装载sockfs
由kern_mount函数实现装载一个伪文件系统(当然,它没有装载点),返回一个static vfsmount对象sock_mnt。
经过以上步骤后,所创建的VFS对象关系如下图:
Socket创建
系统调用socket、accept和socketpair是用户空间创建socket的几种方法,其核心调用链如下图:
从上图可知共同的核心就3个过程:先构造inode,再构造对应的file,最后安装file到当前进程中(即关联映射到一个未用的文件描述符),下面就这3个过程进行详细说明。
构造inode
由sock_alloc函数实现,定义在net/socket.c中。
1
static
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
}

2

3

4

5

6

7


8

9


10

11

12

13


14

15

构造file
有了inode对象后,接下来就要构造对应的file对象了,由sock_alloc_file实现,定义在net/socket.c中。
1
static
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
*f = file;
26
return fd;
27
}

2

3

4

5

6

7

8

9


10

11


12

13

14

15

16

17

18

19


20

21

22

23

24

25

26

27

1)得到空闲的文件描述符fd,实际上就是fd数组的索引,准备作为返回值。
2)先初始化路径path:其目录项的父目录项为超级块对应的根目录,名称为空,操作对象为sockfs_dentry_operations,对应的索引节点对象为sock套接字关联的索引节点对象,即SOCK_INODE(sock);装载点为sock_mnt。
sockfs_dentry_operations定义在net/socket.c中。
1
static
const
struct
dentry_operations
sockfs_dentry_operations
=
{
2
.d_dname = sockfs_dname,
3
}
;

2

3

3)设置索引节点的文件操作对象为socket_file_ops,定义在net/socket.c中。
1
static
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
}
;

2


3

4

5


6

7

8


9

5)建立file与socket的一一映射关系。
安装file
由fd_install实现,定义在fs/open.c中。
1
void
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
}

2

3

4

5

6

7

8

9

10

经过以上过程后,所创建的VFS对象关系图如下
posted on 2015-05-03 16:31 春秋十二月 阅读(1854) 评论(0) 编辑 收藏 引用 所属分类: Network