LINUX SOCKFS文件系统分析之二 相关的结构体及结构体之间的关联
对于网络协议簇而言,包括ax25协议簇、inet4协议簇、inet6协议簇、ipx协议簇等,而每一个协议
簇中又包含多个协议,以inet4协议簇为例,又包含ipv4、tcp、udp、icmp等协议。
而针对网络协议簇与网络协议之间的关系,linux sockfs的架构也按此进行了划分。
针对sockfs,其也兼容VFS架构,但相比于普通文件及设备文件而言,也有不同,针对socket文件的
操作,linux提供了一系列专用的系统调用接口,包括socket、listen、shutdown、connect、bind、accept、recvmsg、getsockname、getpeername、sendmsg、socketpair、send、setsockopt、getsockopt、recvfrom、sendto等接口,使用这些接口可实现上述所说协议簇中的各协议的socket通信。
针对sockfs而言,其系统调用socket与open实现的功能类似,不同的是socket fd中增加了针对网络
协议簇相关的一套框架,用于实现对上述所说的packet协议簇、ipc socket、netlink、ipv4、ipv6等协议的socket通信。这些结构体包括struct sock、struct socket、struct net_proto、struct inet_protosw、struct proto、struct proto_ops,首先我们分析这些结构体的定义以及这些结构体的关联,待我们理解这些结构体之后,再分析具体的实现接口。
在分析每一个结构体之前,我们先看下这些结构体有什么关联,先从整体上了解这些结构体,然后再分而治之。
struct inet_protosw、struct proto、struct proto_ops、struct socket、struct sock、struct file之间的关联
针对这几个结构体以及文件描述符结构体struct file,下图便是这几个结构体的关联。这些结构体的主要关联有:
- 将vfs与sockfs进行了关联;
- 将socket与具体协议处理接口、具体的socket type处理接口完成了关联操作;
- 在af_inet4中,存在struct inet_protosw与socket类型对应的操作接口、具体协议类型对应的操作接口的关联,实现了通过socket即可调用具体协议类型对应处理接口,处理链接创建与删除、数据收发等操作。
介绍完结构体之间的联系后,我们再逐个分析每一个结构体的定义。
1.struct socket分析
结构体socket用于描述一个socket,该结构体的定义如下:
- state定义了socket的状态(包括SS_UNCONNECTED、SS_CONNECTING、SS_CONNECTED、SS_DISCONNECTING等);
- file表示文件描述符,用于与vfs关联;
- struct sock *sk为socket结构体的关键成员变量,后面介绍;
- struct proto_ops *ops用于执行协议相关的socket处理接口。
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;
};
2.struct sock分析
该结构体的定义如下(由于该结构体的内容较多,仅显示主要的几个成员变量),主要包括__sk_common变量包括源、目的ip地址等、socket收发的队列等信息
struct sock {
struct sock_common __sk_common;
#define sk_prot __sk_common.skc_prot
struct sk_filter __rcu *sk_filter;
struct socket_wq __rcu *sk_wq;
struct proto *sk_prot_creator;
struct sk_buff_head sk_receive_queue;
struct sk_buff_head sk_write_queue;
…
}
struct sock_common {
/* 源、目的ip地址 */
union {
__addrpair skc_addrpair;
struct {
__be32 skc_daddr;
__be32 skc_rcv_saddr;
};
};
union {
unsigned int skc_hash;
__u16 skc_u16hashes[2];
};
/* 目的端口号等*/
union {
__portpair skc_portpair;
struct {
__be16 skc_dport;
__u16 skc_num;
};
};
unsigned short skc_family;//所属协议簇
volatile unsigned char skc_state;//socket状态
unsigned char skc_reuse:4;
unsigned char skc_reuseport:4;
int skc_bound_dev_if;
union {
struct hlist_node skc_bind_node;
struct hlist_nulls_node skc_portaddr_node;
};
struct proto *skc_prot;//传输层之上的协议处理接口
#ifdef CONFIG_NET_NS
struct net *skc_net;
#endif
};
3.struct net_proto_family分析
该结构体主要用于说明网络协议簇,即针对ax25协议簇、inet4协议簇、inet6协议簇、ipx协议簇等
相关的定义,该结构体的定义如下:
- family用于说明协议簇的类型,目前linux支持的协议簇类型包括AF_UNIX、AF_LOCAL、AF_INET、AF_INET6、AF_NETLINK等;
- create接口函数,该接口用于初始化strcut sock类型的指针变量,根据传递的协议号,设置协议相关的接口函数。
struct net_proto_family {
int family;
int (*create)(struct net *net, struct socket *sock,
int protocol, int kern);
struct module *owner;
};
在socketfs中定义了struct net_proto_family类型的全局指针数组net_families,该数组中包括了所有
linux内核支持的协议簇,在sockfs初始化时,调用接口sock_register将协议簇的指针存储至该数组中,该全局的变量的关联如下所示,在进行socket的创建时,会根据传递的协议簇类型,从该结构体中获取对应struct net_proto_family类型变量,并调用其create接口进行socket的初始化与操作接口的设置等操作。
该全局变量的作用:
- 表示所有在linux系统中已注册的协议簇变量;
- 在进行socket创建时,根据传递的协议簇类型,找到对应的协议簇变量,进入相应协议簇变量的处理分支,继续进行socket相关的处理操作。
4.struct inet_protosw分析(inet协议簇)
因本文主要介绍inet相关的实现,因此本处介绍struct inet_protosw结构体,
该结构体用于说明一个协议簇中针对不同协议所支持的协议处理操作以及对应的sock的表征,其主要内容如下:
- 其中type用于指示socket的类型,socket的类型包括SOCK_STREAM、SOCK_DGRAM具体的类型在下面有说明;
- protocol用于说明协议的类型(包括ip、tcp、udp、icmp、dccp等);
- prot用于指向协议相关的处理接口;
- proto_ops用于指向socket类型的处理接口,即SOCK_STREAM、SOCK_DGRAM等对应的处理接口
struct inet_protosw {
struct list_head list;
/* These two fields form the lookup key. */
unsigned short type; /* This is the 2nd argument to socket(2). */
unsigned short protocol; /* This is the L4 protocol number. */
struct proto *prot;
const struct proto_ops *ops;
char no_check; /* checksum on rcv/xmit/none? */
unsigned char flags; /* See INET_PROTOSW_* below. */
};
enum sock_type {
SOCK_STREAM = 1,
SOCK_DGRAM = 2,
SOCK_RAW = 3,
SOCK_RDM = 4,
SOCK_SEQPACKET = 5,
SOCK_DCCP = 6,
SOCK_PACKET = 10,
};
5.struct proto_ops分析(inet协议簇)
该结构体主要用于描述socket类型(SOCK_STREAM、SOCK_DGRAM等)的处理接口,该结构体定义如下:
family用于说明协议簇的类型
release、bind、connect、accept、poll、ioctl、listen、shutdown、setsockopt等接口,这些接口与sockfs提供的系统调用接口对应。
struct proto_ops {
int family;
struct module *owner;
int (*release) (struct socket *sock);
int (*bind) (struct socket *sock,
struct sockaddr *myaddr,
int sockaddr_len);
int (*connect) (struct socket *sock,
struct sockaddr *vaddr,
int sockaddr_len, int flags);
int (*socketpair)(struct socket *sock1,
struct socket *sock2);
int (*accept) (struct socket *sock,
struct socket *newsock, int flags);
int (*getname) (struct socket *sock,
struct sockaddr *addr,
int *sockaddr_len, int peer);
unsigned int (*poll) (struct file *file, struct socket *sock,
struct poll_table_struct *wait);
int (*ioctl) (struct socket *sock, unsigned int cmd,
unsigned long arg);
#ifdef CONFIG_COMPAT
int (*compat_ioctl) (struct socket *sock, unsigned int cmd,
unsigned long arg);
#endif
int (*listen) (struct socket *sock, int len);
int (*shutdown) (struct socket *sock, int flags);
int (*setsockopt)(struct socket *sock, int level,
int optname, char __user *optval, unsigned int optlen);
int (*getsockopt)(struct socket *sock, int level,
int optname, char __user *optval, int __user *optlen);
#ifdef CONFIG_COMPAT
int (*compat_setsockopt)(struct socket *sock, int level,
int optname, char __user *optval, unsigned int optlen);
int (*compat_getsockopt)(struct socket *sock, int level,
int optname, char __user *optval, int __user *optlen);
#endif
int (*sendmsg) (struct kiocb *iocb, struct socket *sock,
struct msghdr *m, size_t total_len);
int (*recvmsg) (struct kiocb *iocb, struct socket *sock,
struct msghdr *m, size_t total_len,
int flags);
int (*mmap) (struct file *file, struct socket *sock,
struct vm_area_struct * vma);
ssize_t (*sendpage) (struct socket *sock, struct page *page,
int offset, size_t size, int flags);
ssize_t (*splice_read)(struct socket *sock, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len, unsigned int flags);
void (*set_peek_off)(struct sock *sk, int val);
};
6.struct proto 分析(inet协议簇)
该结构体主要用于具体协议相关的操作接口,对于大多数协议的socket而言,只需要使用上述5中注册的struct proto_ops提供的操作接口,即可完成对sockfs的系统调用的实现,而对一些协议(如udp、icmp、tcp协议),则需要特定的处理函数,这些处理函数的定义即使用struct proto结构体进行定义,该结构体的定义如下:
struct proto {
void (*close)(struct sock *sk,
long timeout);
int (*connect)(struct sock *sk,
struct sockaddr *uaddr,
int addr_len);
int (*disconnect)(struct sock *sk, int flags);
struct sock * (*accept)(struct sock *sk, int flags, int *err);
int (*ioctl)(struct sock *sk, int cmd,
unsigned long arg);
int (*init)(struct sock *sk);
void (*destroy)(struct sock *sk);
void (*shutdown)(struct sock *sk, int how);
int (*setsockopt)(struct sock *sk, int level,
int optname, char __user *optval,
unsigned int optlen);
int (*getsockopt)(struct sock *sk, int level,
int optname, char __user *optval,
int __user *option);
…
int *memory_pressure;
long *sysctl_mem;
int *sysctl_wmem;
int *sysctl_rmem;
int max_header;
bool no_autobind;
…
struct list_head node;
};
结构体之间的关联
上面已经介绍了sockfs相关的结构体,下面描述下这些结构体之间的关联。
struct inet_protosw、struct proto及struct proto_ops之间的关联
在af_inet4中,这三个结构体之间的关联如上所示,另外针对strcut inet_protosw类型,定义了全局指针inetsw,用于链接inet4相关的所有strcut inet_protosw类型的变量,af_inet4注册的变量如下所示,包含了tcp、udp、icmp、raw socket等struct inet_protosw类型的变量。
本小节主要讲述了各结构体的定义以及结构体的关联,通过将这些结构体之间的关系理清,能帮助我们更好的理解sockfs。