linux定时器tinner,第三章 套接字相关数据结构--基于Linux3.10

本章是对socket通信过程中使用到的比较重要的据结构罗列和意义的阐述,在阅读其它层的代码前,先来看几个重要的数据结构,这几个数据结构贯串四层模型。

3.1 socket对应的内核结构体

在用户空间使用socket()函数创建一个套接字。对应的系统调用就是:

asmlinkagelong sys_socketcall(int call, unsigned long __user *args);该系统调用的定义在net/socket.c文件的2436行,调用流程中比较重要的一个函数是:

int __sock_create(struct net *net, int family, int type, int protocol,

struct socket **res, int kern)

{

struct socket*sock;

const struct net_proto_family *pf;

sock = sock_alloc(); //内存空间分配

//根据对应的协议族(protocol family)创建对应的sock。

err = pf->create(net, sock, protocol, kern);

if (err < 0)

goto out_module_put;

}

该函数首先创建一个structsocket的类型结构体,该结构体对应于用户空间的socket,socket的参数之一是协议族,对于Internet协议,create的函数原型是inet_create,internet对应协议族在内核中的表示如下:

static const struct net_proto_family inet_family_ops = {

.family = PF_INET,

.create = inet_create,

.owner = THIS_MODULE,

};

这里的inet_create函数作用是创建一个inet协议族下的套接字,并且初始化其中的一些成员。

该套接字传递到内核后,内核会创建structsocket存储来该数据结构:

struct socket {

socket_state state; //标记sock状态,如SS_CONNECTED、SS_CONNECTING等

short type; //socket类型SOCK_STREAM、SOCK_DGRAM、SOCK_RAW等。

unsigned long flags; //socket flag 如SOCK_ASYNC_NOSPACE

struct socket_wq __rcu*wq;

struct file *file; //垃圾回收的文件指针

struct sock *sk; //因特网内部协议的socket表示,对于PF_INET协议,inet_create会创建该成员的各个字段。

const struct proto_ops*ops; //协议族相关的操作函数集

};

在应用层socket表示套接字,在网络层(IP层)structsock对应应用层中的套接字。

struct sock {

socket_lock_t sk_lock; //该sock的访问锁

struct sk_buff_headsk_receive_queue; //接收到的数据包都放在这个sk_buff_head所指向的队列的头上。

struct {

atomic_t rmem_alloc;

int len;

struct sk_buff *head;

struct sk_buff *tail;

} sk_backlog; //对于接收的frame,其由IP层存放在backlog上,后通过tcp的函数进行接收。

#define sk_rmem_alloc sk_backlog.rmem_alloc

int sk_forward_alloc; //对于到达的frame非本机,允许forward将会被发送出去

#ifdef CONFIG_RPS //网卡新特性,下篇涉及

__u32 sk_rxhash;

#endif

atomic_t sk_drops; //丢弃的sock计数器

int sk_rcvbuf;

#ifdef CONFIG_XFRM

struct xfrm_policy *sk_policy[2]; ///流控策略,属于安全机制

#endif

unsigned long

sk_flags;

struct dst_entry *sk_rx_dst; //接收流向的

struct dst_entry __rcu *sk_dst_cache; //路由项的cache

spinlock_t sk_dst_lock; //路由锁

int sk_sndbuf;

struct sk_buff_headsk_write_queue; //发送队列

/*sock 信息、状态的一些标志*/

unsigned int sk_shutdown : 2,

sk_no_check : 2,

sk_userlocks : 4,

sk_protocol : 8,

sk_type : 16;

gfp_t sk_allocation; //sock动态获申请内存的Flag标志。

/*网卡的一些信息也记录到这里了*/

netdev_features_tsk_route_caps;

netdev_features_tsk_route_nocaps;

int sk_gso_type;

unsigned int sk_gso_max_size;

u16 sk_gso_max_segs;

/*sock 的一些错误统计信息在此处*/

struct sk_buff_headsk_error_queue;

struct proto *sk_prot_creator;

rwlock_t sk_callback_lock;

int sk_err,

sk_err_soft;

unsigned shortsk_ack_backlog;

unsigned shortsk_max_ack_backlog;

__u32 sk_priority;

/*接收和发送的时间戳*/

long sk_rcvtimeo;

long sk_sndtimeo;

void *sk_protinfo;

struct timer_listsk_timer;

ktime_t sk_stamp;

struct socket *sk_socket;

/*分片信息*/

struct page_fragsk_frag;

struct sk_buff *sk_send_head;// /*分片头信息*/

/*sock自带的一些函数指针集*/

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);

};

