Linux内核将一切都看作为文件,因此网络协议栈以文件系统sockfs的形式存在于内核中,该文件系统中的文件则是我们熟知的套接字。稍微熟知Linux文件系统的人都会知道内核中将所有文件系统进行抽象,通过虚拟文件系统(VFS)来统一管理。网络文件系统也是如此,也就是说超级块,索引节点,目录项等概念在网路文件系统中也存在。
在proc文件系统下,我们可以通过下面的方式得到内核当前所有文件系统的名称:
[root@TENCENT64 ~]# cat /proc/filesystems nodev sysfs nodev rootfs nodev bdev nodev proc nodev tmpfs nodev debugfs nodev sockfs nodev pipefs nodev anon_inodefs nodev rpc_pipefs nodev inotifyfs ………… ext3 ext2 ext4
网络文件系统和ext3这样的文件系统有什么区别?前者只存在于内存中,而后者则存在于磁盘中。比如上述信息中的sockfs和tmpfs,甚至是proc,在第一列均显示nodev,也就是说这些文件系统并没有存储在磁盘这样的块设备中。相反,它们存储在内存中,即采用的是一种非持久性的存储方式。
这里应该注意的是sockfs的“虚拟”和VFS的“虚拟”是两个不同的概念。前者表示sockfs这种虚拟文件系统不同于ext2这样的磁盘文件系统,它们存储在内存中,是一种非持久的存储,只要系统重启信息全部丢失。而虚拟文件系统中的“虚拟”表示的是对所有文件系统的一种抽象,它将所有不同种类文件系统的共同信息放入同一组数据结构当中,为上层用户提供了统一的接口。
VFS中用于描述文件系统类型的数据结构为file_system_type,所有文件系统都对应该类型的变量:
static struct file_system_type sock_fs_type = { .name = "sockfs", .mount = sockfs_mount, .kill_sb = kill_anon_super, }; static struct file_system_type proc_fs_type = { .name = "proc", .mount = proc_mount, .kill_sb = proc_kill_sb, };
可以看到,/proc/filesystem中的文件系统名称即从file_system_type结构的实例中获得。
socket结构
内核中通过socket结构体来表示套接字。该结构中包含的字段比较少,比如描述套接字状态的state,套接字标志位flags,等待队列wp以及与该套接字关联的文件对象。
struct socket { socket_state state; kmemcheck_bitfield_begin(type); short type; kmemcheck_bitfield_end(type); unsigned long flags; struct socket_wq __rcu *wq; struct file *file; struct sock *sk; const struct proto_ops *ops; };
在socket结构中包含了一个与它比较相似的结构sock,该结构也是对套接字进行描述的。
sock结构体
sock结构中包含大量的字段,这些字段更多的是用来描述协议的通用属性,而socket结构则是更多的描述与应用程序相关的部分。
sk_buff结构体
sk_buff用于对网络协议中的数据包进行描述,不管该数据包位于那一层协议,都会有一个sk_buff结构与之对应。而sk_buff结构通过其内部的几个指针字段就可以轻松完成加包头去包头的操作。
三者的关系
每个socket结构都有一个sk字段指向sock结构,而sock结构中通过sk_socket字段反指向socket结构。sock结构通过sk_receive_queue和sk_write_queue等字段指向数据包头结构(struct sk_buff_head),而这个结构内部又通过prev和next两个字段指向真正的数据包结构sk_buff。