sock skbuf 结构:
阅读原文时间:2023年07月08日阅读:5

/**
* 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;
};

**
* 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 coun
* 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 sk_buff {
/*next和prev这两个域链接相关的sk_buff结构,当报文分段时,原始报文的每个分段
*通过next域链接在一起。
*/
union {
struct {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;

        union {  
            ktime\_t        tstamp;//记录接收或者传输报文的时间戳  
            struct skb\_mstamp skb\_mstamp;  
        };  
    };  
    struct rb\_node    rbnode; /\* used in netem & tcp stack \*/  
};  
struct sock        \*sk;//指向报文(sk\_buff)所属套接字的指针  
/\* 这是指向设备(struct net\_device)的指针,报文通过此设备接收或者发送。  
 \* net\_device记录网络接口(数据链路层)信息以及该设备的相关操作。  
 \*/  
struct net\_device    \*dev;

/\*  
 \* This is the control buffer. It is free to use for every  
 \* layer. Please put your private variables there. If you  
 \* want to keep them across layers you have to do a skb\_clone()  
 \* first. This is owned by whoever has the skb queued ATM.  
 \*/ /\* 该域保存协议相关的控制信息,每个协议层可能独立的使用这些信息。\*/  
char            cb\[48\] \_\_aligned(8);

unsigned long        \_skb\_refdst;  
void            (\*destructor)(struct sk\_buff \*skb);  

#ifdef CONFIG_XFRM
struct sec_path *sp;
#endif
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conntrack *nfct;
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
struct nf_bridge_info *nf_bridge;
#endif
//skb的组成是有sk_buff控制 + 线性数据 + 非线性数据 (skb_shared_info) 组成!
//非线性数据!那么len就是length(线性数据) + length(非线性数据)!!!
unsigned int len,///该域记录sk_buff中数据的总长度
data_len;//只有当sk_buff中有非线性数据时才使用该域
__u16 mac_len,//有二层数据的时候使用
hdr_len;///// 用于clone时,表示clone的skb的头长度

/\* Following fields are \_not\_ copied in \_\_copy\_skb\_header()  
 \* Note that queue\_mapping is here mostly to fill a hole.  
 \*/  
kmemcheck\_bitfield\_begin(flags1);  
\_\_u16            queue\_mapping;  
\_\_u8            cloned:1,//保存当前的skb\_buff是克隆的还是原始数据  
            nohdr:1,//仅仅引用数据区域// nohdr标识payload是否被单独引用,不存在协议首部。  
            fclone:2,//克隆状态  
            peeked:1,  
            head\_frag:1,  
            xmit\_more:1;//有更多的skb在这个队列中  
/\* one bit hole \*/  
kmemcheck\_bitfield\_end(flags1);

/\* fields enclosed in headers\_start/headers\_end are copied  
 \* using a single memcpy() in \_\_copy\_skb\_header()  
 \*/  
/\* private: \*/  
\_\_u32            headers\_start\[0\];  
/\* public: \*/

/* if you move pkt_type around you also must adapt those constants */
#ifdef __BIG_ENDIAN_BITFIELD
#define PKT_TYPE_MAX (7 << 5)
#else
#define PKT_TYPE_MAX 7
#endif
#define PKT_TYPE_OFFSET() offsetof(struct sk_buff, __pkt_type_offset)

\_\_u8            \_\_pkt\_type\_offset\[0\];  
\_\_u8            pkt\_type:3;// 标记帧的类型 根据L2层帧的目的地址进行类型划分。  
\_\_u8            pfmemalloc:1;  
\_\_u8            ignore\_df:1;//允许本地分段  
\_\_u8            nfctinfo:3;

\_\_u8            nf\_trace:1;//netfilter packet trace flag  
\_\_u8            ip\_summed:2;//该域表示驱动是否计算IP校验和  
\_\_u8            ooo\_okay:1;// allow the mapping of a socket to a queue to be changed  
\_\_u8            l4\_hash:1;// indicate hash is a canonical 4-tuple hash over transportport  
\_\_u8            sw\_hash:1;// indicates hash was computed in software stack  
\_\_u8            wifi\_acked\_valid:1;  
\_\_u8            wifi\_acked:1;

