Linux SockFS文件系统分析2(基于Linux6.6)---SockFS结构体介绍
前言
SockFS 是 Linux 内核中专门用于处理网络套接字的虚拟文件系统(VFS)实现。虽然它兼容 Linux 的 VFS 架构,将套接字作为文件来管理,但它与普通的文件系统和设备文件有明显的区别。特别是在对待套接字文件时,Linux 提供了一系列专用的系统调用和接口来处理网络通信,以下是这些接口的详细说明。
1. 与普通文件的区别
在 VFS 中,所有文件类型都被抽象成文件描述符,包括普通文件、设备文件和套接字文件。但与普通文件或设备文件不同,套接字文件有其独特的行为和操作方式:
-
普通文件操作:普通文件主要支持基于字节的读写操作,通常是线性存储介质上的数据。常见操作包括
open(),read(),write(),close()等。 -
设备文件操作:设备文件用于与硬件设备进行交互。它们的读写操作通常通过设备驱动程序来实现,表现为字符设备或块设备。
-
套接字文件操作:套接字则用于进程间或进程与网络之间的通信,涉及网络协议栈。操作网络套接字的接口和行为较为复杂,除了支持普通的文件操作接口外,还需要特定的协议控制、连接管理、数据传输等特性。
2. Linux 提供的专用系统调用接口
为了有效地操作套接字文件,Linux 提供了一些专门的系统调用接口,这些接口不仅限于 VFS 文件操作,还涉及到网络协议、连接管理、错误处理、数据收发等内容。以下是一些与套接字相关的常见系统调用:
a. socket()
这是创建套接字的基本系统调用,它用于创建一个新的套接字,返回一个文件描述符。它需要指定:
- 地址族(如
AF_INET、AF_INET6或AF_UNIX等); - 套接字类型(如
SOCK_STREAM、SOCK_DGRAM等); - 协议(如
IPPROTO_TCP、IPPROTO_UDP等)。
int socket(int domain, int type, int protocol);
domain:指定协议族(例如AF_INET表示 IPv4)。type:套接字类型(例如SOCK_STREAM表示流式套接字,SOCK_DGRAM表示数据报套接字)。protocol:通常设置为 0,由系统自动选择合适的协议,或者显式指定特定协议(如IPPROTO_TCP)。
b. bind()
bind() 用于将一个套接字与一个本地地址(如 IP 地址和端口号)绑定。这在服务器端特别常见,服务器需要绑定到一个特定的地址和端口,才能等待客户端的连接。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:由socket()创建的套接字文件描述符。addr:包含本地地址的结构体(如struct sockaddr_in用于 IPv4)。addrlen:地址结构体的长度。
c. listen() 和 accept()
这两个函数用于 TCP 套接字的连接管理。
listen():将套接字设置为被动监听状态,准备接受来自客户端的连接请求。
int listen(int sockfd, int backlog);
accept():接受来自客户端的连接,创建一个新的套接字用于与客户端通信。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
d. connect()
connect() 用于客户端套接字与远程服务器建立连接。它会尝试连接到指定的远程地址和端口。
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:由socket()创建的套接字文件描述符。addr:包含远程地址的结构体(如struct sockaddr_in用于 IPv4)。addrlen:地址结构体的长度。
e. read() 和 write()
这些函数与文件系统中的 read() 和 write() 类似,但它们处理的是套接字的网络数据传输。
read():从套接字读取数据,通常是从远程主机接收数据。
ssize_t read(int fd, void *buf, size_t count);
write():将数据写入套接字,通常是将数据发送到远程主机。
ssize_t write(int fd, const void *buf, size_t count);
对于套接字文件来说,read() 和 write() 也可以是阻塞的,具体取决于套接字的配置。如果套接字以非阻塞模式打开,这些操作将不会被阻塞,应用程序可以使用 select() 或 poll() 等机制来等待数据准备好。
f. send() 和 recv()
这些是专门用于网络套接字的发送和接收函数,它们提供了更多的控制选项,如消息标志(flags)、控制数据的大小等。
send():将数据发送到指定套接字。
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
recv():从指定套接字接收数据。
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
g. shutdown()
shutdown() 用于关闭一个套接字的某些操作方向(例如,关闭只读、只写或者双向关闭)。它可以用于优雅地终止套接字通信。
int shutdown(int sockfd, int how);
how参数指定了关闭的方向:SHUT_RD:关闭读取。SHUT_WR:关闭写入。SHUT_RDWR:关闭读取和写入。
h. close()
关闭套接字,释放与套接字相关的内核资源。
int close(int sockfd);
3. 其他重要的网络操作函数
除了这些基本的系统调用外,还有一些专门用于网络套接字的控制函数:
-
setsockopt():设置套接字的选项,如超时、缓冲区大小、TCP_NODELAY 等。
-
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); -
getsockopt():获取套接字的当前选项。
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
对于网络协议簇而言,包括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,首先分析这些结构体的定义以及这些结构体的关联,再分析具体的实现接口。
一、inet_protosw、 proto、 proto_ops、socket、 sock、file之间的关联
针对这几个结构体以及文件描述符结构体struct file,下图便是这几个结构体的关联。这些结构体的主要关联有:
- 将vfs与sockfs进行了关联;
- 将socket与具体协议处理接口、具体的socket type处理接口完成了关联操作;
- 在af_inet4中,存在struct inet_protosw与socket类型对应的操作接口、具体协议类型对应的操作接口的关联,实现了通过socket即可调用具体协议类型对应处理接口,处理链接创建与删除、数据收发等操作。

