linuxc定时器,定时20ms_java 定时器schedule「建议收藏」

linuxc定时器,定时20ms_java 定时器schedule「建议收藏」本章是对socket通信过程中使用到的比较重要的据结构罗列和意义的阐述,在阅读其它层的代码前,先来看几个重要的数据结构,这几个数据结构贯串四层模型

linuxc定时器,定时20ms_java 定时器schedule「建议收藏」

本章是对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.

今天的文章linuxc定时器,定时20ms_java 定时器schedule「建议收藏」分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/89100.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注