proto socket 关联结构:

    {
.type = SOCK_STREAM,
.protocol = IPPROTO_TCP,
.prot = &tcp_prot,
.ops = &inet_stream_ops,
.flags = INET_PROTOSW_PERMANENT |
INET_PROTOSW_ICSK,
}, {
.type = SOCK_DGRAM,
.protocol = IPPROTO_UDP,
.prot = &udp_prot,
.ops = &inet_dgram_ops,
.flags = INET_PROTOSW_PERMANENT,
}, {
const struct proto_ops inet_stream_ops = {
.family = PF_INET,
.owner = THIS_MODULE,
.release = inet_release,
.bind = inet_bind,
.connect = inet_stream_connect,
.socketpair = sock_no_socketpair,
.accept = inet_accept,
.getname = inet_getname,
.poll = tcp_poll,
.ioctl = inet_ioctl,
.listen = inet_listen,
.shutdown = inet_shutdown,
.setsockopt = sock_common_setsockopt,
.getsockopt = sock_common_getsockopt,
.sendmsg = inet_sendmsg,
.recvmsg = inet_recvmsg,
.mmap = sock_no_mmap,
.sendpage = inet_sendpage,
.splice_read = tcp_splice_read,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_sock_common_setsockopt,
.compat_getsockopt = compat_sock_common_getsockopt,
.compat_ioctl = inet_compat_ioctl,
#endif
}; struct proto tcp_prot = {
.name = "TCP",
.owner = THIS_MODULE,
.close = tcp_close,
.connect = tcp_v4_connect,
.disconnect = tcp_disconnect,
.accept = inet_csk_accept,
.ioctl = tcp_ioctl,
.init = tcp_v4_init_sock,
.destroy = tcp_v4_destroy_sock,
.shutdown = tcp_shutdown,
.setsockopt = tcp_setsockopt,
.getsockopt = tcp_getsockopt,
.recvmsg = tcp_recvmsg,
.sendmsg = tcp_sendmsg,
.sendpage = tcp_sendpage,
.backlog_rcv = tcp_v4_do_rcv,
.release_cb = tcp_release_cb,
.hash = inet_hash,
.unhash = inet_unhash,
.get_port = inet_csk_get_port,
.enter_memory_pressure = tcp_enter_memory_pressure,
.stream_memory_free = tcp_stream_memory_free,
.sockets_allocated = &tcp_sockets_allocated,
.orphan_count = &tcp_orphan_count,
.memory_allocated = &tcp_memory_allocated,
.memory_pressure = &tcp_memory_pressure,
.sysctl_mem = sysctl_tcp_mem,
.sysctl_wmem = sysctl_tcp_wmem,
.sysctl_rmem = sysctl_tcp_rmem,
.max_header = MAX_TCP_HEADER,
.obj_size = sizeof(struct tcp_sock),
.slab_flags = SLAB_DESTROY_BY_RCU,
.twsk_prot = &tcp_timewait_sock_ops,
.rsk_prot = &tcp_request_sock_ops,
.h.hashinfo = &tcp_hashinfo,
.no_autobind = true,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_tcp_setsockopt,
.compat_getsockopt = compat_tcp_getsockopt,
#endif
.diag_destroy = tcp_abort,
};

SOCKT 结构:

/**
* struct sock_common - minimal network layer representation of sockets
* @skc_daddr: Foreign IPv4 addr
* @skc_rcv_saddr: Bound local IPv4 addr
* @skc_hash: hash value used with various protocol lookup tables
* @skc_u16hashes: two u16 hash values used by UDP lookup tables
* @skc_dport: placeholder for inet_dport/tw_dport
* @skc_num: placeholder for inet_num/tw_num
* @skc_family: network address family
* @skc_state: Connection state
* @skc_reuse: %SO_REUSEADDR setting
* @skc_reuseport: %SO_REUSEPORT setting
* @skc_bound_dev_if: bound device index if != 0
* @skc_bind_node: bind hash linkage for various protocol lookup tables
* @skc_portaddr_node: second hash linkage for UDP/UDP-Lite protocol
* @skc_prot: protocol handlers inside a network family
* @skc_net: reference to the network namespace of this socket
* @skc_node: main hash linkage for various protocol lookup tables
* @skc_nulls_node: main hash linkage for TCP/UDP/UDP-Lite protocol
* @skc_tx_queue_mapping: tx queue number for this connection
* @skc_flags: place holder for sk_flags
* %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE,
* %SO_OOBINLINE settings, %SO_TIMESTAMPING settings
* @skc_incoming_cpu: record/match cpu processing incoming packets
* @skc_refcnt: reference count
*
* This is the minimal network layer representation of sockets, the header
* for struct sock and struct inet_timewait_sock.
*/
/*
* 该结构是传输控制块信息的最小集合,由sock和inet_timewait_sock结构
* 前面相同部分单独构成,因此只用来构成这两种结构
*/
//tcp_timewait_sock包含inet_timewait_sock,inet_timewait_sock包含sock_common
/* struct sock里面包含struct sock_common
以tcp为例,struct tcp_sock包含struct inet_connection_sock,inet_connection_sock包含 struct inet_sock,struct inet_sock包含struct sock, struct sock后面是 struct sock_common。所以在struct socket里面的sk指向的开辟空间大小是sizeof(struct tcp_sock)
以udp为例,struct udp_sock包含struct inet_connection_sock inet_connection_sock包含struct inet_sock,struct inet_sock包含struct sock, struct sock后面是 struct sock_common。。所以在struct socket里面的sk指向的开辟空间大小是sizeof(struct udp_sock)
以raw为例,struct raw_sock包含struct inet_connection_sock inet_connection_sock包含struct inet_sock,struct inet_sock包含struct sock, struct sock后面是 struct sock_common。。所以在struct socket里面的sk指向的开辟空间大小是sizeof(struct raw_sock)
//tcp_timewait_sock包含inet_timewait_sock,inet_timewait_sock包含sock_common
tcp_request_sock包含inet_request_sock,inet_request_sock包含request_sock*/
//sock_common是传输控制块信息最小集合 struct sock是比较通用的网络层描述块,与具体的协议族无关,他描述个各个不同协议族传输层的公共信息
struct sock_common {
/* skc_daddr and skc_rcv_saddr must be grouped on a 8 bytes aligned
* address on 64bit arches : cf INET_MATCH()
*/
/* @skc_daddr: Foreign IPv4 addr
@skc_rcv_saddr: Bound local IPv4 addr */
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;
/** 等同于TCP的状态 见TCPF_ESTABLISHED*/
volatile unsigned char skc_state;
/*
* 是否可以重用地址和端口 在SO_REUSEADDR中设置,linxu系统中设置地址可重用,端口也可以重用
端口复用是有条件的,就是sk如果传输控制块允许复用并且不是监听状态sk->sk_state != TCP_LISTEN,见inet_csk_get_port
*/
unsigned char skc_reuse:4;
unsigned char skc_reuseport:1;
unsigned char skc_ipv6only:1;
unsigned char skc_net_refcnt:1;
/* 如果不为0,即为输出报文的网络设备索引号 */
int skc_bound_dev_if;//通过应用程序的setsockopt里面的选项设置
/*
* 已绑定端口的传输控制模块利用该字段插入到与之绑定
* 端口信息结构为头结点的链表中。释放端口时,会从中
* 删除。仅用于基于连接的传输控制块,如TCP
*inet_bind_bucket加入到的sk->sk_bind_node中,见inet_bind_hash
struct sock被添加到inet_bind_bucket结构的owners链表中(inet_bind_hash),然后该inet_bind_bucket通过node节点加入到tcp_hashinfo中
*/
union {
struct hlist_node skc_bind_node;
struct hlist_node skc_portaddr_node;//通过函数 ip4_datagram_connect中的udp_v4_rehash添加把udp协议的struct sock添加到udp_table,
};
/* Networking protocol blocks we attach to sockets.
* socket layer -> transport layer interface
*/
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: */
/*
* TCP维护一个所有TCP传输控制块的散列表tcp_hashinfo,
* 而skc_node用来将所属TCP传输控制块链接到该散列表,
udp的hashinfo为udp_table
*/
//udp没有加入到这里面任何一个list中 本段为服务器端的时候tcp和raw在listen的时候
//调用inet_csk_listen_start把struct sock添加到对应协议的struct proto对应的h成员(hashinfo)中
union {
struct hlist_node skc_node;//raw通过raw_hash_sk把sk加入到raw_hashinfo的ht
struct hlist_nulls_node skc_nulls_node;//tcp通过inet_hash把sk->skc_nulls_node加入到tcp_hashinfo结构中的listening_hash
};
int skc_tx_queue_mapping;
union {
int skc_incoming_cpu;
u32 skc_rcv_wnd;
u32 skc_tw_rcv_nxt; /* struct tcp_timewait_sock */
};
/*
* 引用计数,当引用计数为0时才能被释放
*/
atomic_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 sock - network layer representation of sockets
* @__sk_common: shared layout with inet_timewait_sock
* @sk_shutdown: mask of %SEND_SHUTDOWN and/or %RCV_SHUTDOWN
* @sk_userlocks: %SO_SNDBUF and %SO_RCVBUF settings
* @sk_lock: synchronizer
* @sk_rcvbuf: size of receive buffer in bytes
* @sk_wq: sock wait queue and async head
* @sk_rx_dst: receive input route used by early demux
* @sk_dst_cache: destination cache
* @sk_policy: flow policy
* @sk_receive_queue: incoming packets
* @sk_wmem_alloc: transmit queue bytes committed
* @sk_write_queue: Packet sending queue
* @sk_omem_alloc: "o" is "option" or "other"
* @sk_wmem_queued: persistent queue size
* @sk_forward_alloc: space allocated forward
* @sk_napi_id: id of the last napi context to receive data for sk
* @sk_ll_usec: usecs to busypoll when there is no data
* @sk_allocation: allocation mode
* @sk_pacing_rate: Pacing rate (if supported by transport/packet scheduler)
* @sk_max_pacing_rate: Maximum pacing rate (%SO_MAX_PACING_RATE)
* @sk_sndbuf: size of send buffer in bytes
* @sk_no_check_tx: %SO_NO_CHECK setting, set checksum in TX packets
* @sk_no_check_rx: allow zero checksum in RX packets
* @sk_route_caps: route capabilities (e.g. %NETIF_F_TSO)
* @sk_route_nocaps: forbidden route capabilities (e.g NETIF_F_GSO_MASK)
* @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4)
* @sk_gso_max_size: Maximum GSO segment size to build
* @sk_gso_max_segs: Maximum number of GSO segments
* @sk_lingertime: %SO_LINGER l_linger setting
* @sk_backlog: always used with the per-socket spinlock held
* @sk_callback_lock: used with the callbacks in the end of this struct
* @sk_error_queue: rarely used
* @sk_prot_creator: sk_prot of original sock creator (see ipv6_setsockopt,
* IPV6_ADDRFORM for instance)
* @sk_err: last error
* @sk_err_soft: errors that don't cause failure but are the cause of a
* persistent failure not just 'timed out'
* @sk_drops: raw/udp drops counter
* @sk_ack_backlog: current listen backlog
* @sk_max_ack_backlog: listen backlog set in listen()
* @sk_priority: %SO_PRIORITY setting
* @sk_type: socket type (%SOCK_STREAM, etc)
* @sk_protocol: which protocol this socket belongs in this network family
* @sk_peer_pid: &struct pid for this socket's peer
* @sk_peer_cred: %SO_PEERCRED setting
* @sk_rcvlowat: %SO_RCVLOWAT setting
* @sk_rcvtimeo: %SO_RCVTIMEO setting
* @sk_sndtimeo: %SO_SNDTIMEO setting
* @sk_txhash: computed flow hash for use on transmit
* @sk_filter: socket filtering instructions
* @sk_timer: sock cleanup timer
* @sk_stamp: time stamp of last packet received
* @sk_tsflags: SO_TIMESTAMPING socket options
* @sk_tskey: counter to disambiguate concurrent tstamp requests
* @sk_socket: Identd and reporting IO signals
* @sk_user_data: RPC layer private data
* @sk_frag: cached page frag
* @sk_peek_off: current peek_offset value
* @sk_send_head: front of stuff to transmit
* @sk_security: used by security modules
* @sk_mark: generic packet mark
* @sk_cgrp_data: cgroup data for this cgroup
* @sk_memcg: this socket's memory cgroup association
* @sk_write_pending: a write to stream socket waits to start
* @sk_state_change: callback to indicate change in the state of the sock
* @sk_data_ready: callback to indicate there is data to be processed
* @sk_write_space: callback to indicate there is bf sending space available
* @sk_error_report: callback to indicate errors (e.g. %MSG_ERRQUEUE)
* @sk_backlog_rcv: callback to process the backlog
* @sk_destruct: called at sock freeing time, i.e. when all refcnt == 0
* @sk_reuseport_cb: reuseport group container
*/
/*struct sock是与具体传输层协议相关的套接字,所有内核的操作都基于这个套接字。
//传输控制块 struct socket里面的struct sock指向了这里
//在inet_create中为该结构体分配空间并赋初值。
/*套接字中本段和对端的相关信息都放在inet_sock中,可以保证和协议无关,各种协议都用该结构存储本地地址端口和对端地址端口已经连接状态等
以tcp为例,struct tcp_sock包含struct inet_connection_sock,inet_connection_sock包含 struct inet_sock,struct inet_sock包含struct sock。
所以在struct socket里面的sk指向的开辟空间大小是sizeof(struct tcp_sock)
以udp为例,struct udp_sock包含struct inet_connection_sock inet_connection_sock包含struct inet_sock,struct inet_sock包含struct sock。
所以在struct socket里面的sk指向的开辟空间大小是sizeof(struct udp_sock)
以raw为例,struct raw_sock包含struct inet_connection_sock inet_connection_sock包含struct inet_sock,struct inet_sock包含struct sock。
所以在struct socket里面的sk指向的开辟空间大小是sizeof(struct raw_sock) struct sock里面包含struct sock_common
/*以tcp为例,struct tcp_sock包含struct inet_connection_sock,inet_connection_sock包含 struct inet_sock,struct inet_sock包含struct sock, struct sock后面是 struct sock_common。所以在struct socket里面的sk指向的开辟空间大小是sizeof(struct tcp_sock)
以udp为例,struct udp_sock包含struct inet_connection_sock inet_connection_sock包含struct inet_sock,struct inet_sock包含struct sock, struct sock后面是 struct sock_common。。所以在struct socket里面的sk指向的开辟空间大小是sizeof(struct udp_sock)
以raw为例,struct raw_sock包含struct inet_connection_sock inet_connection_sock包含struct inet_sock,struct inet_sock包含struct sock, struct sock后面是 struct sock_common。。所以在struct socket里面的sk指向的开辟空间大小是sizeof(struct raw_sock)
//tcp_timewait_sock包含inet_timewait_sock,inet_timewait_sock包含sock_common
tcp_request_sock包含inet_request_sock,inet_request_sock包含request_sock tcp_sock->inet_connection_sock->inet_sock->sock(socket里面的sk指向sock)*/
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//raw通过raw_hash_sk sk->sk_node加入到raw_hashinfo的ht,相当于struct sock连接到了raw_hashinfo中
#define sk_nulls_node __sk_common.skc_nulls_node//tcp通过inet_hash把sk->skc_nulls_node加入到tcp_hashinfo结构中的listening_hash。见__sk_nulls_add_node_rcu
#define sk_refcnt __sk_common.skc_refcnt
#define sk_tx_queue_mapping __sk_common.skc_tx_queue_mapping #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
//////sk_flags取值为sock_flags, 状态装换图为前面的sk_state,取值为TCP_SYN_RECV等 sk_state在tcp_set_state中赋值
#define sk_state __sk_common.skc_state //创建sk的时候,默认为TCP_CLOSE sock_init_data
#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
//客户端tcp在conncet的时候把sk通过inet_bind_bucket加入到tcp_hashinfo中 inet_bind_bucket也被添加到inet_connection_sock中的icsk_bind_hash
//参考 sk_add_bind_node
#define sk_bind_node __sk_common.skc_bind_node
/* 指向网络接口层的指针,如果是TCP套接字,为tcp_prot
* 如果是UDP套接字为udp_prot。raw_prot
* */
#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
/*
* 标志位,可能的取值参见枚举类型sock_flags.
* 判断某个标志是否设置调用sock_flag函数来
* 判断,而不是直接使用位操作。
*/
#define sk_flags __sk_common.skc_flags
#define sk_rxhash __sk_common.skc_rxhash
/*
* 同步锁,其中包括了两种锁:一是用于用户进程读取数据
* 和网络层向传输层传递数据之间的同步锁;二是控制Linux
* 下半部访问本传输控制块的同步锁,以免多个下半部同
* 时访问本传输控制块
*/
socket_lock_t sk_lock;
/*
* 接收队列,等待用户进程读取。TCP比较特别,
* 当接收到的数据不能直接复制到用户空间时才会
* 缓存在此
*/
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.
*/
/*
* 后备接收队列,目前只用于TCP.传输控制块被上锁后(如应用层
* 读取数据时),当有新的报文传递到传输控制块时,只能把报文
* 放到后备接受队列中,之后有用户进程读取TCP数据时,再从
* 该队列中取出复制到用户空间中.
* 一旦用户进程解锁传输控制块,就会立即处理
* 后备队列,将TCP段处理之后添加到接收队列中。
*/
struct {
atomic_t rmem_alloc;
int len;
struct sk_buff *head;
struct sk_buff *tail;
} sk_backlog;
////这个只针对接收数据,发送数据对应的是sk_rmem_alloc,
//阅读函数__sk_mem_schedule可以了解proto的内存情况判断方法
//表示接收队列中所有skb的总长度,在sock_queue_rcv_skb函数的skb_set_owner_r中增加
#define sk_rmem_alloc sk_backlog.rmem_alloc
/*
* 预分配缓存长度,这只是一个标识,目前 只用于TCP。
* 当分配的缓存小于该值时,分配必然成功,否则需要
* 重新确认分配的缓存是否有效。参见__sk_mem_schedule().
* 在sk_clone()中,sk_forward_alloc被初始化为0.
*
* update:sk_forward_alloc表示预分配长度。当我们第一次要为
* 发送缓冲队列分配一个struct sk_buff时,我们并不是直接
* 分配需要的内存大小,而是会以内存页为单位进行
* 预分配(此时并不是真的分配内存)。当把这个新分配
* 成功的struct sk_buff放入缓冲队列sk_write_queue后,从sk_forward_alloc
* 中减去该sk_buff的truesize值。第二次分配struct sk_buff时,只要再
* 从sk_forward_alloc中减去新的sk_buff的truesize即可,如果sk_forward_alloc
* 已经小于当前的truesize,则将其再加上一个页的整数倍值,
* 并累加如tcp_memory_allocated。 * 也就是说,通过sk_forward_alloc使全局变量tcp_memory_allocated保存
* 当前tcp协议总的缓冲区分配内存的大小,并且该大小是
* 页边界对齐的。
*/ //这是本sock的缓存大小,如果要看整个tcp sock的缓存大小,要参考tcp_prot中的memory_allocated成员
////阅读函数__sk_mem_schedule可以了解proto的内存情况判断方法 。 注意和上面的sk_wmem_alloc的区别
int sk_forward_alloc;
//skb_entail中的sk_mem_charge里面会对新分配的SKB空间做一次减法,表示预分配缓存空间少了 在真正分配空间之前需要比较这个值,看内存空间释放使用达到限度
//在应用层send_msg的时候,会在函数__sk_mem_schedule中开辟空间,为sk_forward_alloc增加amt * SK_MEM_QUANTUM;如果发送的数据长度小于该值,肯定超过,若果大于该值
//则会增加sk_forward_alloc拥有的内存空间,见sk_wmem_schedule
//该变量表示的是当前sk的可用空间,预分配后的可用空间。例如应用层send,在内核分配ksb的时候空间做减法,表示可用空间少了这部分长度,当发送出去释放skb后,做加法,这时表示可用空间有多了 __u32 sk_txhash;
#ifdef CONFIG_NET_RX_BUSY_POLL
unsigned int sk_napi_id;
unsigned int sk_ll_usec;
#endif
atomic_t sk_drops;
/* 接收缓冲区大小的上限,默认值是sysctl_rmem_default(sock_init_data),即32767, 也就是IP首部16位长度(最大65535)的一半*/
//当sock接收到一个包的时候,会在sock_queue_rcv_skb中判断当前队列中已有的skb占用的buffer和这个新来的buff之后是否超过了sk_rcvbuf
int sk_rcvbuf;
/*
* 套接字过滤器。在传输层对输入的数据包通过BPF过滤代码进行过滤,
* 只对设置了套接字过滤器的进程有效。
*/
struct sk_filter __rcu *sk_filter;
/*
* 进程等待队列。进程等待连接、等待输出缓冲区、等待
* 读数据时,都会将进程暂存到此队列中。这个成员最初
* 是在sk_clone()中初始化为NULL,该成员实际存储的socket结构
* 中的wait成员,这个操作在sock_init_data()中完成。 有的版本这里直接是wait, 唤醒该队列上的进程函数是sock_def_wakeup
*/
union {
struct socket_wq __rcu *sk_wq;
struct socket_wq *sk_wq_raw;
};
#ifdef CONFIG_XFRM
struct xfrm_policy __rcu *sk_policy[2];
#endif
struct dst_entry *sk_rx_dst;
/*
* 目的路由项缓存,一般都是在创建传输控制块发送
* 数据报文时,发现未设置该字段才从路由表或路由
* 缓存中查询到相应的路由项来设置新字段,这样可以
* 加速数据的输出,后续数据的输出不必再查询目的
* 路由。某些情况下会刷新此目的路由缓存,比如断开
* 连接、重新进行了连接、TCP重传、重新绑定端口
* 等操作
*/
struct dst_entry __rcu *sk_dst_cache;
/* Note: 32bit hole on 64bit arches */
/* 所在传输控制块中,为发送而分配的所有SKB数据区的总长度。这个成员和
* sk_wmem_queued不同,所有因为发送而分配的SKB数据区的内存都会统计到
* sk_wmem_alloc成员中。例如,在tcp_transmit_skb()中会克隆发送队列中的
* SKB,克隆出来的SKB所占的内存会统计到sk_wmem_alloc,而不是sk_wmem_queued中。
*
* 释放sock结构时,会先将sk_wmem_alloc成员减1,如果为0,说明没有待
* 发送的数据,才会真正释放。所以这里要先将其初始化为1 ,参见
* sk_alloc()。
* 该成员在skb_set_owner_w()中会更新。
*///通过阅读函数sock_alloc_send_pskb可以理解改变量的作用 每开辟一个SKB的时候当应用程序通过套接口传数据的时候,最终会把数据传输到SKB中,然后把数据长度+header长度的值赋值给该变量中,表示当前该套接字中未发送的数据为多少
// 见sock_alloc_send_pskb中的skb_set_owner_w 在开辟空间前要和sk_sndbuf做比较
//在sk_alloc的时候初始化设置为1,然后在skb_set_owner_w加上SKB长度,当SKB发送出去后,在减去该SKB的长度,所以这个值当数据发送后其值始终是1,不会执行sock_wfree
//这个为发送队列(包括克隆的)分配的实际空间,sk_forward_alloc是提前预分配的,实际上并没有分片空间,只是说先确定下来可以用这么多空间,就是后面分片空间的时候最多可以分片这么多空间。
atomic_t sk_wmem_alloc;//这个只针对发送数据,接收数据对应的是sk_rmem_alloc,
/*
* 分配辅助缓冲区的上限,辅助数据包括进行设置选项、
* 设置过滤时分配到的内存和组播设置等
*/
atomic_t sk_omem_alloc;
/*
* 发送缓冲区长度的上限,发送队列中报文数据总长度不能
* 超过该值.默认值是sysctl_wmem_default,即32767。在通过setsockops设置时,其值最大为sysctl_wmem_max的两倍
*/ //发送缓冲区会根据该proto使用的内存情况,进行调整,见__sk_mem_schedule中的sk_stream_moderate_sndbuf 并能通过tcp_rmem调整。
int sk_sndbuf;
/*
* 发送队列,在TCP中,此队列同时也是重传队列,
* 在sk_send_head之前为重传队列,之后为发送
* 队列,参见sk_send_head
*/ //这上面存的是发送SKB链表,即使调用了dev_queue_xmit后,该SKB海在该链表上面,知道收到对方ack。
struct sk_buff_head sk_write_queue; /*
* Because of non atomicity rules, all
* changes are protected by socket lock.
*/
kmemcheck_bitfield_begin(flags);
unsigned int sk_padding : 2,
sk_no_check_tx : 1,
sk_no_check_rx : 1,
sk_userlocks : 4,
sk_protocol : 8,
sk_type : 16;
#define SK_PROTOCOL_MAX U8_MAX
kmemcheck_bitfield_end(flags);
/* 发送队列中所有报文数据的总长度,目前只用于TCP 。这里
* 统计的是发送队列中所有报文的长度,不包括因为发送而克隆
* 出来的SKB占用的内存。是真正的占用空间的发送队列数据长度。见skb_entail
* */
int sk_wmem_queued;//skb_entail中会赋值
gfp_t sk_allocation; /*
* 内存分配方式,参见include\linux\gfp.h。值为__GFP_DMA等
*/
u32 sk_pacing_rate; /* bytes per second */
u32 sk_max_pacing_rate;
/*
* 目的路由网络设备的特性,在sk_setup_caps()中根据
* net_device结构的features成员设置
*/ //参考//如果网口设备dev设置了dev->features |= NETIF_F_TSO,则支持TSO 参考e1000网卡的这里enic_ethtool_ops
netdev_features_t sk_route_caps;
netdev_features_t sk_route_nocaps;
/*
* 传输层支持的GSO类型,如SKB_GSO_TCPV4等 默认该值为SKB_GSO_TCPV4
*/
int sk_gso_type;//tcp_v4_connect
/*
* 这个成员在sk_setup_caps()中初始化,表示最大TCP分段的大小。
* 注意,这个大小包括IP首部长度长度、IP选项长度及TCP首部和选项,
* 另外还要减1(这个减1不知道是为什么。。。。)
*/
unsigned int sk_gso_max_size;
u16 sk_gso_max_segs;
/*
* 标识接收缓存下限值
*/
int sk_rcvlowat;
/* 关闭套接字前发送剩余数据的时间*/
unsigned long sk_lingertime;//setsockops中设置 SO_LINGER
/*
* 错误链表,存放详细的出错信息。应用程序通过setsockopt
* 系统调用设置IP_RECVERR选项,即需获取详细出错信息。当
* 有错误发生时,可通过recvmsg(),参数flags为MSG_ERRQUEUE
* 来获取详细的出错信息
* update:
* sk_error_queue用于保存错误消息,当ICMP接收到差错消息或者
* UDP套接字和RAW套接字输出报文出错时,会产生描述错误信息的
* SKB添加到该队列上。应用程序为能通过系统调用获取详细的
* 错误消息,需要设置IP_RECVERR套接字选项,之后可通过参数
* flags为MSG_ERRQUEUE的recvmsg系统调用来获取详细的出错
* 信息。
* UDP套接字和RAW套接字在调用recvmsg接收数据时,可以设置
* MSG_ERRQUEUE标志,只从套接字的错误队列上接收错误而不
* 接收其他数据。实现这个功能是通过ip_recv_error()来完成的。
* 在基于连接的套接字上,IP_RECVERR意义则会有所不同。并不
* 保存错误信息到错误队列中,而是立即传递所有收到的错误信息
* 给用户进程。这对于基于短连接的TCP应用是很有用的,因为
* TCP要求快速的错误处理。需要注意的是,TCP没有错误队列,
* MSG_ERRQUEUE对于基于连接的套接字是无效的。
* 错误信息传递给用户进程时,并不将错误信息作为报文的内容传递
* 给用户进程,而是以错误信息块的形式保存在SKB控制块中,
* 通常通过SKB_EXT_ERR来访问SKB控制块中的错误信息块。
* 参见sock_exterr_skb结构。
*/
struct sk_buff_head sk_error_queue;
/*
* 原始网络协议块指针。因为传输控制块中的另一个网络
* 协议块指针sk_prot在IPv6的IPV6_ADDRFORM套接字选项
* 设置时被修改
*/
struct proto *sk_prot_creator;
/*
* 确保传输控制块中一些成员同步访问的锁。因为有些成员在软
* 中断中被访问,存在异步访问的问题
*
*/
rwlock_t sk_callback_lock;
/*
* 记录当前传输层中发生的最后一次致命错误的错误码,但
* 应用层读取后会自动恢复为初始正常状态.
* 错误码的设置是由tcp_v4_err()函数完成的。
*/
int sk_err,
/*
* 用于记录非致命性错误,或者用作在传输控制块被
* 锁定时记录错误的后备成员
*/
sk_err_soft;
/* 当前已建立的连接数 */ //表示套接口上可以排队等待连接的连接数门限值
//在三次握手成功的第三步ACK成功后,会从listen_sock里面的syn_table hash中取出,让后加入到request_sock_queue的rskq_accept_head中,
//同时增加已连接成功值,当应用程序调用accept的时候,会从里面取出这个已连接信息,然后再减小改制,同时释放这个request_sock
//这个是从半连接队列取出request_sock后加入到已连接队列中的request_sock个数,sk_ack_backlog是已经完成了三次握手,但是还没有被accept系统调用处理的连接请求数量;sk_max_ack_backlog就是我们经常熟悉的listen的参数。
//建立连接的过程中加1,在reqsk_queue_add中赋值 减1在reqsk_queue_get_child
u32 sk_ack_backlog;
//在inet_listen赋值,为listen的第三个参数向上取得的2次密reqsk_queue_alloc,这个值和半连接里面的listen_sock中的nr_table_entries相
u32 sk_max_ack_backlog;
__u32 sk_priority;//SKB->priority就是用的该字段
__u32 sk_mark;
struct pid *sk_peer_pid;
const struct cred *sk_peer_cred;/* 返回连接至该套接字的外部进程的身份验证,目前主要用于PF_UNIX协议族*/
/*
* 套接字层接收超时,初始值为MAX_SCHEDULE_TIMEOUT。
* 可以通过套接字选项SO_RCVTIMEO来设置接收的超时时间。 sock_init_data设置为无限大,也就是accept的时候默认是无限阻塞的,见inet_csk_accept
* 如果想设置为非阻塞,可以通过SO_RCVTIMEO参数设置
*/
long sk_rcvtimeo;
/*
* 套接字层发送超时,初始值为MAX_SCHEDULE_TIMEOUT。
* 可以通过套接字选项SO_SNDTIMEO来设置发送的超时时间。 connect的时候判断是否connect超时用的就是这个值 使用该值的地方在sock_sndtimeo
*/
long sk_sndtimeo;
/*
* 通过TCP的不同状态,来实现连接定时器、FIN_WAIT_2定时器(该定时器在TCP四次挥手过程中结束,见tcp_rcv_state_process)以及
* TCP保活定时器,在tcp_keepalive_timer中实现
* 定时器处理函数为tcp_keepalive_timer(),参见tcp_v4_init_sock()
* 和tcp_init_xmit_timers()。
*/
struct timer_list sk_timer;//inet_csk_init_xmit_timers sock_init_data
/*
* 在未启用SOCK_RCVTSTAMP套接字选项时,记录报文接收数据到
* 应用层的时间戳。在启用SOCK_RCVTSTAMP套接字选项时,接收
* 数据到应用层的时间戳记录在SKB的tstamp中
*/
ktime_t sk_stamp;
u16 sk_tsflags;
u8 sk_shutdown;
u32 sk_tskey;
struct socket *sk_socket; /* 指向对应套接字的指针 */
void *sk_user_data;
struct page_frag sk_frag;
/*
* 指向sk_write_queue队列中第一个未发送的结点,如果sk_send_head
* 为空则表示发送队列是空的,发送队列上的报文已全部发送。
*/
struct sk_buff *sk_send_head; //表示sk_write_queue队列中还未调用dev_queue_xmit的最前面一个SKB的地方
/*
* 表示数据尾端在最后一页分片内的页内偏移,
* 新的数据可以直接从这个位置复制到该分片中
*/ //在tcp_sendmsg中开辟空间后,并复制,见里面的TCP_OFF(sk) = off + copy;
__s32 sk_peek_off;
/* 标识有数据即将写入套接口,
* 也就是有写数据的请求*/
int sk_write_pending;
#ifdef CONFIG_SECURITY
void *sk_security;
#endif
struct sock_cgroup_data sk_cgrp_data;
struct mem_cgroup *sk_memcg;
/*
* 当传输控制块的状态发生变化时,唤醒哪些等待本套接字的进程。
* 在创建套接字时初始化,IPv4中为sock_def_wakeup() 通常当传输 状态发生变化时调用
*/
void (*sk_state_change)(struct sock *sk);
/*
* 当有数据到达接收处理时,唤醒或发送信号通知准备读本套接字的
* 进程。在创建套接字时被初始化,IPv4中为sock_def_readable()。如果
* 是netlink套接字,则为netlink_data_ready()。 通常当传输控制块接收到数据包,存在可读的数据之后被调用
*///内核创建netlink sock的时候,对应的是netlink_kernel_create->netlink_data_ready
void (*sk_data_ready)(struct sock *sk);
void (*sk_write_space)(struct sock *sk);
void (*sk_error_report)(struct sock *sk);
/*
* 用于TCP和PPPoE中。在TCP中,用于接收预备队列和后备队列中的
* TCP段,TCP的sk_backlog_rcv接口为tcp_v4_do_rcv()。如果预备
* 队列中还存在TCP段,则调用tcp_prequeue_process()预处理,在
* 该函数中会回调sk_backlog_rcv()。如果后备队列中还存在TCP段,
* 则调用release_sock()处理,也会回调sk_backlog_rcv()。该函数
* 指针在创建套接字的传输控制块时由传输层backlog_rcv接口初始化
*/
int (*sk_backlog_rcv)(struct sock *sk,
struct sk_buff *skb);
void (*sk_destruct)(struct sock *sk);
struct sock_reuseport __rcu *sk_reuseport_cb;
struct rcu_head sk_rcu;
}; #define __sk_user_data(sk) ((*((void __rcu **)&(sk)->sk_user_data))) #define rcu_dereference_sk_user_data(sk) rcu_dereference(__sk_user_data((sk)))
#define rcu_assign_sk_user_data(sk, ptr) rcu_assign_pointer(__sk_user_data((sk)), ptr)