二、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处理接口。
include/linux/net.h
struct socket {
socket_state state;
short type;
unsigned long flags;
struct file *file;
struct sock *sk;
const struct proto_ops *ops; /* Might change with IPV6_ADDRFORM or MPTCP. */
struct socket_wq wq;
};
三、struct sock分析
该结构体的定义如下(由于该结构体的内容较多,仅显示主要的几个成员变量),主要包括__sk_common变量包括源、目的ip地址等、socket收发的队列等信息.
include/net/sock.h
struct sock {
/*
* Now struct inet_timewait_sock also uses sock_common, so please just
* don't add nothing before this first member (__sk_common) --acme
*/
struct sock_common __sk_common;
#define sk_node __sk_common.skc_node
#define sk_nulls_node __sk_common.skc_nulls_node
#define sk_refcnt __sk_common.skc_refcnt
#define sk_tx_queue_mapping __sk_common.skc_tx_queue_mapping
#ifdef CONFIG_SOCK_RX_QUEUE_MAPPING
#define sk_rx_queue_mapping __sk_common.skc_rx_queue_mapping
#endif
#define sk_dontcopy_begin __sk_common.skc_dontcopy_begin
#define sk_dontcopy_end __sk_common.skc_dontcopy_end
#define sk_hash __sk_common.skc_hash
#define sk_portpair __sk_common.skc_portpair
#define sk_num __sk_common.skc_num
#define sk_dport __sk_common.skc_dport
#define sk_addrpair __sk_common.skc_addrpair
#define sk_daddr __sk_common.skc_daddr
#define sk_rcv_saddr __sk_common.skc_rcv_saddr
#define sk_family __sk_common.skc_family
#define sk_state __sk_common.skc_state
#define sk_reuse __sk_common.skc_reuse
#define sk_reuseport __sk_common.skc_reuseport
#define sk_ipv6only __sk_common.skc_ipv6only
#define sk_net_refcnt __sk_common.skc_net_refcnt
#define sk_bound_dev_if __sk_common.skc_bound_dev_if
#define sk_bind_node __sk_common.skc_bind_node
#define sk_prot __sk_common.skc_prot
#define sk_net __sk_common.skc_net
#define sk_v6_daddr __sk_common.skc_v6_daddr
#define sk_v6_rcv_saddr __sk_common.skc_v6_rcv_saddr
#define sk_cookie __sk_common.skc_cookie
#define sk_incoming_cpu __sk_common.skc_incoming_cpu
#define sk_flags __sk_common.skc_flags
#define sk_rxhash __sk_common.skc_rxhash
/* early demux fields */
struct dst_entry __rcu *sk_rx_dst;
int sk_rx_dst_ifindex;
u32 sk_rx_dst_cookie;
socket_lock_t sk_lock;
atomic_t sk_drops;
int sk_rcvlowat;
struct sk_buff_head sk_error_queue;
struct sk_buff_head sk_receive_queue;
/*
* The backlog queue is special, it is always used with
* the per-socket spinlock held and requires low latency
* access. Therefore we special case it's implementation.
* Note : rmem_alloc is in this structure to fill a hole
* on 64bit arches, not because its logically part of
* backlog.
*/
struct {
atomic_t rmem_alloc;
int len;
struct sk_buff *head;
struct sk_buff *tail;
} sk_backlog;
#define sk_rmem_alloc sk_backlog.rmem_alloc
int sk_forward_alloc;
u32 sk_reserved_mem;
#ifdef CONFIG_NET_RX_BUSY_POLL
unsigned int sk_ll_usec;
/* ===== mostly read cache line ===== */
unsigned int sk_napi_id;
#endif
int sk_rcvbuf;
int sk_disconnects;
struct sk_filter __rcu *sk_filter;
union {
struct socket_wq __rcu *sk_wq;
/* private: */
struct socket_wq *sk_wq_raw;
/* public: */
};
#ifdef CONFIG_XFRM
struct xfrm_policy __rcu *sk_policy[2];
#endif
struct dst_entry __rcu *sk_dst_cache;
atomic_t sk_omem_alloc;
int sk_sndbuf;
/* ===== cache line for TX ===== */
int sk_wmem_queued;
refcount_t sk_wmem_alloc;
unsigned long sk_tsq_flags;
union {
struct sk_buff *sk_send_head;
struct rb_root tcp_rtx_queue;
};
struct sk_buff_head sk_write_queue;
__s32 sk_peek_off;
int sk_write_pending;
__u32 sk_dst_pending_confirm;
u32 sk_pacing_status; /* see enum sk_pacing */
long sk_sndtimeo;
struct timer_list sk_timer;
__u32 sk_priority;
__u32 sk_mark;
unsigned long sk_pacing_rate; /* bytes per second */
unsigned long sk_max_pacing_rate;
struct page_frag sk_frag;
netdev_features_t sk_route_caps;
int sk_gso_type;
unsigned int sk_gso_max_size;
gfp_t sk_allocation;
__u32 sk_txhash;
/*
* Because of non atomicity rules, all
* changes are protected by socket lock.
*/
u8 sk_gso_disabled : 1,
sk_kern_sock : 1,
sk_no_check_tx : 1,
sk_no_check_rx : 1,
sk_userlocks : 4;
u8 sk_pacing_shift;
u16 sk_type;
u16 sk_protocol;
u16 sk_gso_max_segs;
unsigned long sk_lingertime;
struct proto *sk_prot_creator;
rwlock_t sk_callback_lock;
int sk_err,
sk_err_soft;
u32 sk_ack_backlog;
u32 sk_max_ack_backlog;
kuid_t sk_uid;
u8 sk_txrehash;
#ifdef CONFIG_NET_RX_BUSY_POLL
u8 sk_prefer_busy_poll;
u16 sk_busy_poll_budget;
#endif
spinlock_t sk_peer_lock;
int sk_bind_phc;
struct pid *sk_peer_pid;
const struct cred *sk_peer_cred;
long sk_rcvtimeo;
ktime_t sk_stamp;
#if BITS_PER_LONG==32
seqlock_t sk_stamp_seq;
#endif
atomic_t sk_tskey;
atomic_t sk_zckey;
u32 sk_tsflags;
u8 sk_shutdown;
u8 sk_clockid;
u8 sk_txtime_deadline_mode : 1,
sk_txtime_report_errors : 1,
sk_txtime_unused : 6;
bool sk_use_task_frag;
struct socket *sk_socket;
void *sk_user_data;
#ifdef CONFIG_SECURITY
void *sk_security;
#endif
struct sock_cgroup_data sk_cgrp_data;
struct mem_cgroup *sk_memcg;
void (*sk_state_change)(struct sock *sk);
void (*sk_data_ready)(struct sock *sk);
void (*sk_write_space)(struct sock *sk);
void (*sk_error_report)(struct sock *sk);
int (*sk_backlog_rcv)(struct sock *sk,
struct sk_buff *skb);
#ifdef CONFIG_SOCK_VALIDATE_XMIT
struct sk_buff* (*sk_validate_xmit_skb)(struct sock *sk,
struct net_device *dev,
struct sk_buff *skb);
#endif
void (*sk_destruct)(struct sock *sk);
struct sock_reuseport __rcu *sk_reuseport_cb;
#ifdef CONFIG_BPF_SYSCALL
struct bpf_local_storage __rcu *sk_bpf_storage;
#endif
struct rcu_head sk_rcu;
netns_tracker ns_tracker;
struct hlist_node sk_bind2_node;
};
include/net/sock.h
struct sock_common {
union {
__addrpair skc_addrpair;
struct {
__be32 skc_daddr;
__be32 skc_rcv_saddr;
};
};
union {
unsigned int skc_hash;
__u16 skc_u16hashes[2];
};
/* skc_dport && skc_num must be grouped as well */
union {
__portpair skc_portpair;
struct {
__be16 skc_dport;
__u16 skc_num;
};
};
unsigned short skc_family;
volatile unsigned char skc_state;
unsigned char skc_reuse:4;
unsigned char skc_reuseport:1;
unsigned char skc_ipv6only:1;
unsigned char skc_net_refcnt:1;
int skc_bound_dev_if;
union {
struct hlist_node skc_bind_node;
struct hlist_node skc_portaddr_node;
};
struct proto *skc_prot;
possible_net_t skc_net;
#if IS_ENABLED(CONFIG_IPV6)
struct in6_addr skc_v6_daddr;
struct in6_addr skc_v6_rcv_saddr;
#endif
atomic64_t skc_cookie;
/* following fields are padding to force
* offset(struct sock, sk_refcnt) == 128 on 64bit arches
* assuming IPV6 is enabled. We use this padding differently
* for different kind of 'sockets'
*/
union {
unsigned long skc_flags;
struct sock *skc_listener; /* request_sock */
struct inet_timewait_death_row *skc_tw_dr; /* inet_timewait_sock */
};
/*
* fields between dontcopy_begin/dontcopy_end
* are not copied in sock_copy()
*/
/* private: */
int skc_dontcopy_begin[0];
/* public: */
union {
struct hlist_node skc_node;
struct hlist_nulls_node skc_nulls_node;
};
unsigned short skc_tx_queue_mapping;
#ifdef CONFIG_SOCK_RX_QUEUE_MAPPING
unsigned short skc_rx_queue_mapping;
#endif
union {
int skc_incoming_cpu;
u32 skc_rcv_wnd;
u32 skc_tw_rcv_nxt; /* struct tcp_timewait_sock */
};
refcount_t skc_refcnt;
/* private: */
int skc_dontcopy_end[0];
union {
u32 skc_rxhash;
u32 skc_window_clamp;
u32 skc_tw_snd_nxt; /* struct tcp_timewait_sock */
};
/* public: */
};
四、struct net_proto_family分析
该结构体主要用于说明网络协议簇,即针对ax25协议簇、inet4协议簇、inet6协议簇、ipx协议簇等
相关的定义,该结构体的定义如下:
- family用于说明协议簇的类型,目前linux支持的协议簇类型包括AF_UNIX、AF_LOCAL、AF_INET、AF_INET6、AF_NETLINK等;
- create接口函数,该接口用于初始化strcut sock类型的指针变量,根据传递的协议号,设置协议相关的接口函数。
include/linux/net.h
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相关的处理操作。