\_\_u8            no\_fcs:1;// Request NIC to treat last 4 bytes as Ethernet FCS  
/\* Indicates the inner headers are valid in the skbuff. \*/  
\_\_u8            encapsulation:1;  
\_\_u8            encap\_hdr\_csum:1;  
\_\_u8            csum\_valid:1;  
\_\_u8            csum\_complete\_sw:1;  
\_\_u8            csum\_level:2;  
\_\_u8            csum\_bad:1;

#ifdef CONFIG_IPV6_NDISC_NODETYPE
__u8 ndisc_nodetype:2;
#endif
__u8 ipvs_property:1;
__u8 inner_protocol_type:1;
__u8 remcsum_offload:1;
/* 3 or 5 bit hole */

#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u16 tc_verd; /* traffic control verdict */
#endif
#endif

union {  
    \_\_wsum        csum;//某时刻协议的校验和//检验码,必须包括开始/偏移  
    struct {  
        \_\_u16    csum\_start;  
        \_\_u16    csum\_offset;  
    };  
};  
\_\_u32            priority;//该域保存报文的排队优先级信息,这基于IP头中的TOS域  
int            skb\_iif;// 接受设备的index  
\_\_u32            hash;  
\_\_be16            vlan\_proto;// vlan encapsulation protocol  
\_\_u16            vlan\_tci;// vlan tag control information  

#if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS)
union {
unsigned int napi_id;
unsigned int sender_cpu;
};
#endif
union {
#ifdef CONFIG_NETWORK_SECMARK
__u32 secmark;
#endif
#ifdef CONFIG_NET_SWITCHDEV
__u32 offload_fwd_mark;
#endif
};

union {  
    \_\_u32        mark;  
    \_\_u32        reserved\_tailroom;  
};

union {  
    \_\_be16        inner\_protocol;  
    \_\_u8        inner\_ipproto;  
};

\_\_u16            inner\_transport\_header;  
\_\_u16            inner\_network\_header;  
\_\_u16            inner\_mac\_header;  
\_\_be16            protocol;//协议信息  
\_\_u16            transport\_header;/// 指向四层帧头结构体指针  
\_\_u16            network\_header;// 指向三层IP头结构体指针  
\_\_u16            mac\_header;// 指向二层mac头的头

/\* private: \*/  
\_\_u32            headers\_end\[0\];  
/\* public: \*/

/\* These elements must be at the end, see alloc\_skb() for details.  \*/  
sk\_buff\_data\_t        tail;//该域指向驻留在线性数据区的最后一个字节的数据  

/* 该域指向线性数据区的结尾,与tail不同。驻留在线性数据区中的数据结尾并不是
* 总是在线性数据区的结尾。利用该域可以确保我们没有使用超出可用存储的缓冲区
*/
sk_buff_data_t end;
unsigned char *head,/* 该域指向线性数据区的开始(为sk_buff分配的线性数据区的首字节) */
*data; /* 该域指向驻留在线性数据区的数据的起始位置。驻留在线性数据区中的数据可能并
* 不总是从线性数据区的起始head开始。
*/
/* 该域保存为该缓冲区所分配的总内存。它包括sk_buff结构的大小+分配给该
* sk_buff的数据块的大小。
*/
unsigned int truesize;
atomic_t users;//这是个引用计数,表明了有多少实体引用了这个skb
/*
(1)sk_buff->data_len:只计算分片中数据的长度,即是分片结构体中page指向的数据区长度。
(2)sk_buff->len:表示当前缓冲区中数据块的大小的总长度。它包括主缓冲中?
词莝k_buff结构中指针data指向)的数据区的实际长度(data-tail)和分片中的数据长度。
这个长度在数据包在各层间传输时会改变,因为分片数据长度不变,从L2到L4时,
则len要减去帧头大小和网络头大小;从L4到L2则相反,要加上帧头和网络头大小。
所以:len = (data - tail) + data_len;? ? ? ??
(3)sk_buff->truesize:这是缓冲区的总长度,包括sk_buff结构和数据部分。
如果申请一个len字节的缓冲区,alloc_skb函数会把它初始化成len+sizeof(sk_buff)。
当skb->len变化时,这个变量也会变化。
所以:truesize = len + sizeof(sk_buff) = (data - tail) + data_len + sizeof(sk_buff);

\*/  

};