| 2009年10月16日
在Linux中的网络接口主要的有这么几种:
- socket layer -> transport layer interface is defined by struct proto
从socket层到传输层,这个socket层基本上也就是所谓的应用层了,应为就是这一层给用户态程序提供了系统调用接口。用户台程序在glibc的帮助下就可以使用这一层的函数进行网络数据的收发了。
- transport -> network interface is defined by struct inet_proto
传输层到网络层,
还有就是网络层到物理接口.
这里先分析从socket层到传输层:
在net/ipv4/af_inet.c文件中有一个函数:static int __init inet_init(void) 这个函数由fs_initcall(inet_init);运行,fs_initcall和module_init的工作是一样的。 就是将这个模块加载到内核中运行,所以内核在配置的IPV4的支持之后就会编译 net/ipv4/af_inet.c这个文件。这可通过分析其中的Makefile文件得知。
net/Makefile中有如下内容
obj-$(CONFIG_INET) += ipv4/
这里可以看出如果选中了ipv4的支持则会编译ipv4这个文件夹下的内容。
net/ipv4/Makefile中有如下内容
obj-y := route.o inetpeer.o protocol.o \
ip_input.o ip_fragment.o ip_forward.o ip_options.o \
ip_output.o ip_sockglue.o inet_hashtables.o \
inet_timewait_sock.o inet_connection_sock.o \
tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o \
tcp_minisocks.o tcp_cong.o \
datagram.o raw.o udp.o udplite.o \
arp.o icmp.o devinet.o af_inet.o igmp.o \
fib_frontend.o fib_semantics.o \
inet_fragment.o
这里可以看出如果net/ipv4/这个文件夹中的内容要编译的话,af_inet.o是一定会编译到内核中的。
在来看static int __init inet_init(void)这个函数所做的工作:
在所有的内核中的bind,listen,accept等都是以这样的方式被调用的 sock->ops->(bind,listen,….) 例如:在用户态发起sendmsg请求时,就会调用socketcall,socketcall又会根据传递的参数调用sys_sendmsg, 在sys_sendmsg函数中最终又会去调用已经创建的socket中的sendmsg函数:sock->ops->sendmsg(…..)。
这里关键是要知道sock->ops->sendmsg(…..)这个函数是从什么地方来的,这个方法是在使用socket函数的时候给新创建的sock->ops给的一个操作集。要认识这个操作集就需要看这样的一个数据结构: 定义在net/socket.c文件
static const struct net_proto_family *net_families[NPROTO] __read_mostly;
include/linux/net.h
struct net_proto_family 定义如下:
struct net_proto_family {
int family;
int (*create)(struct net *net, struct socket *sock, int protocol);
struct module *owner;
};
这个指针数组使用来成方各种内核所支持协议的操作集的,这个操作集中就是像上面sock->ops->sendmsg(…..)之类的函数。
这个操作是在创建socket的函数__sock_create,创建的。
static int __sock_create(struct net *net, int family, int type, int protocol,
struct socket **res, int kern)
const struct net_proto_family *pf;
由这个操作pf = rcu_dereference(net_families[family]);
对于net_families的初始化是由net/ipv4/af_inet.c中的函数
static int inet_create(struct net *net, struct socket *sock, int protocol)
来初始化的!!这个会在下一篇文章中去分析。
err = pf->create(net, sock, protocol);
在这个里面pf的实现是在各个协议自己的实现中定义的。 以ipv4为例可以看到是这样的:
net/ipv4/af_inet.c
static struct net_proto_family inet_family_ops = {
.family = PF_INET,
.create = inet_create,
.owner = THIS_MODULE,
};
在这个inet_create中进行sock->ops的实现:
static int inet_create(struct net *net, struct socket *sock, int protocol)
{
struct sock *sk;
struct inet_protosw *answer;
。。。。
list_for_each_entry_rcu(answer, &inetsw[sock->type], list) {
//这里是根据具体的协议号来判断传递过来的是不是这个协议的请求。判断真确之后就会以answer的形式返回。
//这里又有一个数据结构&inetsw[sock->type],这个数据结构是在在net/ipv4/af_inet.c这个文件的函数inet_create在ipv4协议初始化时进行填充的。
//见下面的分析
。。。
sock->ops = answer->ops;
。。。
}
在net/ipv4/af_inet.c这个文件中:
net/ipv4/af_inet.c
static int inet_create(struct net *net, struct socket *sock, int protocol)
该函数以模块的形式对ipv4的协议进行了初始化和内核注册。
static int __sock_create(struct net *net, int family, int type, int protocol,
struct socket **res, int kern)
int sock_register(const struct net_proto_family *ops)
{
...
net_families[ops->family] = ops;
...
}
关注「黑光技术」,关注大数据+微服务