管理套接字的数据类型
套接字缓冲区类型 struct sk_buff, struct sk_buff_head
skbuff.h
struct sk_buff是套接字缓冲区类型,用来管理网络数据包,为发/收的网络数据提供存储区域和操作方法。sk_buff数据类型贯穿数据传输全过程,是十分重要的数据结构。
struct sk_buff_head管理一个双向链表,来组织sk_buff。
sk_buff与数据存储
1 | struct sk_buff{ |
协议族管理类型 struct net_proto_family
include/linux/net.h
管理不同协议族套接字的创建方法。
1 | struct net_proto_family { |
linux内核通过struct net_proto_family型的net_families表来管理协议族。linux支持的协议族会被sock_register()注册到net_families数组。
1 | static struct net_proto_family *net_families[NPROTO]; |
我们看看该类型还有哪些变量。
对于INET协议族,有:
net/ipv4/af_inet.c
1 | struct net_proto_family inet_family_ops = { |
对于UNIX域,有unix_family_ops管理创建方法,create
指针指向unix_create()。
net/ipv4/af_unix.c
协议族套接字操作集 struct proto_ops
include/linux/net.h
协议族操作集类型,统一管理套接字操作函数。linux通过struct proto_ops类型定义的统一接口管理各种功能的函数,接口就是定义中的各种函数指针。
1 | struct proto_ops { |
struct proto_ops类型也有针对不同协议的各种变量。
对于INET协议族的TCP和UDP协议,有inet_stream_ops和inet_dgram_ops两个变量。当INET协议族套接字采用TCP传输层协议时,由inet_stream_ops变量管理套接字操作集,通过这个操作集变量,各个函数有所指定。
INET协议族的定义都在af_inet.c,UNIX域的定义都在af_unix.c中。
1 | struct proto_ops inet_stream_ops = { //TCP协议 |
套接字结构类型 struct socket
include/linux/net.h
每个套接字在内核中都对应唯一的struct socket结构,该类型提供不同协议族套接字的统一表示。
用户程序通过唯一的套接字描述符来表示套接字,用户的套接字描述符与struct socket一一对应。
1 | struct socket { |
传输层协议操作集 struct proto
include/net/sock.h
struct proto封装了传输协议操作集,其中函数指针指向特定于传输协议的函数。
1 | struct proto { |
针对不同的传输层协议,linux提供不同的struct proto变量来管理传输协议操作集。对于INET的TCP和UDP的协议,有struct proto tcp_prot和struct proto udp_prot,对于其他协议有struct proto raw_prot。
net/ipv4/tcp_ipv4.c
1 | struct proto tcp_prot = { //TCP协议 |
tcp_prot变量封装了与TCP协议相关的操作函数,为INET协议族的SOCK_STREAM套接字提供了使用TCP的方法,例如函数指针connect
指向的tcp_v4_connect()函数发出连接请求,启动三次握手过程。
raw_prot变量封装了与原始数据相关的操作函数,为INET的SOCK_RAW套接字提供了访问IP层的发方法。
套接字在传输层的表示结构 struct sock
include/net/sock.h
struct sock结构体代表了传输层的套接字结构,包含了与具体传输层协议相关的信息。在为套接字指定传输层协议后,struct socket的sk
指针会指向一个与传输协议关联的struct sock。
struct sock结构定义(部分内容)如下:
1 | struct sock{ |
所有套接字最后通过struct sock结构来使用网络协议栈的服务。
其中的sk_prot指针,根据不同的传输层协议,指向具体的struct proto变量,即一些传输层的操作集。传输层为TCP协议时,sk_prot指向的结构体等同于tcp_prot;传输层为UDP协议时,sk_prot指向的结构体等同于udp_prot。
传输层接收方法管理 struct net_protocol
include/net/protocol.h
管理第四层接收数据包的方法。各协议通过注册该类型变量,来注册接收该协议接收数据包的方法。
1 | struct net_protocol { |
例如TCP初始化时,内核注册了struct net_protocol tcp_protocol来管理接收TCP数据包的方法。
1 | static struct net_protocol tcp_protocol = { |
struct inet_protosw, inetsw_array数组
将INET套接字的协议族操作集和传输层协议操作集关联起来。
1 | struct inet_protosw { |
该类型的inetsw_array[]实现了INET套接字的协议族操作集与具体的传输层协议关联。该数组在收发数据筛选函数时有用。
1 | static struct inet_protosw inetsw_array[] = { |
在函数inet_init()对INET协议族进行初始化的时候,由inet_register_protosw把数组inetsw_array上记录的关联信息注册到inetsw数组中。
在系统使用过程中,内核以套接字类型
为索引访问inetsw,inetsw定义如下:
1 | static struct list_head inetsw[SOCK_MAX]; |
1 | struct list_head{ |
套接字功能实现
start_kernel()调用了与网络初始化相关的函数:sock_init()、do_initcalls()等。
sock_init()主要完成与套接字相关的初始化,do_initcalls()完成协议初始化,inet_init()完成与INET套接字相关的初始化。
套接字初始化 sock_init()
net/socket.c
sock_init()主要任务是初始化套接字。具体初始化的具体内容如下:
1 | void __init sock_init(void) |
协议族套接字初始化的工作由对应的初始化函数来完成。
协议族初始化
不同协议族套接字初始化都不同的函数,例如:INET协议族套接字初始化由inet_init()函数完成。
INET协议族套接字初始化inet_init()
net/ipv4/af_inet.c
INET协议族初始化时,inet_init()调用sock_register()注册INET套接字的创建方法到(struct net_proto_family)*net_families[NPROTO]中,该数组记录各协议套接字的创建方法。
1 | static int __init inet_init(void) |
注册协议族套接字的创建方法 sock_register()
net/socket.c
sock_register()向net_families数组中添加协议族套接字的创建方法,该数组记录各协议套接字的创建方法。
1 | int sock_register(struct net_proto_family *ops) |
套接字创建流程
1 | int socket(int domain, int type, int protocol); |
我们在应用层使用socket()函数创建套接字时,socket()触发内核调用sys_socket(),sys_socket()又调用sock_create()。
sock_create()根据一路传参下来的协议族类型参数有选择地调用不同的套接字创建函数。例如,指定的协议族类型为PF_INET,则sock_create()调用inet_create()创建INET套接字。
继续以INET套接字为例。inet_create()首先创建套接字的内核表示结构,再返回一个套接字描述符来记录生成的套接字对象。该套接字描述符一直被返回到socket()函数,从而我们可以通过调用socket()获得套接字描述符。
INET协议套接字创建函数 inet_create()
net/ipv4/af_inet.c
inet_create()函数创建一个INET套接字(一个struct socket变量),并根据套接字类型来决定传输层所采用的协议。
struct socket中最重要的成员是sk,它指向一个struct sock。inet_create()首先调用sk_alloc()为sock分配内存,令sk
指向它,并按照套接字类型对其初始化。
例,如果sock->type=SOCK_STREAM
,那么内核会创建TCP协议的套接字,sock->ops
提供INET协议族操作集,sock->sk->sk_prot
将提供TCP协议操作集。通过查询inetsw链表,inet_create()得到记录传输层协议操作集的信息。
针对TCP协议,查询到的结果是tcp_prot,传输协议操作集struct proto tcp_prot中记录了一系列TCP协议相关操作的函数,有关套接字初始化的函数为tcp_v4_init_sock()。
TCP协议套接字初始化函数 tcp_v4_init_sock()
tcp_v4_init_sock()初始化套接字与TCP有关的数据,包括初始化TCP队列、传输定时器、发送窗口、慢启动窗口、拥塞窗口控制、数据包最小长度,以及sk相关参数:套接字状态、收发缓冲区、队列有关的字段。