五、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等对应的处理接口
include/net/protocol.h
/* This is used to register socket interfaces for IP protocols. */
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;
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,
};
六、struct proto_ops分析(inet协议簇)
该结构体主要用于描述socket类型(SOCK_STREAM、SOCK_DGRAM等)的处理接口,该结构体定义如下:
family用于说明协议簇的类型。
release、bind、connect、accept、poll、ioctl、listen、shutdown、setsockopt等接口,这些接口与sockfs提供的系统调用接口对应。
include/linux/net.h
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, bool kern);
int (*getname) (struct socket *sock,
struct sockaddr *addr,
int peer);
__poll_t (*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 (*gettstamp) (struct socket *sock, void __user *userstamp,
bool timeval, bool time32);
int (*listen) (struct socket *sock, int len);
int (*shutdown) (struct socket *sock, int flags);
int (*setsockopt)(struct socket *sock, int level,
int optname, sockptr_t optval,
unsigned int optlen);
int (*getsockopt)(struct socket *sock, int level,
int optname, char __user *optval, int __user *optlen);
void (*show_fdinfo)(struct seq_file *m, struct socket *sock);
int (*sendmsg) (struct socket *sock, struct msghdr *m,
size_t total_len);
/* Notes for implementing recvmsg:
* ===============================
* msg->msg_namelen should get updated by the recvmsg handlers
* iff msg_name != NULL. It is by default 0 to prevent
* returning uninitialized memory to user space. The recvfrom
* handlers can assume that msg.msg_name is either NULL or has
* a minimum size of sizeof(struct sockaddr_storage).
*/
int (*recvmsg) (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 (*splice_read)(struct socket *sock, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len, unsigned int flags);
void (*splice_eof)(struct socket *sock);
int (*set_peek_off)(struct sock *sk, int val);
int (*peek_len)(struct socket *sock);
/* The following functions are called internally by kernel with
* sock lock already held.
*/
int (*read_sock)(struct sock *sk, read_descriptor_t *desc,
sk_read_actor_t recv_actor);
/* This is different from read_sock(), it reads an entire skb at a time. */
int (*read_skb)(struct sock *sk, skb_read_actor_t recv_actor);
int (*sendmsg_locked)(struct sock *sk, struct msghdr *msg,
size_t size);
int (*set_rcvlowat)(struct sock *sk, int val);
};
七、struct proto 分析(inet协议簇)
该结构体主要用于具体协议相关的操作接口,对于大多数协议的socket而言,只需要使用上述5中注册的struct proto_ops提供的操作接口,即可完成对sockfs的系统调用的实现,而对一些协议(如udp、icmp、tcp协议),则需要特定的处理函数,这些处理函数的定义即使用struct proto结构体进行定义,该结构体的定义如下:
include/net/sock.h
/* Networking protocol blocks we attach to sockets.
* socket layer -> transport layer interface
*/
struct proto {
void (*close)(struct sock *sk,
long timeout);
int (*pre_connect)(struct sock *sk,
struct sockaddr *uaddr,
int addr_len);
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,
bool kern);
int (*ioctl)(struct sock *sk, int cmd,
int *karg);
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, sockptr_t optval,
unsigned int optlen);
int (*getsockopt)(struct sock *sk, int level,
int optname, char __user *optval,
int __user *option);
void (*keepalive)(struct sock *sk, int valbool);
#ifdef CONFIG_COMPAT
int (*compat_ioctl)(struct sock *sk,
unsigned int cmd, unsigned long arg);
#endif
int (*sendmsg)(struct sock *sk, struct msghdr *msg,
size_t len);
int (*recvmsg)(struct sock *sk, struct msghdr *msg,
size_t len, int flags, int *addr_len);
void (*splice_eof)(struct socket *sock);
int (*bind)(struct sock *sk,
struct sockaddr *addr, int addr_len);
int (*bind_add)(struct sock *sk,
struct sockaddr *addr, int addr_len);
int (*backlog_rcv) (struct sock *sk,
struct sk_buff *skb);
bool (*bpf_bypass_getsockopt)(int level,
int optname);
void (*release_cb)(struct sock *sk);
/* Keeping track of sk's, looking them up, and port selection methods. */
int (*hash)(struct sock *sk);
void (*unhash)(struct sock *sk);
void (*rehash)(struct sock *sk);
int (*get_port)(struct sock *sk, unsigned short snum);
void (*put_port)(struct sock *sk);
#ifdef CONFIG_BPF_SYSCALL
int (*psock_update_sk_prot)(struct sock *sk,
struct sk_psock *psock,
bool restore);
#endif
/* Keeping track of sockets in use */
#ifdef CONFIG_PROC_FS
unsigned int inuse_idx;
#endif
#if IS_ENABLED(CONFIG_MPTCP)
int (*forward_alloc_get)(const struct sock *sk);
#endif
bool (*stream_memory_free)(const struct sock *sk, int wake);
bool (*sock_is_readable)(struct sock *sk);
/* Memory pressure */
void (*enter_memory_pressure)(struct sock *sk);
void (*leave_memory_pressure)(struct sock *sk);
atomic_long_t *memory_allocated; /* Current allocated memory. */
int __percpu *per_cpu_fw_alloc;
struct percpu_counter *sockets_allocated; /* Current number of sockets. */
/*
* Pressure flag: try to collapse.
* Technical note: it is used by multiple contexts non atomically.
* Make sure to use READ_ONCE()/WRITE_ONCE() for all reads/writes.
* All the __sk_mem_schedule() is of this nature: accounting
* is strict, actions are advisory and have some latency.
*/
unsigned long *memory_pressure;
long *sysctl_mem;
int *sysctl_wmem;
int *sysctl_rmem;
u32 sysctl_wmem_offset;
u32 sysctl_rmem_offset;
int max_header;
bool no_autobind;
struct kmem_cache *slab;
unsigned int obj_size;
unsigned int ipv6_pinfo_offset;
slab_flags_t slab_flags;
unsigned int useroffset; /* Usercopy region offset */
unsigned int usersize; /* Usercopy region size */
unsigned int __percpu *orphan_count;
struct request_sock_ops *rsk_prot;
struct timewait_sock_ops *twsk_prot;
union {
struct inet_hashinfo *hashinfo;
struct udp_table *udp_table;
struct raw_hashinfo *raw_hash;
struct smc_hashinfo *smc_hash;
} h;
struct module *owner;
char name[32];
struct list_head node;
int (*diag_destroy)(struct sock *sk, int err);
} __randomize_layout;
八、结构体之间的关联
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类型的变量。

3

被折叠的 条评论
为什么被折叠?



