Linux SockFS文件系统分析2

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_INETAF_INET6AF_UNIX 等);
  • 套接字类型(如 SOCK_STREAMSOCK_DGRAM 等);
  • 协议(如 IPPROTO_TCPIPPROTO_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,下图便是这几个结构体的关联。这些结构体的主要关联有:

  1. 将vfs与sockfs进行了关联;
  2. 将socket与具体协议处理接口、具体的socket type处理接口完成了关联操作;
  3. 在af_inet4中,存在struct inet_protosw与socket类型对应的操作接口、具体协议类型对应的操作接口的关联,实现了通过socket即可调用具体协议类型对应处理接口,处理链接创建与删除、数据收发等操作。

二、struct socket分析

结构体socket用于描述一个socket,该结构体的定义如下:

  1. state定义了socket的状态(包括SS_UNCONNECTED、SS_CONNECTING、SS_CONNECTED、SS_DISCONNECTING等);
  1. file表示文件描述符,用于与vfs关联;
  2. struct sock *sk为socket结构体的关键成员变量,后面介绍;
  3. 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协议簇等

相关的定义,该结构体的定义如下:

  1. family用于说明协议簇的类型,目前linux支持的协议簇类型包括AF_UNIX、AF_LOCAL、AF_INET、AF_INET6、AF_NETLINK等;
  2. 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的初始化与操作接口的设置等操作。

该全局变量的作用:

  1. 表示所有在linux系统中已注册的协议簇变量;
  2. 在进行socket创建时,根据传递的协议簇类型,找到对应的协议簇变量,进入相应协议簇变量的处理分支,继续进行socket相关的处理操作。

五、struct inet_protosw分析(inet协议簇)

因本文主要介绍inet相关的实现,因此本处介绍struct inet_protosw结构体,

该结构体用于说明一个协议簇中针对不同协议所支持的协议处理操作以及对应的sock的表征,其主要内容如下:

  1. 其中type用于指示socket的类型,socket的类型包括SOCK_STREAM、SOCK_DGRAM具体的类型在下面有说明;
  2. protocol用于说明协议的类型(包括ip、tcp、udp、icmp、dccp等);
  3. prot用于指向协议相关的处理接口;
  4. 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类型的变量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值