3.2 struct proto_ops

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,

};

上面函数的指针用户空间时常会用到,sendmsg  和recvmsg   是介于应用层和传输层之间的收发函数。

3.3 structproto

struct proto tcp_prot = {

.name = "TCP",

.owner = THIS_MODULE,

.close = tcp_close,

.connect = tcp_v4_connect, //建立连接使用到的函数,对应于用户空间的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,

}

该结构体描述的是tcp处理各种任务的若干函数,这些任务包括tcp链接的建立、控制等,这些数据都是由sk_buff_head(用于描述套接字缓存区头)结构体管理,数据本身会存在2.4节描述的sk_buff里。这个buffer使用通过proc的slabinfo可以看到,曾在一个嵌入式视频监控设备上就遇到过由于WiFi导致的sk_buff_head和sk_buff不定时异常增大的情况。

struct sk_buff_head {

/* These two members must be first. */

struct sk_buff*next; //下一个数据存放指针

struct sk_buff*prev;//前一个数据存放指针,next和prev会串接成一个双链表,qlen用于标记双链表的长度

__u32 qlen;//标记

spinlock_t lock; //保护该结构体的锁

};

3.4 sk_buff(SKB)

SKB存储了用户要求传递的数据,这些数据可能源于视频、图像、文本等,应用层传递到TCP/IP协议栈的数据会保存在sk_buff,不论是http还是rtsp,数据会一直存在sk_buff的结构成员中直到从网卡发送出去,接收也是类似的。网络数据包收发如此频繁,可以想象该结构体必然针对协议实现特点、处理流程以及内存等方面做了一些优化。

struct sk_buff {

/* These two members must be first. */

struct sk_buff*next; //指向该SKB的后一个SKB,其头就是上面sk_buff_head 指定的成员。

struct sk_buff*prev; //指向该SKB的前一个SKB

ktime_t tstamp; //数据包到达的时间戳

struct sock *sk; //对应的sock成员,即应用程序的socket在内核的代表,

struct net_device*dev; //网络设备,数据到达的网络设备或者数据离开的网络设备

//control buffer,协议栈很多地方都使用到了这个字段来存储一些会使用到的信息。

char cb[48] __aligned(8);

unsigned long _skb_refdst; //目的入口项

#ifdef CONFIG_XFRM

struct sec_path *sp; //xfrm安全机制使用,Security path。

#endif

unsigned int len, //数据实际长度值

data_len; //数据的长度,和真实长度的区别在于可能有padding

__u16 mac_len, //MAC的长度

hdr_len; //拷贝skb时,可更改的头长度

union {

__wsum csum; //校验和

struct {

__u16 csum_start; //校验和计算起始地址

__u16 csum_offset;//从csum_start开始的校验,这部分校验和会被存储。

};

};

__u32 priority; //packet排队的优先级

kmemcheck_bitfield_begin(flags1);

__u8 local_df:1, //允许本地分片的标志

cloned:1, //标记头是否可能拷贝,如果不对数据执行更改操作,则只会拷贝头。

ip_summed:2, //驱动程序填写的IP层校验和标志。

nohdr:1, //负载使用

nfctinfo:3;//SKB和tcp连接的关系

__u8 pkt_type:3, //packet所属的类

fclone:2, //复制状态标志,标识该SKB是复制的。

ipvs_property:1,//该SKB为ipvs所有。IP virtual Server,负载均衡,netfilter框架调用

peeked:1, //标志标识统计信息是否还要更新

nf_trace:1;//netfilter 包跟踪标志

kmemcheck_bitfield_end(flags1);

__be16 protocol; //packet所属的协议

void (*destructor)(struct sk_buff *skb);//解析函数

int skb_iif; //该packet所在设备的接口索引

__u32 rxhash; //接收数据包的哈希标志

__u16 queue_mapping; //支持多队列网卡设备的队列映射

kmemcheck_bitfield_begin(flags2);

__u8 pfmemalloc:1;

__u8 ooo_okay:1;

__u8 l4_rxhash:1;

__u8 wifi_acked_valid:1;

__u8 wifi_acked:1;

__u8 no_fcs:1;

__u8 head_frag:1;

sk_buff_data_tinner_transport_header; //MAC头、IP头、tcp头。前三个是指封装过的。

sk_buff_data_tinner_network_header;

sk_buff_data_tinner_mac_header;

sk_buff_data_ttransport_header;

sk_buff_data_tnetwork_header;

sk_buff_data_tmac_header;

/* These elements must be at the end, see alloc_skb() for details. */

sk_buff_data_ttail;

sk_buff_data_tend; //数据的相关指针

unsigned char *head, *data;

unsigned int truesize;

atomic_t users;

};

