本文的copyleft歸[email protected]所有,使用GPL釋出,可以自由拷貝,轉載。但轉載請保持文檔的完整性,注明原作者及原連結,嚴禁用于任何商業用途。
部落格:linuxfocus.blog.chinaunix.net
在前面的博文中,我畫出了TCP/IP資料包發送的流程圖。現在開始學習TCP部分。今天由sock_aio_write開始。這是第一個進入TCP發送函數write會調用的第一個socket層的函數。
static ssize_t sock_aio_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
struct sock_iocb siocb, *x;
if (pos != 0)
return -ESPIPE;
/* 申請sock iocb */
x = alloc_sock_iocb(iocb, &siocb);
if (!x)
return -ENOMEM;
return do_sock_write(&x->async_msg, iocb, iocb->ki_filp, iov, nr_segs);
}
從函數名字上看,這個函數是一個異步寫函數。參數struct kiocb iocb不進行特殊說明了,因為它與TCP/IP協定棧沒有直接聯系,它存在與所有的I/O操作中。這個函數其實與TCP/IP并不太大聯系,更多的是由于VFS。
int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t size)
struct iovec *iov;
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb;
int iovlen, flags;
int mss_now, size_goal;
int sg, err, copied;
long timeo;
...... ......
與UDP類似,對應于udp_sock,TCP也有struct tcp_sock。因為TCP的要比UDP複雜的多,是以這個tcp_sock也比udp_sock複雜龐大的多。這個struture非常複雜,超過160多行。不過幾乎每一個成員變量都有注釋,而我目前暫時也沒有能力添加更多的注釋,是以這裡就不粘貼代碼了。
不過我要對其中的struct inet_sock進行一點說明。在struct udp_sock中,struct inet_sock inet為其第一個成員變量。而對應struct tcp_sock,其第一個成員變量是struct inet_connection_sock inet_conn,不過struct inet_connection_sock inet_conn的第一個成員變量依然是struct inet_sock inet。是以無論是udp_sock還是tcp_sock,其記憶體布局上都保證了struct inet_sock inet為第一個成員變量。
struct inet_sock {
/* sk and pinet6 has to be the first two members of inet_sock */
struct sock sk;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct ipv6_pinfo *pinet6;
#endif
/* Socket demultiplex comparisons on incoming packets. */
/*
通過與下列的值比較,可以demultiplex收到的包。
通過系統調用connect,bind或setsocktopt可以設定下面的部分值。
*/
__be32 inet_daddr;
__be32 inet_rcv_saddr;
__be16 inet_dport;
/* inet_num 為主機序的port,即inet_sport為其網絡序格式 */
__u16 inet_num;
__be32 inet_saddr;
/* 使用者指定的ttl值,如為-1,則使用系統預設值 */
__s16 uc_ttl;
__u16 cmsg_flags;
__be16 inet_sport;
__u16 inet_id;
/* ip option 資訊*/
struct ip_options *opt;
__u8 tos;
最小的ttl,可以通過/proc指定。
若收到的包的ttl小于該值,則drop掉該包
__u8 min_ttl;
/* 多點傳播ttl */
__u8 mc_ttl;
PMTU value:
#define IP_PMTUDISC_DONT 0 /* Never send DF frames */
#define IP_PMTUDISC_WANT 1 /* Use per route hints */
#define IP_PMTUDISC_DO 2 /* Always DF */
#define IP_PMTUDISC_PROBE 3 /* Ignore dst pmtu */
__u8 pmtudisc;
/* 下面這些基本上都是socket的option */
__u8 recverr:1,
is_icsk:1, /* 是否是connection socket*/
freebind:1, /* 是否enable IP_FREEBIND option*/
hdrincl:1, /* 是否enable IP_HDRINCL option*/
mc_loop:1,
transparent:1,
mc_all:1,
nodefrag:1;
/* 多點傳播網卡的索引 */
int mc_index;
/* 用于發送的多點傳播位址 */
__be32 mc_addr;
/* 所有加入的多點傳播組 */
struct ip_mc_socklist *mc_list;
/* cork 資訊
struct {
unsigned int flags;
unsigned int fragsize;
struct ip_options *opt;
struct dst_entry *dst;
int length; /* Total length of all frames */
__be32 addr;
struct flowi fl;
} cork;
};
從上面可以看出,struct inet_sock為TCP/IP協定棧極為重要的結構,它要位于所有的不同sock類型的頂部。主要用于儲存不同socket類型公有的特性和資訊。組織結構有些類似于C++的基類——尤其是記憶體布局上。