计算机网络segment和socket,sock、socket和tcp_sock之间的关系

1. sock结构和tcp_sock结构之间的关系

在linux的内核网络的源码分析中,我们经常看见这样的内核函数使用。

首先,看个例子,如下所示。

struct sock *sk;

struct tcp_sock *tp = tcp_sk(sk);static inline struct tcp_sock *tcp_sk(const struct sock *sk)

{

return (struct tcp_sock *)sk;

}

通过上面的源码中可以看出,可以通过类型的强制转换,将struct sock结构转换成struct tcp_sock类型的结构。进行过强制转换的变量占用的内存地址空间,没有发生变化,只是类型发生的变化,这样我们就可以使用转换后的类型来操作结构体里面的成员,上面进行转换的目的就是这个。

当时,我在看到这个类型转换的时候,我就猜测,这个sock和tcp_sock结构之间肯定是包含的关系,即要么就是sock结构中第一个字段里面包含tcp_sock结构,要么就是相反,tcp_sock结构中第一个字段中包含sock结构。我当时是这么想的。然后,我就把这两个结构体打开。如下所示。

struct tcp_sock {

/* inet_connection_sock has to be the first member of tcp_sock */

struct inet_connection_sockinet_conn;

u16tcp_header_len;/* Bytes of tcp header to send*/

u16gso_segs;/* Max number of segs per GSO packet*/

...

...

};struct sock {

/*

* Now struct inet_timewait_sock also uses sock_common, so please just

* don't add nothing before this first member (__sk_common) --acme

*/

struct sock_common__sk_common;

#define sk_node__sk_common.skc_node

#define sk_nulls_node__sk_common.skc_nulls_node

#define sk_refcnt__sk_common.skc_refcnt

#define sk_tx_queue_mapping__sk_common.skc_tx_queue_mapping

#define sk_dontcopy_begin__sk_common.skc_dontcopy_begin

#define sk_dontcopy_end__sk_common.skc_dontcopy_end

#define sk_hash__sk_common.skc_hash

#define sk_portpair__sk_common.skc_portpair

#define sk_num__sk_common.skc_num

#define sk_dport__sk_common.skc_dport

#define sk_addrpair__sk_common.skc_addrpair

#define sk_daddr__sk_common.skc_daddr

#define sk_rcv_saddr__sk_common.skc_rcv_saddr

#define sk_family__sk_common.skc_family

#define sk_state__sk_common.skc_state

#define sk_reuse__sk_common.skc_reuse

#define sk_reuseport__sk_common.skc_reuseport

#define sk_ipv6only__sk_common.skc_ipv6only

#define sk_net_refcnt__sk_common.skc_net_refcnt

#define sk_bound_dev_if__sk_common.skc_bound_dev_if

#define sk_bind_node__sk_common.skc_bind_node

#define sk_prot__sk_common.skc_prot

#define sk_net__sk_common.skc_net

#define sk_v6_daddr__sk_common.skc_v6_daddr

#define sk_v6_rcv_saddr__sk_common.skc_v6_rcv_saddr

#define sk_cookie__sk_common.skc_cookie

#define sk_incoming_cpu__sk_common.skc_incoming_cpu

#define sk_flags__sk_common.skc_flags

#define sk_rxhash__sk_common.skc_rxhash

socket_lock_tsk_lock;

atomic_tsk_drops;

intsk_rcvlowat;

struct sk_buff_headsk_error_queue;

struct sk_buff_headsk_receive_queue;

/*

* The backlog queue is special, it is always used with

* the per-socket spinlock held and requires low latency

* access. Therefore we special case it's implementation.

* Note : rmem_alloc is in this structure to fill a hole

* on 64bit arches, not because its logically part of

* backlog.

*/

struct {

atomic_trmem_alloc;

intlen;

struct sk_buff*head;

struct sk_buff*tail;

} sk_backlog;

#define sk_rmem_alloc sk_backlog.rmem_alloc

...

struct socket*sk_socket;

...

};

上面的两个结构体都很大,我只截取了部分字段。但是我为了验证我的猜想,我们只需要每个结构体的第一个字段就可以。当我们查看了两个结构体的第一个字段,发现并不是我们猜想的那样,要么sock结构中第一个字段包含tcp_sock结构,要么tcp_sock结构中第一个字段包含sock结构。比对过后,发现这个两个结构都不相互包含。那么进行类型强制转换过后,我们直接操作结构里面的数据成员,会发生数据的读取的错误,因为原有的成员的位置在新的结构体中成员的名字和位置都不同了。显然,这是不可能发生的。

那么,情况只能是,某个结构中第一个字段中,在这个字段中包含另一个结构体。我们也只能这么想,接下来我们验证这个想法。

在tcp_sock结构体中我们看到第一个字段是

/* inet_connection_sock has to be the first member of tcp_sock */

struct inet_connection_sockinet_conn;

我们进入源码,查看相应的结构体。

