以ipv4来分析Linux网络的基本运行机制(1)

| 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;
...
}
看完本文有收获?请分享给更多人

关注「黑光技术」,关注大数据+微服务