Socket 结构体的更多相关文章

  1. 2. socket结构体——表示socket地址

    一.两种通用socket结构体 1. sockaddr struct sockaddr { sa_family_t sa_family; // 地址族 char sa_data[14]; // 存放s ...

  2. struct socket结构体详解

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://weiguozhihui.blog.51cto.com/3060615/15852 ...

  3. java socket传送一个结构体给用C++编写的服务器解析的问题

    另一端是Java写客户端程序,两者之间需要通信.c++/c接收和发送的都是结构体,而Java是直接发送的字节流或者byte 数组.解决方法:c++/c socket 在发送结构体的时候其实发送的也是字 ...

  4. socket编程相关的结构体和字节序转换、IP、PORT转换函数

    注意:结构体之间不能直接进行强制转换, 必须先转换成指针类型才可以进行结构体间的类型转换, 这里需要明确的定义就是什么才叫强制转换. 强制转换是将内存中一段代码以另一种不同类型的方式进行解读, 因此转 ...

  5. C# Socket 入门4 UPD 发送结构体(转)

    今天我们来学 socket  发送结构体 1. 先看要发送的结构体 using System; using System.Collections.Generic; using System.Text; ...

  6. C#.NET和C++结构体Socket通信与数据转换

    最近在用C#做一个项目的时候,Socket发送消息的时候遇到了服务端需要接收C++结构体的二进制数据流,这个时候就需要用C#仿照C++的结 构体做出一个结构来,然后将其转换成二进制流进行发送,之后将响 ...

  7. Linux C Socket编程发送结构体、文件详解及实例

    利用Socket发送文件.结构体.数字等,是在Socket编程中经常需要用到的.由于Socket只能发送字符串,所以可以使用发送字符串的方式发送文件.结构体.数字等等. 本文:http://www.c ...

  8. socket编程——sockaddr_in结构体操作

    sockaddr结构体 sockaddr的缺陷: struct sockaddr 是一个通用地址结构,这是为了统一地址结构的表示方法,统一接口函数,使不同的地址结构可以被bind() , connec ...

  9. C#与C++通过socket传送结构体

    C#服务端: using System; using System.Net.Sockets; using System.Net; using System.IO; using System.Diagn ...

随机推荐

  1. 迅雷bt种子的制作

    BT是目前最热门的下载方式之一,它的全称为"BitTorrent"简称"BT",中文全称"比特流",但很多朋友将它戏称为"变态下载 ...

  2. MeteoInfoLab脚本示例:TRMM 2A12 HDF数据

    TRMM 2A12 HDF数据是卫星观测的SWATH数据(轨道数据),比格点数据处理起来要麻烦一些.数据的经纬度保存在geolocation变量中,需要先将经纬度数据读出来(均为2维数组),然后读取云 ...

  3. day33 Pyhton 常用模块03

    一.正则表达式: 1.元字符 . 匹配除换行符以外的任意字符 \w 匹配字母或数字或下划线 \s 匹配任意的空白符 \d 匹配数字 \n 匹配一个换行符 \t 匹配一个制表符 \b 匹配一个单词的结尾 ...

  4. day30 Pyhton 面向对象 继承.装饰器

    一面向对象相关 谈谈你对面向对象的理解 1.泛泛谈:什么情况下使用面向对象 2.针对面向对象的 封装 继承 多态一个一个的谈 3.根据你工作中的实际情况对应来举例子 封装 1.广义上:普遍的大家认为的 ...

  5. C 语言因为疫情重登最流行编程语言榜第一名!其实它一直都在~

      C 语言时隔五年后再次荣登最流行编程语言榜单第一名. 通过调查得出结论, C 语言再次受欢迎的原因竟然是因为病毒,这听起来愚蠢但确实是事实,嵌入式语言 C 和 C++ 被用于医疗设备中的幅度因为疫 ...

  6. RabbitMQ消息队列总结

    AMQP[高级消息队列协议] 是一个异步消息传递所使用的应用层协议规范(是线路层协议)AMQP 客户端能够无视消息的来源任意发送和接受信息 队列的使用场景: 1.与业务的主要逻辑无关,但又需要执行,就 ...

  7. MySQL关于月份日期的操作

    #获取当前日期 SELECT CURDATE(); #获取本月最后一天 SELECT LAST_DAY(CURDATE()); #获取本月的第一天 SELECT DATE_ADD(CURDATE(), ...

  8. Python入门基础教程-准备工作

    作为一名Python的忠实爱好者,在Python的学习过程中趟过了很多坑.不论是在基础语法.爬虫.可视化的学习,亦或是在数据挖掘的项目开展中,整个过程有痛苦也有收获,有捶胸顿足也有仰天长笑.所以在以后 ...

  9. 初探RT-Thread系统在GD32E103x芯片上的使用,点亮LED灯

    初探RT-Thread系统在GD32E103x芯片上的使用,点亮LED灯 前言 ​ 随着中美贸易战的加剧,很多公司越来越重视使用国产技术的重要性.使用国产技术,一方面可规避国外对技术的封锁造成产品核心 ...

  10. 通过maven创建springboot项目

    1,idea选择创建一个maven项目 2,pom.xml <dependencies> <dependency> <groupId>org.springframe ...