全部博文(41)
分类: LINUX
2009-11-18 11:28:35
Linux
978计划工作组
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_family __sk_common.skc_family
#define sk_state __sk_common.skc_state
#define sk_reuse __sk_common.skc_reuse
#define sk_bound_dev_if __sk_common.skc_bound_dev_if
#define sk_node __sk_common.skc_node
#define sk_nulls_node __sk_common.skc_nulls_node
#define sk_bind_node __sk_common.skc_bind_node
#define sk_refcnt __sk_common.skc_refcnt
#define sk_hash __sk_common.skc_hash
#define sk_prot __sk_common.skc_prot
#define sk_net __sk_common.skc_net
unsigned char sk_shutdown : 2,
sk_no_check : 2,
sk_userlocks : 4;
unsigned char sk_protocol;
unsigned short sk_type;
int sk_rcvbuf;
socket_lock_t sk_lock;
/*
* 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.
*/
struct {
struct sk_buff *head;
struct sk_buff *tail;
} sk_backlog;
wait_queue_head_t *sk_sleep;
struct dst_entry *sk_dst_cache;
#ifdef CONFIG_XFRM
struct xfrm_policy *sk_policy[2];
#endif
rwlock_t sk_dst_lock;
atomic_t sk_rmem_alloc;
atomic_t sk_wmem_alloc;
atomic_t sk_omem_alloc;
int sk_sndbuf;
struct sk_buff_head sk_receive_queue;
struct sk_buff_head sk_write_queue;
#ifdef CONFIG_NET_DMA
struct sk_buff_head sk_async_wait_queue;
#endif
int sk_wmem_queued;
int sk_forward_alloc;
gfp_t sk_allocation;
int sk_route_caps;
int sk_gso_type;
unsigned int sk_gso_max_size;
int sk_rcvlowat;
unsigned long sk_flags;
unsigned long sk_lingertime;
struct sk_buff_head sk_error_queue;
struct proto *sk_prot_creator;
rwlock_t sk_callback_lock;
int sk_err,
sk_err_soft;
atomic_t sk_drops;
unsigned short sk_ack_backlog;
unsigned short sk_max_ack_backlog;
__u32 sk_priority;
struct ucred sk_peercred;
long sk_rcvtimeo;
long sk_sndtimeo;
struct sk_filter *sk_filter;
void *sk_protinfo;
struct timer_list sk_timer;
ktime_t sk_stamp;
struct socket *sk_socket;
void *sk_user_data;
struct page *sk_sndmsg_page;
struct sk_buff *sk_send_head;
__u32 sk_sndmsg_off;
int sk_write_pending;
#ifdef CONFIG_SECURITY
void *sk_security;
#endif
__u32 sk_mark;
/* XXX 4 bytes hole on 64 bit */
void (*sk_state_change)(struct sock *sk);
void (*sk_data_ready)(struct sock *sk, int bytes);
void (*sk_write_space)(struct sock *sk);
void (*sk_error_report)(struct sock *sk);
int (*sk_backlog_rcv)(struct sock *sk,
struct sk_buff *skb);
void (*sk_destruct)(struct sock *sk);
};
sock结构是对socket结构的扩充,与文件系统相关的域放在socket结构中了,而与网络相关的域放在了sock结构中了,目的是在非网络数据结构中定义socket域时无需包括整个sock结构,而只分配一个sock结构的指针即可,这样可以大大的节约内存。
struct sock_common __sk_common
网络层的最常用属性放在此结构中了,如地址族、连接状态等。详细信息参见sock_common结构。
unsigned char sk_shutdown : 2
标示了socket关闭的情况,关于关闭的情况有3种,如下所示:
SHUTDOWN_MASK:宏值为3,表示完全关闭。
RCV_SHUTDOWN:宏值为1 :接收通道被关闭(远端发送了FIN 数据包)。
SEND_SHUTDOWN:发送通道关闭(本地主动发送FIN 数据包)。
unsigned char sk_no_check : 2
打开或关闭校验和。
unsigned char sk_userlocks : 4
用户锁标志,有四种锁,共占四位,如下所示:
SOCK_SNDBUF_LOCK :宏值为1,sk_userlocks的第0位(最低位为0位)为1时不可以修改sk_sndbuf的值,即如果锁了就不可以修改此值。
SOCK_RCVBUF_LOCK :宏值为2,sk_userlocks的第1位(最低位为0位)为1时不可以修改sk_rcvbuf的值,即如果锁了就不可以修改此值。
SOCK_BINDADDR_LOCK :宏值为4,sk_userlocks域在inet_bind函数中将第2位(最低位为0位)置位1,此位被置位后不可再调用inet_reset_saddr函数重新设置地址。
SOCK_BINDPORT_LOCK :宏值为8,sk_userlocks域在inet_bind函数中将第3位(最低位为0位)置位1,该位被置位后不可调用inet_put_port函数。
unsigned char sk_protocol
标示使用哪种协议,此域在inet_create中被初始化。
unsigned short sk_type
socket的类型,有7种类型,如下所示:
SOCK_STREAM:值为1,流套接字,提供双向连续且可信赖的数据流,TCP协议用此种类型。SOCK_DGRAM:值为2,数据报套接字,使用不连续不可信赖的数据包连接,UDP协议用此种类
型。
SOCK_RAW:值为3,原始套接字,一般的套接字是通过TCP才能和IP底层进行数据交换的,原始套接字可以直接穿过TCP层操作IP数据包,也就是说原始套接字是上层应用程序和IP层之间的通道。
SOCK_RDM:值为4,提供可信赖的数据包连接。
SOCK_SEQPACKET:值为5,顺序包套接字, 提供连续可信赖的数据包连接。
SOCK_DCCP:值为6,数据报阻塞控制协议套接字。
SOCK_PACKET:值为10,提供和网络驱动程序直接通信,Linux特有的方式。
还有一个特殊的宏,标示socket类型的上限值的宏SOCK_MAX,被定义为 (SOCK_PACKET + 1)。
Int sk_rcvbuf
以字节为单位的接收缓冲区的大小。
socket_lock_t sk_lock
sock的同步锁。
struct {
struct sk_buff *head;
struct sk_buff *tail;
} sk_backlog
sk_backlog链表中数据的处理的函数调用关系是:
tcp_recvmsg调用release_sock,release_sock调用__release_sock,__release_sock调用sk_backlog_rcv,而sk_backlog_rcv函数指针指向了tcp_v4_do_rcv,详情参见下面对sk_backlog_rcv的介绍,tcp_v4_do_rcv函数在TCP_ESTABLISHED状态时通过调用tcp_rcv_established函数实现数据处理并释放了节点。在TCP_LISTEN状态时会调用tcp_child_process。
sk_backlog链表中的数据被增加的函数调用关系是:
ip层接到数据包处理完后是通过ip_local_deliver函数把ip包递送到上层进行处理的(具体的Ip层及以下层是如何处理的在其他文档中讨论),ip_local_deliver函数调用ip_local_deliver_finish函数,ip_local_deliver_finish函数中通过全局的struct net_protocol的实例inet_protos[hash]取得struct net_protocol的指针。
inet_protos[hash]的handler域是一个函数指针指向了tcp_v4_rcv函数;系统中存在一个全局静态struct net_protocol结构的实例tcp_protocol,此实例的handler域被tcp_v4_rcv初始化,在inet_init函数中将此静态实例tcp_protocol通过inet_add_protocol付给了hash表结构的数组inet_protos,这样inet_protos[hash]->handler域就指向了tcp_v4_rcv。
tcp_v4_rcv函数在需要将数据放到sk_backlog对列时通过直接调用sk_add_backlog函数实现了将收到的数据增加到sk_backlog链表中,或者通过调用tcp_v4_do_rcv函数,tcp_v4_do_rcv在TCP_LISTEN状态时可能会调用tcp_child_process ,而tcp_child_process 再调用sk_add_backlog函数间接实现将收到的数据添加到sk_backlog链表中。
wait_queue_head_t *sk_sleep
等待队列,主要应用在客户端发起连接后等待连接完成、服务器端的accept函数等待新连接建立的完成,等待数据到达等方面。涉及的函数如rfcomm_sock_accept、sco_sock_accept、inet_wait_for_connect等。
现以inet_wait_for_connect为例讲述一下sk_sleep的应用,以及线程是如何被睡眠和唤醒的;函数首先用DEFINE_WAIT宏初始化一个wait_queue_t类型的睡眠队列节点,调用prepare_to_wait函数把当前进程添加到sk_sleep睡眠队列(通过调用__add_wait_queue函数实现),然后函数进入一个while循环等待系统连接完成,while循环中调用schedule_timeout函数,schedule_timeout函数会调用setup_timer_on_stack把当前线程放入一个计时器队列并将process_timeout函数作为时间到后的执行函数,然后调用schedule函数让调度函数重新选择一个就绪的线程去执行,当计时器到期后会执行process_timeout函数,而process_timeout函数会调用wake_up_process函数把刚才睡眠的线程唤醒,inet_wait_for_connect函数继续执行,调用signal_pending函数查看信号是否到达,到达后执行finish_wait函数将当前线程从sk_sleep睡眠队列中删除,至此连接完成。
struct dst_entry *sk_dst_cache
目的入口,最终生成的IP数据报的路由称为目的入口(dst_entry),目的入口反映了相邻的外部主机在主机内部的一种“映像”。
rwlock_t sk_dst_lock
读写锁,针对目的入口结构(dst_entry)的读写锁。
atomic_t sk_rmem_alloc
接收缓冲队列中已经分配的字节数,用于跟踪缓冲区的使用情况。
atomic_t sk_wmem_alloc
发送缓冲队列中已经分配的字节数,用于跟踪缓冲区的使用情况。
atomic_t sk_omem_alloc
当前申请的sock选项的空间大小。
int sk_sndbuf
是socket所允许的最大发送缓冲区。最小值用宏SOCK_MIN_SNDBUF表示,宏值为2048。它的值在系统初始化的时候设为变量sysctl_tcp_wmem[1]的值,在tcp_init函数中sysctl_tcp_wmem[1] 被初始化为16*1024,可以通过系统调用进行修改。
struct sk_buff_head sk_receive_queue
接收队列,与sk_backlog域关系密切,ip层接到数据包处理完后是通过ip_local_deliver函数把ip包递送到上层进行处理的,ip_local_deliver函数调用ip_local_deliver_finish函数,ip_local_deliver_finish函数最终会调用tcp_v4_rcv函数。
tcp_v4_rcv函数会先判断进程空间是否有进程使用sock及是否在等待数据到来,如sock空闲且在等待数据到来则直接放到sk_receive_queue对列中,如sock空闲但未在等待则加数据到prequeue对列,如进程在使用sock则将数据放到sk_backlog对列。
struct sk_buff_head sk_write_queue
发送数据队列,在函数sock_init_data中被初始化,在函数ip_append_data和ip_append_page中被增加节点,在函数ip_flush_pending_frames和ip_push_pending_frames中被减少节点。
int sk_wmem_queued
记录了当前在发送缓存队列sk_write_queue中总的字节数。当收到对应的ack包后此值才会被相应的减少。
int sk_forward_alloc
sock分配的缓存的总合,在__sk_mem_schedule函数中分配读、写缓冲区,分配成功则此值增加,在函数__sk_mem_reclaim中释放分配的读、写缓冲区,释放成功则此值相应的减少,注意一次分配和减少的缓冲区的大小是宏PAGE_SIZE的整数倍,此值是0X1000即4k大小。
gfp_t sk_allocation
用于标示内存分配的一种模式。在sock_init_data函数中被初始化为GFP_KERNEL,此宏值为0,GFP_KERNEL表示在内存分配时如果内存不够可以睡眠等待,也可以是其它宏,如:GFP_ATOMIC(内存的分配不可睡眠)、GFP_USER(为用户空间页来分配内存; 可能睡眠)等。
此处的GFP是Get Free Pages的缩写。
int sk_route_caps
标示网络驱动的特征,可以是如下值的组合:NETIF_F_SG、NETIF_F_IP_CSUM、NETIF_F_NO_CSUM、NETIF_F_HW_CSUM 等,具体的参见net_device结构。
int sk_gso_type
gso是Generic Segmentation Offload的缩写,中文为“普通段卸载”,此域表示GSO的类型。在tcp_v4_connect和tcp_v6_connect函数中分别被赋值为SKB_GSO_TCPV4(针对v4的IP分片)和SKB_GSO_TCPV6(针对v6的IP分片)。sk_gso_type每位代表的含义为SKB_GSO_TCPV4(第0位) 、SKB_GSO_UDP(第1位)、SKB_GSO_DODGY(第2位)、SKB_GSO_TCP_ECN(第3位)、SKB_GSO_TCPV6(第4位)、SKB_GSO_FCOE(第5位)。
sock的选项标志,可以被设置为SO_LINGER、SO_BROADCAST、SO_KEEPALIVE、SO_OOBINLINE、SO_TIMESTAMPING。
unsigned int sk_gso_max_size
最大的gso(普通段卸载)尺寸。
int sk_rcvlowat
接收缓冲区的最小字节数,此值在Linux中是固定值1,在函数sock_init_data中被初始化。
unsigned long sk_flags
sock的选项标志,可以被设置为SO_LINGER、SO_BROADCAST、SO_KEEPALIVE、SO_OOBINLINE、SO_TIMESTAMPING。
unsigned long sk_lingertime
如果sk_flags标志的SO_LINGER位为1,那么当执行关闭(close或shutdown)动作时将等到所有套接字里排队的消息成功发送或延时sk_lingertime时间后才会返回,否则调用将立即返回。
struct sk_buff_head sk_error_queue
错误队列,是一个sk_buff结构的链表。
struct proto *sk_prot_creator
指向一组协议处理函数集。inet_create函数调用sk_alloc函数,sk_prot_creator在函数sk_alloc中被初始化,可能的取值为tcp_protocol、udp_protocol、icmp_protocol等。