Linux SockFS文件系统分析1(基于Linux6.6)---SockFS定义与注册接口介绍
一、概述
1. SockFS 的背景和作用
在传统的操作系统中,网络通信通常是通过套接字接口(如 socket()
系统调用)来实现的。Linux 内核为此提供了一个通用的网络抽象层(net
子系统),它包括不同类型的网络协议(如 IPv4、IPv6、Unix 域套接字等)。SockFS
的目的是将套接字操作与文件系统操作进行统一,使得网络通信能够通过类似文件的接口进行处理。
在 Linux 中,一切皆文件,网络套接字也不例外。Linux 内核使用 SockFS
作为文件系统的一部分,它将套接字视作文件,允许应用程序通过文件描述符进行套接字操作,这使得网络通信可以与本地文件操作一样进行管理。
2. SockFS 的核心功能
SockFS
通过文件系统接口(如 open()
, read()
, write()
, ioctl()
等)与套接字进行交互,实现了网络通信的抽象。它提供以下核心功能:
- 套接字的文件表示:
SockFS
通过文件描述符表示网络套接字,允许应用程序通过文件系统接口进行网络操作。 - 不同类型的套接字支持:
SockFS
支持不同类型的套接字,如:- 流式套接字(
SOCK_STREAM
):例如 TCP 套接字。 - 数据报套接字(
SOCK_DGRAM
):例如 UDP 套接字。 - 原始套接字(
SOCK_RAW
):用于处理低层网络协议。 - Unix 域套接字(
AF_UNIX
):用于进程间通信(IPC)。
- 流式套接字(
- 与网络协议栈的集成:
SockFS
与内核中的网络协议栈紧密集成,支持各种网络协议(如 TCP/IP、UDP、UNIX 套接字等),将它们通过文件系统接口暴露给用户空间应用程序。 - 支持异步操作:
SockFS
允许异步 I/O 操作,允许非阻塞模式和事件驱动的编程模型(如select()
和poll()
)。
3. SockFS 的工作流程
SockFS
的工作方式与传统的文件系统类似,但它的目标是处理网络通信的套接字。以下是一个套接字的操作流程概述:
-
套接字创建: 当应用程序调用
socket()
系统调用时,Linux 内核会创建一个套接字对象,这个套接字对象实际上是一个与网络协议栈相关的内核数据结构。套接字对象可以通过文件描述符访问。 -
套接字与
SockFS
的关联: 套接字与SockFS
文件系统进行关联。当套接字被创建后,SockFS
将会管理该套接字,并通过文件系统接口提供对该套接字的访问。 -
操作套接字:
- 读取数据:当应用程序执行
read()
操作时,内核会通过网络协议栈接收数据,并将数据返回给用户空间。 - 写入数据:当执行
write()
操作时,内核会通过网络协议栈将数据发送到网络上。 - 关闭套接字:调用
close()
时,内核会释放与该套接字相关的资源,并关闭套接字。
- 读取数据:当应用程序执行
-
与其他协议栈的集成: 由于套接字的抽象,
SockFS
能够与多种网络协议(如 TCP/IP、Unix 域套接字等)集成。例如,对于 Unix 域套接字,SockFS
会与本地的 Unix 域套接字协议栈进行交互;对于 IPv4/IPv6 套接字,SockFS
会与相应的网络协议栈(如 TCP/IP 协议栈)交互。 -
事件驱动和异步操作:
SockFS
还支持异步 I/O 操作,如select()
和poll()
。这使得应用程序能够以事件驱动的方式高效地处理多个套接字的读取和写入操作,而不需要阻塞在某一个套接字上。
二、sockfs文件系统类型的定义及注册
针对sockfs,其在sock_init接口中,进行文件系统的注册以及挂载操作,sock_init的接口流程图如下:
通过调用register_filesystem实现文件系统的注册。
其中sockfs文件系统的定义如下:
net/socket.c
static struct file_system_type sock_fs_type = {
.name = "sockfs",
.init_fs_context = sockfs_init_fs_context,
.kill_sb = kill_anon_super,
};
在vfs_kern_mount->mount_fs->type->
init_fs_context时,即调用
sockfs_init_fs_context接口,进行超级块、根root、根dentry相关的创建及初始化操作。如下接口定义,其中sockfs_ops为sockfs的超级块变量相关的操作接口,而sockfs_dentry_operations为sockfs的根dentry的操作接口。
net/socket.c
static int sockfs_init_fs_context(struct fs_context *fc)
{
struct pseudo_fs_context *ctx = init_pseudo(fc, SOCKFS_MAGIC);
if (!ctx)
return -ENOMEM;
ctx->ops = &sockfs_ops;
ctx->dops = &sockfs_dentry_operations;
ctx->xattr = sockfs_xattr_handlers;
return 0;
}
socketfs 超级块的操作接口,主要包括:
- inode节点的创建接口sock_alloc_inode;
- inode节点的删除接口sock_free_inode;
- 文件系统的状态获取接口simple_statfs,可获取文件系统的类型、块大小、文件系统名称大小等
net/socket.c
static const struct super_operations sockfs_ops = {
.alloc_inode = sock_alloc_inode,
.free_inode = sock_free_inode,
.statfs = simple_statfs,
};
struct socket_alloc {
struct socket socket;
struct inode vfs_inode;
};
下面稍微说明下alloc_inode接口,在介绍该接口之前引入结构体struct socket_alloc,该结构体中包含了vfs模块的inode和socket模块的socket变量,可以说该变量链接了VFS与SOCKETFS模块。
分析下sock_alloc_inode函数,该函数也就是创建struct socket_alloc类型的指针变量,并对该变量中的inode与socket变量进行初始化,具体功能为:
1.初始化socket变量的状态、flag、ops、sk、file等成员变量;
2.初始化socket的等待队列成员变量wq。
net/socket.c
static struct inode *sock_alloc_inode(struct super_block *sb)
{
struct socket_alloc *ei;
ei = alloc_inode_sb(sb, sock_inode_cachep, GFP_KERNEL);
if (!ei)
return NULL;
init_waitqueue_head(&ei->socket.wq.wait);
ei->socket.wq.fasync_list = NULL;
ei->socket.wq.flags = 0;
ei->socket.state = SS_UNCONNECTED;
ei->socket.flags = 0;
ei->socket.ops = NULL;
ei->socket.sk = NULL;
ei->socket.file = NULL;
return &ei->vfs_inode;
}
而针对dentry的处理操作接口sockfs_dentry_operations,则提供了sockfs_dname接口,该接口用于生成dentry的名称。
net/socket.c
static const struct dentry_operations sockfs_dentry_operations = {
.d_dname = sockfs_dname,
};