struct inet_connection_sock {

/* inet_sock has to be the first member! */

struct inet_sock icsk_inet;

struct request_sock_queue icsk_accept_queue;

struct inet_bind_bucket *icsk_bind_hash;

unsigned long icsk_timeout;

struct timer_list icsk_retransmit_timer;

struct timer_list icsk_delack_timer;

__u32 icsk_rto;

__u32 icsk_pmtu_cookie;

const struct tcp_congestion_ops *icsk_ca_ops;

const struct inet_connection_sock_af_ops *icsk_af_ops;

const struct tcp_ulp_ops *icsk_ulp_ops;

void *icsk_ulp_data;

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;

__u8 icsk_backoff;

__u8 icsk_syn_retries;

__u8 icsk_probes_out;

__u16 icsk_ext_hdr_len;

struct {

__u8 pending; /* ACK is pending */

__u8 quick; /* Scheduled number of quick acks */

__u8 pingpong; /* The session is interactive */

__u8 blocked; /* Delayed ACK was blocked by socket lock */

__u32 ato; /* Predicted tick of soft clock */

unsigned long timeout; /* Currently scheduled timeout */

__u32 lrcvtime; /* timestamp of last received data packet */

__u16 last_seg_size; /* Size of last incoming segment */

__u16 rcv_mss; /* MSS used for delayed ACK decisions */

} icsk_ack;

struct {

int enabled;

/* Range of MTUs to search */

int search_high;

int search_low;

/* Information on the current probe. */

int probe_size;

u32 probe_timestamp;

} icsk_mtup;

u32 icsk_user_timeout;

u64 icsk_ca_priv[88 / sizeof(u64)];

#define ICSK_CA_PRIV_SIZE (11 * sizeof(u64))

};

在这个inet_connection_sock结构中,看到的第一个字段是struct inet_sockicsk_inet;还不是我们想要的sock结构体。没有办法,我们只能够不停的递归的展开,希望在这个结构体中第一个字段包含我们的sock结构体。

我再次进入到struct inet_sock结构中。源码如下。

struct inet_sock {

/* sk and pinet6 has to be the first two members of inet_sock */

struct socksk;

#if IS_ENABLED(CONFIG_IPV6)

struct ipv6_pinfo*pinet6;

#endif

/* Socket demultiplex comparisons on incoming packets. */

#define inet_daddrsk.__sk_common.skc_daddr

#define inet_rcv_saddrsk.__sk_common.skc_rcv_saddr

#define inet_dportsk.__sk_common.skc_dport

#define inet_numsk.__sk_common.skc_num

__be32inet_saddr;

__s16uc_ttl;

__u16cmsg_flags;

__be16inet_sport;

__u16inet_id;

struct ip_options_rcu __rcu*inet_opt;

intrx_dst_ifindex;

__u8tos;

__u8min_ttl;

__u8mc_ttl;

__u8pmtudisc;

__u8recverr:1,

is_icsk:1,

freebind:1,

hdrincl:1,

mc_loop:1,

transparent:1,

mc_all:1,

nodefrag:1;

__u8bind_address_no_port:1,

defer_connect:1; /* Indicates that fastopen_connect is set

* and cookie exists so we defer connect

* until first data frame is written

*/

__u8rcv_tos;

__u8convert_csum;

intuc_index;

intmc_index;

__be32mc_addr;

struct ip_mc_socklist __rcu*mc_list;

struct inet_cork_fullcork;

};

看到这个结构体的源码,很是高兴,果不其然,这真是我们想看到的,在这个inet_sock结构中,包含的第一个字段正是我们的sock结构。

通过上面的分析,我们进行总结:tcp_sock和sock之间的关系。

tcp_sock结构体中包含的第一个字段inet_connection_sock结构。

inet_connection_sock结构体中包含的第一个字段是inet_sock结构。

inet_sock结构体中包含的第一个字段是sock结构。

2. sock结构和socket结构之间的关系

在sock结构中,我们也看到了一个字段是struct socket*sk_socket;这个字段是个指针变量,指向socket结构体。

查看 socket结构体的源码信息。

struct socket {

socket_statestate;

shorttype;

unsigned longflags;

struct socket_wq __rcu*wq;

struct file*file;

struct sock*sk;

const struct proto_ops*ops;

};

在socket结构体中,我们也看到一个成员字段struct sock*sk;这个也是一个指针变量,指向 sock结构体。

通过上面发现,我们可以看出,sock和socket字段中都有相互指向对方的指针变量的字段。

3. 总结(sock结构、socket结构和tcp_sock结构三者之间的关系)

对于struct socket和struct sock,它们的区别在于,socket结构体是对应于用户态,是为应用层提供的统一结构,也就是所谓的general BSD socket。而sock结构体是对应于内核态,是socket在网络层的表示(network layer representation of sockets)。它们两者是一一对应的,在struct socket中有一个指针指向对应的struct sock。

最后用一张图大概描述一下这个几个结构体的关系。

55ccc7c2242deb6fdff342f496d6b98b.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值