linux支持多种不同的符合OSI模型的通信协议。协议模块分为多组协议族和套接字类型如下图:
套接字有两个抽象的层次,一个是socket一个是sock。
套接字与sock结构相关联,包含于特定协议族和类型相关的域。sock数据结构中的域指向特定协议族的数据,包括特定协议的函数集合、控制标志、特定协议信息的数据指针。
socket()系统调用仅标识每个协议族和类型的函数集合,并对套接字和sock数据结构进行初始化。sock对象有一个prot域,该域指向协议相关的操作集。
1.1.1 协议注册
sock_register函数(位于net/socket.c中),入参为net_proto_family,在系统初始化阶段为协议族添加套接字的接口。对于INET而言就是inet_family_ops。
inet_create函数是创建INET协议族套接字的调用函数。这个函数在sock_register()中赋值给struct net_proto_family中的create函数指针。
net_families[]是全局数组,用来注册网族套接字。
inetsw是在系统初始化时候初始化的数组,通过套接字类型(SOCK_STREAM等)索引。
inet_register_protosw()函数位于net/ipv4/af_inet.c文件中, 注册inet套接字的回调函数。
inetsw_array是一个全局静态数组,对象是结构体inet_protosw,结构体static struct inet_protosw inetsw_array[]已经定义如下,系统在初始化的时候会读取inetsw_array来填写inetsw数组(在网络初始化inet_init函数中,调用inet_register_protosw函数来完成),因此系统中所有inet套接字都在inetsw数组中。
static struct inet_protosw inetsw_array[] =
{
{
.type = SOCK_STREAM,
.protocol = IPPROTO_TCP,
.prot = &tcp_prot,
.ops = &inet_stream_ops,
.flags = INET_PROTOSW_PERMANENT |
INET_PROTOSW_ICSK,
},
{
.type = SOCK_DGRAM,
.protocol = IPPROTO_UDP,
.prot = &udp_prot,
.ops = &inet_dgram_ops,
.flags = INET_PROTOSW_PERMANENT,
},
{
.type = SOCK_DGRAM,
.protocol = IPPROTO_ICMP,
.prot = &ping_prot,
.ops = &inet_sockraw_ops,
.flags = INET_PROTOSW_REUSE,
},
{
.type = SOCK_RAW,
.protocol = IPPROTO_IP, /* wild card */
.prot = &raw_prot,
.ops = &inet_sockraw_ops,
.flags = INET_PROTOSW_REUSE,
}
};
如下图
1.1.2 创建sock
inet_protosw结构体定义如下:
定义在文件include/net/protocol.h中:
/* This is used to register socket interfaces for IP protocols. */
struct inet_protosw {
struct list_head list;
/* These two fields form the lookup key. */
unsigned short type; /* This is the 2nd argument to socket(2). */
unsigned short protocol; /* This is the L4 protocol number. */
struct proto *prot;
const struct proto_ops *ops;
unsigned char flags; /* See INET_PROTOSW_* below. */
};
其中prot是proto结构的指针,该结构包含特定于IP协议(如TCP/UDP)的函数集合(close,connect,accept,bind),例如tcp_prot与SOCK_STREAM相对应,udp_prot与SOCK_DGRAM相对应。IP协议块在结构proto帮助下,实现与套接字结合。
ops是指向proto_ops类型结构的指针,包含特定协议族的函数集,和prot类似,不过是套接字层的函数。inet_stream_ops与SOCK_STREAM对应,inet_dgram_ops与SOCK_DGRAM对应。一旦调用与套接字相关的系统调用,首先调用proto_ops结构体中的相应函数,然后调用proto结构中相应IP协议相关的函数。
1.1.3 套接字创建
以tcp套接字为例,其创建过程如下:
socket->sys_socket->sock_create->inet_create(net_families[family]->create())->sock_init_data()->(sk->prot->init=tcp_v4_init_sock())->tcp_init_xmit_timers()->tcp_prequeue_init()
sock_create(位于net/socket.c文件中)调用__sock_create,用于初始化套接字相关的sock结构体。
实现过程如下图所示: