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

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

  1. 《UNIX网络编程》读书笔记——第三章 套接字编程简介

    第三章   套接字编程简介 一.套接字地址结构 以socket_in命名,定义在<netinet/in.h>头文件中. struct in_addr{ in_addr_t s_addr:} ...

  2. UNP学习笔记-第三章套接字编程

    套接字地址结构 地址转换函数 地址转换函数在地址的文本表达和存放在套接字地址结构中的二进制值之间进行转换. 例如IPv4中inet_addr, inet_ntoa .新函数:inet_pton,ine ...

  3. linux下使用fread读socket套接字的注意点

    linux下使用fread读socket套接字的注意点 linux总所周知,一切皆文件.因此我们在读写文件时觉得fread,recv这些可以随便用.下面看看有哪些注意事项呢. 因为C库的文件处理函数较 ...

  4. 【Android 应用开发】Android 网络编程 API笔记 - java.net 包 权限 地址 套接字 相关类 简介

    Android 网络编程相关的包 : 9 包, 20 接口, 103 类, 6 枚举, 14异常; -- Java包 : java.net 包 (6接口, 34类, 2枚举, 12异常); -- An ...

  5. Linux设备驱动——第三章字符驱动

    当对幸福的憧憬过于急切,那痛苦就在人的心灵深处升起.--加缪 本章的目的是编写一个完整的字符设备驱动.我们开发一个字符驱动是因为这一类适合大部分简单的硬件设备.字符驱动也比块驱动易于理解.本章的最终目 ...

  6. 【正点原子Linux连载】第三章 RV1126开发环境搭建 摘自【正点原子】ATK-DLRV1126系统开发手册

    1)实验平台:正点原子RV1126 Linux开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=692176265749 3)全套实验源码+手册+视频 ...

  7. Linux内核探讨-- 第三章

    本文是个人分析<Linux内核设计与实现>而写的总结,欢迎转载,请注明出处:                                                         ...

  8. 第二章:Linux系统目录介绍+第三章:Linux系统文件类型和文件权限

    文章目录 二.Linux目录结构 1.Linux的文件目录结构图 2.常用目录介绍 3.相对路径和绝对路径 三. Linux系统文件类型和文件权限 1.文件类型 2.文件权限 3.如果修改文件权限 3 ...

  9. linux网络编程Internet Socket地址,套接字,和函数

    文章内容节选<linux/UNIX 系统网络编程> Internet domain socket地址有两种:IPv4 IPv6 IPv4被存储在结构体中, 该结构体在 netinet/in ...

最新文章

  1. Quartz 实现分布式任务调度
  2. BufferedInputStream与BufferedOutputStream用法简介
  3. iptables命令_理解 Linux 下的 Netfilter/iptables
  4. javascript基础拾遗——词法作用域
  5. C++ - 模板函数须要类型转换时使用友元(friend)模板函数
  6. Java集合总结大全--史上最强
  7. ASP.NET中的HTTP模块和处理程序
  8. 虚拟机的性能测试经验总结(一)
  9. python 批量gif转tif_使用Python 批量转移*.tif和*.mov文件
  10. 【C/C++】C++重复率最高、最经典面试题/笔试题【持续更新】
  11. 巴克莱银行申请区块链专利改进银行服务
  12. python 计时器模块_python中计时器
  13. sap crm button_如何安装SAP软件?
  14. 无盘服务器 安装客户机程序,顺网云服务端和客户端安装
  15. 基于javaweb的房屋租赁系统(前端+后端)
  16. 让IIS支持PHP (转载)
  17. 关于项目部署在Tomcat中使用软连接之坑
  18. LM358呼吸灯(亲测能用,效果很棒~)
  19. 局域网安全之ARP攻击
  20. Chrome解析JSON工具

热门文章

  1. 在Python中使用MongoDB
  2. mysql数据库管理指导_mysql学习笔记一(数据库管理控制)
  3. 图片裁剪(cropperjs)
  4. jssdk 获取微信收货地址_微信收货地址共享开发接口讲解
  5. 华为户外模式怎么设置_华为FreeLace Pro降噪器效果怎么样?降噪开启和设置教程!...
  6. 指定module_详解流量复制解决方案--NGINX的ngx_http_mirror_module模块实现
  7. mysql 组内排名_【原】MySQL分组排序(包含组内排名、求中位数)
  8. 请问投稿中要求上传的author_文章投稿如何做到时间管理?(二)
  9. 气候变化与 计算机网络,北京理工大学:气候变化综合评估模式研究获立项
  10. 用Html 和 Css来实现一个小小案例