3.5

softnet_data

softnet_data是一个per-CPU变量,即每个CPU都有一个自己的softnet_data结构体,相比只有一个该结构体由多个CPU共享的变量,每个CPU都有一个队列可以减少锁操作。该结构体管理接收和发送的数据。定义于include/linux/netdevice.h文件。

struct softnet_data {

struct Qdisc *output_queue; //有数据包要发送的设备。

struct Qdisc **output_queue_tailp; //上述结构体的待处理的最后一个元素的指针。

struct list_head poll_list;

struct sk_buff *completion_queue; //已经成功发送,占用的空间可以释放了。

struct sk_buff_head process_queue;

/* stats */

unsigned int processed; //每一个处理该数据包的进程会将这里的计数器加1,以标记有多少个进程在其sk_buff。

unsigned int time_squeeze;

unsigned int cpu_collision;

unsigned int received_rps;

#ifdef CONFIG_RPS //网卡多队列,Receive Packet Steering,网卡的硬件特性。

struct softnet_data*rps_ipi_list;

/* Elements below can be accessed between CPUs for RPS */

struct call_single_datacsd ____cacheline_aligned_in_smp;

struct softnet_data*rps_ipi_next;

unsigned int cpu;

unsigned int input_queue_head;

unsigned int input_queue_tail;

#endif

unsigned int dropped;

struct sk_buff_headinput_pkt_queue; //在net_dev_initz中初始化,在网卡驱动程序处理以前,sk_buff链接到该链表上。

struct napi_structbacklog; //NAPI 处理最开始的那两个元素。

};

3.6 struct packet_type

struct packet_type {

__be16 type; /* This is really htons(ether_type). 标记类型,对于IP而言是 cpu_to_be16(ETH_P_IP),*/

struct net_device*dev;/* NULL is wildcarded here */

//该函数是四层网络模型中的网络层的函数,对于ipv4是ip_rcv,这是在之三文章中tcp/ip协议栈的网络层从网络到主机层接收数据包的函数。

int (*func) (struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *);

bool (*id_match)(struct packet_type *ptype, struct sock *sk);

void *af_packet_priv;

struct list_headlist;

};

netif_receive_skb函数会从ptype_base协议链表上查找和数据包的type对应的func处理程序,对于IP数据包,其类型是ETH_P_IP,服务函数是ip_rcv。此外还有和TCP/IP相关的一些重要数据结构。

3.7 一些名词简称

csk ---connection sock

icsk--- inet connection sock

ca –congestion avoid

cwr congestion window reduction(cwnd reduction)

ECN: Explicit Congestion Notification

SACK:selective ACK

PSH (1 bit) – Push function. Asks to push the buffered data to the receiving application

TIME-WAIT :

(either server or client) represents waiting for enough time to pass to be sure the remote TCP received the acknowledgment of its connection termination request.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值