原文地址:http://anders0913.iteye.com/blog/411986
用戶使用socket系統調用編寫應用程序時,通過一個數字來表示一個socket,所有的操作都在該數字上進行,這個數字稱為套接字描述符。在系統調用 的實現函數里,這個數字就會被映射成一個表示socket的結構體,該結構體保存了該socket的所有屬性和數據。在內核的協議中實現中,關於表示 socket的結構體,是一個比較復雜的東西,下面一一介紹。
struct socket。
這是一個基本的BSD socket,我們調用socket系統調用創建的各種不同類型的socket,開始創建的都是它,到后面,各種不同類型的socket在它的基礎上進行 各種擴展。struct socket是在虛擬文件系統上被創建出來的,可以把它看成一個文件,是可以被安全地擴展的。下面是其完整定義:
- struct socket {
- socket_state state;
- unsigned long flags;
- const struct proto_ops *ops;
- struct fasync_struct *fasync_list;
- struct file *file;
- struct sock *sk;
- wait_queue_head_t wait;
- short type;
- };
state用於表示socket所處的狀態,是一個枚舉變量,其類型定義如下:
- typedef enum {
- SS_FREE = 0, //該socket還未分配
- SS_UNCONNECTED, //未連向任何socket
- SS_CONNECTING, //正在連接過程中
- SS_CONNECTED, //已連向一個socket
- SS_DISCONNECTING //正在斷開連接的過程中
- }socket_state;
該成員只對TCP socket有用,因為只有tcp是面向連接的協議,udp跟raw不需要維護socket狀態。
flags是一組標志位,在內核中並沒有發現被使用。
ops是協議相關的一組操作集,結構體struct proto_ops的定義如下:
- struct proto_ops {
- int family;
- struct module *owner;
- int (*release)(struct socket *sock);
- int (*bind)(struct socket *sock, struct sockaddr *myaddr, int sockaddr_len);
- int (*connect)(struct socket *sock, struct sockaddr *vaddr, int sockaddr_len, int flags);
- int (*socketpair)(struct socket *sock1, struct socket *sock2);
- int (*accept)(struct socket *sock,struct socket *newsock, int flags);
- int (*getname)(struct socket *sock, struct sockaddr *addr,int *sockaddr_len, int peer);
- unsigned int (*poll)(struct file *file, struct socket *sock,
- struct poll_table_struct *wait);
- int (*ioctl)(struct socket *sock, unsigned int cmd, unsigned long arg);
- int (*listen)(struct socket *sock, int len);
- int (*shutdown)(struct socket *sock, int flags);
- int (*setsockopt)(struct socket *sock, int level,
- int optname, char __user *optval, int optlen);
- int (*getsockopt)(struct socket *sock, int level,
- int optname, char __user *optval, int __user *optlen);
- int (*sendmsg)(struct kiocb *iocb, struct socket *sock,
- struct msghdr *m, size_t total_len);
- int (*recvmsg)(struct kiocb *iocb, struct socket *sock,
- struct msghdr *m, size_t total_len, int flags);
- int (*mmap)(struct file *file, struct socket *sock,struct vm_area_struct * vma);
- ssize_t (*sendpage)(struct socket *sock, struct page *page,
- int offset, size_t size, int flags);
- };
協議棧中總共定義了三個strcut proto_ops類型的變量,分別是myinet_stream_ops, myinet_dgram_ops, myinet_sockraw_ops,對應流協議, 數據報和原始套接口協議的操作函數集。
type是socket的類型,對應的取值如下:
- enum sock_type {
- SOCK_DGRAM = 1,
- SOCK_STREAM = 2,
- SOCK_RAW = 3,
- SOCK_RDM = 4,
- SOCK_SEQPACKET = 5,
- SOCK_DCCP = 6,
- SOCK_PACKET = 10,
- };
sk是網絡層對於socket的表示,結構體struct sock比較龐大,這里不詳細列出,只介紹一些重要的成員,
sk_prot和sk_prot_creator,這兩個成員指向特定的協議處理函數集,其類型是結構體struct proto,該結構體也是跟struct proto_ops相似的一組協議操作函數集。這兩者之間的概念似乎有些混淆,可以這么理解,struct proto_ops的成員操作struct socket層次上的數據,處理完了,再由它們調用成員sk->sk_prot的函數,操作struct sock層次上的數據。即它們之間存在着層次上的差異。struct proto類型的變量在協議棧中總共也有三個,分別是mytcp_prot,myudp_prot,myraw_prot,對應TCP, UDP和RAW協議。
sk_state表示socket當前的連接狀態,是一個比struct socket的state更為精細的狀態,其可能的取值如下:
- enum {
- TCP_ESTABLISHED = 1,
- TCP_SYN_SENT,
- TCP_SYN_RECV,
- TCP_FIN_WAIT1,
- TCP_FIN_WAIT2,
- TCP_TIME_WAIT,
- TCP_CLOSE,
- TCP_CLOSE_WAIT,
- TCP_LAST_ACK,
- TCP_LISTEN,
- TCP_CLOSING,
- TCP_MAX_STATES
- ;
這些取值從名字上看,似乎只使用於TCP協議,但事實上,UDP和RAW也借用了其中一些值,在一個socket創建之初,其取值都是 TCP_CLOSE,一個UDP socket connect完成后,將這個值改為TCP_ESTABLISHED,最后,關閉sockt前置回TCP_CLOSE,RAW也一樣。
sk_rcvbuf和sk_sndbuf分別表示接收和發送緩沖區的大小。sk_receive_queue和sk_write_queue分別為接收緩 沖隊列和發送緩沖隊列,隊列里排列的是套接字緩沖區struct sk_buff,隊列中的struct sk_buff的字節數總和不能超過緩沖區大小的設定。
接着上一篇,繼續介紹struct sock。
sk_rmem_alloc, sk_wmem_alloc和sk_omem_alloc分別表示接收緩沖隊列,發送緩沖隊列及其它緩沖隊列中已經分配的字節數,用於跟蹤緩沖區的使用情況。
struct sock有一個struct sock_common成員,因為struct inet_timewait_sock也要用到它,所以把它單獨歸到一個結構體中,其定義如下:
- struct sock_common {
- unsigned short skc_family;
- volatile unsigned char skc_state;
- unsigned char skc_reuse;
- int skc_bound_dev_if;
- struct hlist_node skc_node;
- struct hlist_node skc_bind_node;
- atomic_t skc_refcnt;
- unsigned int skc_hash;
- struct proto *skc_prot;
- };
struct inet_sock。
這是INET域專用的一個socket表示,它是在struct sock的基礎上進行的擴展,在基本socket的屬性已具備的基礎上,struct inet_sock提供了INET域專有的一些屬性,比如TTL,組播列表,IP地址,端口等,下面是其完整定義:
- struct inet_sock {
- struct sock sk;
- #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- struct ipv6_pinfo *pinet6;
- #endif
- __u32 daddr; //IPv4的目的地址。
- __u32 rcv_saddr; //IPv4的本地接收地址。
- __u16 dport; //目的端口。
- __u16 num; //本地端口(主機字節序)。
- __u32 saddr; //發送地址。
- __s16 uc_ttl; //單播的ttl。
- __u16 cmsg_flags;
- struct ip_options *opt;
- __u16 sport; //源端口。
- __u16 id; //單調遞增的一個值,用於賦給iphdr的id域。
- __u8 tos; //服務類型。
- __u8 mc_ttl; //組播的ttl
- __u8 pmtudisc;
- __u8 recverr:1,
- is_icsk:1,
- freebind:1,
- hdrincl:1, //是否自己構建ip首部(用於raw協議)
- mc_loop:1; //組播是否發向回路。
- int mc_index; //組播使用的本地設備接口的索引。
- __u32 mc_addr; //組播源地址。
- struct ip_mc_socklist *mc_list; //組播組列表。
- struct {
- unsigned int flags;
- unsigned int fragsize;
- struct ip_options *opt;
- struct rtable *rt;
- int length;
- u32 addr;
- struct flowi fl;
- } cork;
- };
struct raw_sock
這是RAW協議專用的一個socket的表示,它是在struct inet_sock基礎上的擴展,因為RAW協議要處理ICMP協議的過濾設置,其定義如下:
- struct raw_sock {
- struct inet_sock inet;
- struct icmp_filter filter;
- };
struct udp_sock
這是UDP協議專用的一個socket表示,它是在struct inet_sock基礎上的擴展,其定義如下:
- struct udp_sock {
- struct inet_sock inet;
- int pending;
- unsigned int corkflag;
- __u16 encap_type;
- __u16 len;
- };
struct inet_connection_sock
看完上面兩個,我們覺得第三個應該就是struct tcp_sock了,但事實上,struct tcp_sock並不直接從struct inet_sock上擴展,而是從struct inet_connection_sock基礎上進行擴展,struct inet_connection_sock是所有面向連接的socket的表示,關於該socket,及下面所有tcp相關的socket,我們在分析 tcp實現時再詳細介紹,這里只列出它們的關系。
strcut tcp_sock
這是TCP協議專用的一個socket表示,它是在struct inet_connection_sock基礎進行擴展,主要是增加了滑動窗口協議,避免擁塞算法等一些TCP專有屬性。
struct inet_timewait_sock
struct tcp_timewait_sock
在struct inet_timewait_sock的基礎上進行擴展。
struct inet_request_sock
struct tcp_request_sock
在struct inet_request_sock的基礎上進行擴展。

