分析ipvx内存泄漏时 linux tcpip 数据结构

Linux 的 TCP 协议栈用全局的 3 个哈希表(位于 tcp_hashinfo 对象,定义于 net/ipv4/tcp_ipv4.c)来管理全部的 TCP 协议控制块。

  • ehash 负责有名有姓(source IP/source port/destination IP/destination port 俱全)的 socket,其中 e 表示 established。以 C++ 来说,ehash 相当于unordered_map<Key, sock*>,其中 Key 是 struct Key { uint32_t source_ip, destination_ip; uint16_t source_port, destination_port; }; ,而 sock* 可以指向 tcp_request_sock、tcp_sock、tcp_timewait_sock 之一。
  • bhash 负责端口分配,其中 b 表示 bind。以 C++ 来说,bhash 的功能相当于 map<uint16_t, list<tcp_sock*>>,其中的 list 对应 inet_bind_bucket。(更准确的描述是 unordered_map<uint16_t, forward_list<tcp_sock*>>。)
  • listening_hash 负责侦听(listening) socket。
/**
 *    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;// 外部/目的IPV4地址
            __be32    skc_rcv_saddr;// 本地绑定IPV4地址
        };
    };
    union  {
        unsigned int    skc_hash;// 根据协议查找表获取的哈希值
        __u16        skc_u16hashes[2];// 2个16位哈希值,UDP专用
    };
    /* skc_dport && skc_num must be grouped as well */
    union {
        __portpair    skc_portpair;
        struct {
            __be16    skc_dport;// inet_dport占位符
            __u16    skc_num; // inet_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; /*
     * 内存分配方式,参见includelinuxgfp.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)


/** inet_connection_sock - INET connection oriented sock
 *
 * @icsk_accept_queue:       FIFO of established children 
 * @icsk_bind_hash:       Bind node
 * @icsk_timeout:       Timeout
 * @icsk_retransmit_timer: Resend (no ack)
 * @icsk_rto:           Retransmit timeout
 * @icsk_pmtu_cookie       Last pmtu seen by socket
 * @icsk_ca_ops           Pluggable congestion control hook
 * @icsk_af_ops           Operations which are AF_INET{4,6} specific
 * @icsk_ca_state:       Congestion control state
 * @icsk_retransmits:       Number of unrecovered [RTO] timeouts
 * @icsk_pending:       Scheduled timer event
 * @icsk_backoff:       Backoff
 * @icsk_syn_retries:      Number of allowed SYN (or equivalent) retries
 * @icsk_probes_out:       unanswered 0 window probes
 * @icsk_ext_hdr_len:       Network protocol overhead (IP/IPv6 options)
 * @icsk_ack:           Delayed ACK control data
 * @icsk_mtup;           MTU probing control data
 */
struct inet_connection_sock {
    /* inet_sock has to be the first member! */
    struct inet_sock      icsk_inet; //inet_connection_sock  common struct
    struct request_sock_queue icsk_accept_queue;  //tcp newsk 存放新的链接sock 等待accept 读取
    struct inet_bind_bucket      *icsk_bind_hash;//指向与之bind的信息
    unsigned long          icsk_timeout;//重传 tv_off   通常为 jiffies+ icsk_rto 后 进行重传
     struct timer_list      icsk_retransmit_timer; // 通过icsk_pengding 来区分重传定时器和持续定时器
     struct timer_list      icsk_delack_timer;// 延时发送ack 定时器
    __u32              icsk_rto;// 重传超时时间 初始值为    TCP_TIMEOUT_INIT    根据网络情况动态计算
    __u32              icsk_pmtu_cookie; //最后一次更新的路径MTU 
    const struct tcp_congestion_ops *icsk_ca_ops;
    const struct inet_connection_sock_af_ops *icsk_af_ops;
    unsigned int          (*icsk_sync_mss)(struct sock *sk, u32 pmtu);
    __u8              icsk_ca_state:6,// 拥塞状态
                  icsk_ca_setsockopt:1,
                  icsk_ca_dst_locked:1;
    __u8              icsk_retransmits;// 超时重传的次数
    __u8              icsk_pending; //标志定时器事件     ICSK_TIME_EARLY_RETRANS  等可选值 表示 重传定时 持续定时器 保活定时器等
    __u8              icsk_backoff;// 计算持续定时器 下一个设定值的指数 退避算法指数 
    __u8              icsk_syn_retries; // 建立tcp 允许 重传 syn   syn+ack的次数
    __u8              icsk_probes_out;// 持续定时等  周期性发出未被确认的tcp seg 数目
    __u16              icsk_ext_hdr_len;
    struct {
        __u8          pending;     /* ACK is pending 标示 需要确认发送的 紧急程度 和状态               */
        __u8          quick;     /* Scheduled number of quick acks在快速发送确认模式中       */
        __u8          pingpong;     /* The session is interactive 启用禁用 快速确认模式    1 ---标示延时发送ack 0 标示快速发送ack       */
        __u8          blocked;     /* Delayed ACK was blocked by socket lock  软中断 用户进程 不能同时own sk    
        如果sk 被 user  拥有, 延时ack 定时器被触发,此时不应该发送ack, 
        blocked 为1;标示 如果有机会就需要立即发送,所以当接收的数据被cp到user 后 就可以立即发送ack */
        __u32          ato;         /* Predicted tick of soft clock 延时确认的估值       */
        unsigned long      timeout;     /* Currently scheduled timeout当前延时确认时间 超时后立即发送ack           */
        __u32          lrcvtime;     /* timestamp of last received data packet 最近一次接收到数据包时间*/
        __u16          last_seg_size; /* Size of last incoming segment最后一个接收到段的长度 用来计算rcv_mss       */
        __u16          rcv_mss;     /* MSS used for delayed ACK decisions 由最近接收的段计算出MSS       */ 
    } icsk_ack; // 延时确认控制块
    struct {
        int          enabled;// 是否开启路径MTU

        /* Range of MTUs to search  */
        int          search_high;
        int          search_low;

        /* Information on the current probe当前mtu 探测的长度 用于判断mtu是否完成  初始值为0. */
        int          probe_size;

        u32          probe_timestamp;
    } icsk_mtup;
    u32              icsk_user_timeout;

    u64              icsk_ca_priv[64 / sizeof(u64)];
#define ICSK_CA_PRIV_SIZE      (8 * sizeof(u64))
};

#define ICSK_TIME_RETRANS    1    /* Retransmit timer */
#define ICSK_TIME_DACK        2    /* Delayed ack timer */
#define ICSK_TIME_PROBE0    3    /* Zero window probe timer */
#define ICSK_TIME_EARLY_RETRANS 4    /* Early retransmit timer */
#define ICSK_TIME_LOSS_PROBE    5    /* Tail loss probe timer */




struct tcp_sock {//在 inet_connection_sock  基础上增加了 滑动窗口 拥塞控制算法等tcp 专有 属性
    /* inet_connection_sock has to be the first member of tcp_sock */
    struct inet_connection_sock    inet_conn;
    u16    tcp_header_len;    /* Bytes of tcp header to send        */
    u16    gso_segs;    /* Max number of segs per GSO packet    */

/*
 *    Header prediction flags
 *    0x5?10 << 16 + snd_wnd in net byte order
 */
    __be32    pred_flags;/*首部预测标志 在接收到 syn 跟新窗口 等时设置此标志 ,
    此标志和时间戳 序号等 用于判断执行 快速还是慢速路径*/
        
/*
 *    RFC793 variables by their proper names. This means you can
 *    read the code and the spec side by side (and laugh ...)
 *    See RFC793 and RFC1122. The RFC writes these in capitals.
 */
    u64    bytes_received;    /* RFC4898 tcpEStatsAppHCThruOctetsReceived
                 * sum(delta(rcv_nxt)), or how many bytes
                 * were acked.
                 */
    u32    segs_in;    /* RFC4898 tcpEStatsPerfSegsIn
                 * total number of segments in.
                 */
     u32    rcv_nxt;    /* What we want to receive next  等待接收的下一个序列号    */
    u32    copied_seq;    /* Head of yet unread data        */
    u32    rcv_wup;    /* rcv_nxt on last window update sent最早接收但没有确认的序号, 
    也就是接收窗口的左端,在发送ack的时候, rcv_nxt更新 因此rcv_wup 更新比rcv_nxt 滞后一些    */
     u32    snd_nxt;    /* Next sequence we send  等待发送的下一个序列号        */
    u32    segs_out;    /* RFC4898 tcpEStatsPerfSegsOut
                 * The total number of segments sent.
                 */
    u64    bytes_acked;    /* RFC4898 tcpEStatsAppHCThruOctetsAcked
                 * sum(delta(snd_una)), or how many bytes
                 * were acked.
                 */
    struct u64_stats_sync syncp; /* protects 64bit vars (cf tcp_get_info()) */

     u32    snd_una;    /* First byte we want an ack for  最早一个未被确认的序号    */
     u32    snd_sml;    /* Last byte of the most recently transmitted small packet  最近发送一个小于mss的最后 一个字节序列号
    在成功发送, 如果报文小于mss,跟新这个字段 主要用来判断是否启用 nagle 算法*/
    u32    rcv_tstamp;    /* timestamp of last received ACK (for keepalives)  最近一次收到ack的时间 用于 tcp 保活*/
    u32    lsndtime;    /* timestamp of last sent data packet (for restart window) 最近一次发送 数据包时间*/
    u32    last_oow_ack_time;  /* timestamp of last out-of-window ACK */

    u32    tsoffset;    /* timestamp offset */

    struct list_head tsq_node; /* anchor in tsq_tasklet.head list */
    unsigned long    tsq_flags;

    /* Data for direct copy to user cp 数据到用户进程的控制块 有用户缓存以及其长度 prequeue 队列 其内存*/
    struct {
        struct sk_buff_head    prequeue // tcp 段 缓冲到此队列 知道进程主动读取才真正的处理;
        struct task_struct    *task;
        struct msghdr        *msg;
        int            memory;// prequeue 当前消耗的内存
        int            len;// 用户缓存中 当前可以使用的缓存大小 
    } ucopy;

    u32    snd_wl1;    /* Sequence for window update记录跟新发送窗口的那个ack 段号 用来判断是否 需要跟新窗口
    如果后续收到ack大于snd_wll 则表示需要更新 窗口*/
    u32    snd_wnd;    /* The window we expect to receive 接收方 提供的窗口大小 也就是发送方窗口大小    */
    u32    max_window;    /* Maximal window ever seen from peer 接收方通告的最大窗口    */
    u32    mss_cache;    /* Cached effective mss, not including SACKS  发送方当前有效的mss*/

    u32    window_clamp;    /* Maximal window to advertise 滑动窗口最大值        */
    u32    rcv_ssthresh;    /* Current window clamp  当前接收窗口的阈值            */

    /* Information of the most recently (s)acked skb */
    struct tcp_rack {
        struct skb_mstamp mstamp; /* (Re)sent time of the skb */
        u8 advanced; /* mstamp advanced since last lost marking */
        u8 reord;    /* reordering detected */
    } rack;
    u16    advmss;        /* Advertised MSS            */
    u8    unused;
    u8    nonagle     : 4,/* Disable Nagle algorithm?  是否  开启 ngnagle 算法           */
        thin_lto    : 1,/* Use linear timeouts for thin streams */
        thin_dupack : 1,/* Fast retransmit on first dupack      */
        repair      : 1,
        frto        : 1;/* F-RTO (RFC5682) activated in CA_Loss */
    u8    repair_queue;
    u8    do_early_retrans:1,/* Enable RFC5827 early-retransmit  */
        syn_data:1,    /* SYN includes data */
        syn_fastopen:1,    /* SYN includes Fast Open option */
        syn_fastopen_exp:1,/* SYN includes Fast Open exp. option */
        syn_data_acked:1,/* data in SYN is acked by SYN-ACK */
        save_syn:1,    /* Save headers of SYN packet */
        is_cwnd_limited:1;/* forward progress limited by snd_cwnd? */
    u32    tlp_high_seq;    /* snd_nxt at the time of TLP retransmit. */

/* RTT measurement */
    u32    srtt_us;    /* smoothed round trip time << 3 in usecs  平滑rtt*/
    u32    mdev_us;    /* medium deviation    rtt平均偏差        */
    u32    mdev_max_us;    /* maximal mdev for the last rtt period   rtt平均偏差最大值     */
    u32    rttvar_us;    /* smoothed mdev_max            */
    u32    rtt_seq;    /* sequence number to update rttvar  记录SND.UNA 计算rto 时比较SND.NUA是否已经给更新
    如果SND.UNA 跟新,则需要同时跟新rttval*/
    struct rtt_meas {
        u32 rtt, ts;    /* RTT in usec and sampling time in jiffies. */
    } rtt_min[3];

    u32    packets_out;    /* Packets which are "in flight"发送出去 没有被ack的数 (SND.NEXT -SND.UNA )*/
    u32    retrans_out;    /* Retransmitted packets out 重传还未得到确认的tcp数        */
    u32    max_packets_out;  /* max packets_out in last window */
    u32    max_packets_seq;  /* right edge of max_packets_out flight */

    u16    urg_data;    /* Saved octet of OOB data and control flags 存放紧急数据以及控制标示 */
    u8    ecn_flags;    /* ECN status bits.            */
    u8    keepalive_probes; /* num of allowed keep alive probes 保活探测次数上限    */
    u32    reordering;    /* Packet reordering metric.        */
    u32    snd_up;        /* Urgent pointer 紧急数据指针 带外数据的序号        */

/*
 *      Options received (usually on last packet, some only on SYN packets).
 */
    struct tcp_options_received rx_opt;

/*
 *    Slow start and congestion control (see also Nagle, and Karn & Partridge)
 */
     u32    snd_ssthresh;    /* Slow start size threshold 拥塞控制 满启动阈值        */
     u32    snd_cwnd;    /* Sending congestion window    当前拥塞窗口大小    */
    u32    snd_cwnd_cnt;    /* Linear increase counter    自从上次调整拥塞窗口后 到目前位置接收到的
    总ack段数 如果该字段为0  表示调整拥塞窗口但是没有收到ack,调整拥塞窗口之后 收到ack段就回让
    snd_cwnd_cnt 加1 */
    u32    snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this  snd_cwnd  的最大值*/
    u32    snd_cwnd_used;//记录已经从队列发送而没有被ack的段数
    u32    snd_cwnd_stamp;//记录最近一次检验cwnd 的时间  拥塞期间 每次会检验cwnd而调节拥塞窗口 ,
    //在非拥塞期间,为了防止应用层序造成拥塞窗口失效  因此在发送后 有必要检测cwnd
    u32    prior_cwnd;    /* Congestion window at start of Recovery. */
    u32    prr_delivered;    /* Number of newly delivered packets to
                 * receiver in Recovery. */
    u32    prr_out;    /* Total number of pkts sent during Recovery. */

     u32    rcv_wnd;    /* Current receiver window 当前接收窗口的大小        */
    u32    write_seq;    /* Tail(+1) of data held in tcp send buffer */
    u32    notsent_lowat;    /* TCP_NOTSENT_LOWAT */
    u32    pushed_seq;    /* Last pushed seq, required to talk to windows */
    u32    lost_out;    /* Lost packets            */
    u32    sacked_out;    /* SACK'd packets            */
    u32    fackets_out;    /* FACK'd packets            */

    /* from STCP, retrans queue hinting */
    struct sk_buff* lost_skb_hint;
    struct sk_buff *retransmit_skb_hint;

    /* OOO segments go in this list. Note that socket lock must be held,
     * as we do not use sk_buff_head lock.
     */
    struct sk_buff_head    out_of_order_queue;

    /* SACKs data, these 2 need to be together (see tcp_options_write) */
    struct tcp_sack_block duplicate_sack[1]; /* D-SACK block */
    struct tcp_sack_block selective_acks[4]; /* The SACKS themselves*/

    struct tcp_sack_block recv_sack_cache[4];

    struct sk_buff *highest_sack;   /* skb just after the highest
                     * skb with SACKed bit set
                     * (validity guaranteed only if
                     * sacked_out > 0)
                     */

    int     lost_cnt_hint;
    u32     retransmit_high;    /* L-bits may be on up to this seqno */

    u32    prior_ssthresh; /* ssthresh saved at recovery start    */
    u32    high_seq;    /* snd_nxt at onset of congestion    */

    u32    retrans_stamp;    /* Timestamp of the last retransmit,
                 * also used in SYN-SENT to remember stamp of
                 * the first SYN. */
    u32    undo_marker;    /* snd_una upon a new recovery episode. */
    int    undo_retrans;    /* number of undoable retransmissions. */
    u32    total_retrans;    /* Total retransmits for entire connection */

    u32    urg_seq;    /* Seq of received urgent pointer  紧急数据的序号 所在段的序号和紧急指针相加获得*/
    unsigned int        keepalive_time;      /* time before keep alive takes place */
    unsigned int        keepalive_intvl;  /* time interval between keep alive probes */

    int            linger2;

/* Receiver side RTT estimation */
    struct {
        u32    rtt;
        u32    seq;
        u32    time;
    } rcv_rtt_est;

/* Receiver queue space */
    struct {
        int    space;
        u32    seq;
        u32    time;
    } rcvq_space;

/* TCP-specific MTU probe information. */
    struct {
        u32          probe_seq_start;
        u32          probe_seq_end;
    } mtu_probe;
    u32    mtu_info; /* We received an ICMP_FRAG_NEEDED / ICMPV6_PKT_TOOBIG
               * while socket was owned by user.
               */

#ifdef CONFIG_TCP_MD5SIG
/* TCP AF-Specific parts; only used by MD5 Signature support so far */
    const struct tcp_sock_af_ops    *af_specific;

/* TCP MD5 Signature Option information */
    struct tcp_md5sig_info    __rcu *md5sig_info;
#endif

/* TCP fastopen related information */
    struct tcp_fastopen_request *fastopen_req;
    /* fastopen_rsk points to request_sock that resulted in this big
     * socket. Used to retransmit SYNACKs etc.
     */
    struct request_sock *fastopen_rsk;
    u32    *saved_syn;
};

 

end

http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!! 但行好事 莫问前程 --身高体重180的胖子
原文地址:https://www.cnblogs.com/codestack/p/14877938.html