proto socket 關聯結構:node
{ .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 結構:linux
/** * 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)