TCP的初始化告一段落。沿着inet_init函数继续向下看。(尝试用MarkDown)
1422行 注册了个轻量级的UDP协议,有什么用处暂时还不知道。根据之前TCP等协议的注册情况,只需要记着udplite_prot是它资源管理者,udplite_protocol是面向IP的接口,udplite4_protows是面向socket的接口,就可以了。
1428 icmp_init(&inet_family_ops);
很显然是初始化ICMP协议了。ICMP协议全称是Internet Control Message Protocol叫Internet控制报文协议。是IP协议层的好伙伴,之前有提到过。传入的数据结构实例,也很眼熟,之前介绍过的用户socket向inet socket传递的入口数据结构实例。去看下源码。
1069 void __init icmp_init(struct net_proto_family *ops)
1070 {
1071 struct inet_sock *inet;
1072 int i;
1074 for_each_possible_cpu(i) {
1075 int err;
1077 err = sock_create_kern(PF_INET, SOCK_RAW, IPPROTO_ICMP,
1078 &per_cpu(__icmp_socket, i));
1080 if (err < 0)
1081 panic("Failed to create the ICMP control socket.\n");
1083 per_cpu(__icmp_socket, i)->sk->sk_allocation = GFP_ATOMIC;
1088 per_cpu(__icmp_socket, i)->sk->sk_sndbuf =
1089 (2 * ((64 * 1024) + sizeof(struct sk_buff)));
1091 inet = inet_sk(per_cpu(__icmp_socket, i)->sk); 1092 inet->uc_ttl = -1;
1093 inet->pmtudisc = IP_PMTUDISC_DONT;
1099 per_cpu(__icmp_socket, i)->sk->sk_prot->unhash(per_cpu(__icmp_socket, i)->sk); 1100 } 1101 }
又是一个简单的函数。一个循环,为每个可能的CPU都创建一个控制sock。和之前说过的TCP的控制sock有点像。剩下的内容就是初始化其相应的成员变量。不过这里还是要展开说一下的,因为看到没有现在已经可以创建socket了。之前TCP的控制socket创建时就应该展开说一个的。来吧,跟踪一下sock_create_kern的执行流程。
socket_create_kern定义在net/socket.c中,只是一层包装,真正调用的是__sock_create函数。__sock_create定义在同一个函数中。
1197 int sock_create_kern(int family, int type, int protocol, struct socket **res) 1198 { 1199 return __sock_create(&init_net, family, type, protocol, res, 1);
}
注意到,__sock_create此时多了一个参数init_net,定义在net/core/net_namespace.c上,类型为struct net,用来内核中网络子系统的命名空间管理。__sock_create代码如下:
1079 static int __sock_create(struct net *net, int family, int type, int protocol,
1080 struct socket **res, int kern)
1081 {
1082 int err;
1083 struct socket *sock;
1084 const struct net_proto_family *pf;
1089 if (family < 0 || family >= NPROTO)
1090 return -EAFNOSUPPORT;
1091 if (type < 0 || type >= SOCK_MAX)
1092 return -EINVAL;
1094 /* Compatibility.
1096 This uglymoron is moved from INET layer to here to avoid
1097 deadlock in module load.
1099 if (family == PF_INET && type == SOCK_PACKET) {
1100 static int warned;
1101 if (!warned) {
1102 warned = 1;
1103 printk(KERN_INFO "%s uses obsolete (PF_INET,SOCK_PACKET)\n",
1104 current->comm);
1105 }
1106 family = PF_PACKET;
1107 }
1109 err = security_socket_create(family, type, protocol, kern);
1110 if (err)
1111 return err;
1118 sock = sock_alloc();
1119 if (!sock) {
1120 if (net_ratelimit())
1121 printk(KERN_WARNING "socket: no more sockets\n");
1122 return -ENFILE; /* Not exactly a match, but its the
1123 closest posix thing */
1124 }
1126 sock->type = type;
1139 rcu_read_lock();
1140 pf = rcu_dereference(net_families[family]);
1141 err = -EAFNOSUPPORT;
1142 if (!pf)
1143 goto out_release;
1149 if (!try_module_get(pf->owner))
1150 goto out_release;
1153 rcu_read_unlock();
1155 err = pf->create(net, sock, protocol);
1156 if (err < 0)
1157 goto out_module_put;
1163 if (!try_module_get(sock->ops->owner))
1164 goto out_module_busy;
1170 module_put(pf->owner);
1171 err = security_socket_post_create(sock, family, type, protocol, kern);
1172 if (err)
1173 goto out_sock_release;
1174 *res = sock;
1176 return 0;
1190 }
函数的流程也不复杂,先是进行了一系列的参数检查,linux稳定的性能离不开对参数的严格检查,这几乎是linux所有函数的特点。我们假设传入的参数都没有问题。来看下主要的函数调用。
1109 security_socket_create 如果内核在编译时支持了网络安全模块,这个函数就会做一些实际的工作,我们假设不支持此选项。这个函数就简单地返回0。同样的还有 security_socket_post_create这个函数。
1118. sock_alloc 这个函数调用才是生成了真正的socket,注意不是strcut sock而是struct socket,面向用户的socket。这个函数的内容并不值得去看,和协议没有什么关系。只是分配了文件系统里的结点而已,分配了一定的内存,由于系统现在还不知道底层具体是什么样的协议,需要多少内存还不确定。
1155 这里是一个成员函数的调用。这是我们的兴趣点,要想知道底层是什么样socket被创建,就要追溯下这个成员函数的前因后果了。
首先看pf,在1140行被赋值,是一个解引用操作,决定性的变量是net_families和family两个。其中net_families在之前已经有讲过。family是我们调用sock_create_kern时传入的参数,值为PF_INET。在本系列第3篇中有说到过inet_family_ops被挂到net_families中时,它的索引就是inet_family_ops.family也就是PF_INET。
1140 pf = rcu_dereference(net_families[family]);
所以,这里pf就是指向inet_family_ops的指针了。那么pf->create调用的自然就是inet_create了。这就是为什么说inet_family_ops,在“创建特定协议的SOCKET时,系统正是在这里找到对应的协议入口”。
inet_create的内容很长。先说一半吧。定义在net/ipv4/af_inet.c中。从函数开始到289行。用来寻找协议与类型对应的inet_protosw。还记得循环中的inetsw列表吗?在本系列第四篇中被写入了tcp_prot udp_prot raw_prot三个成员。这里是根据sock->type来索引,而sock在创建之后sock->type被置为最初调用时的type参数就是SOCK_RAW。解引用时,得到是raw_prot对应的实例。第二参数protocol为IPPROTO_ICMP,最后得到的answer是raw_prot对应的在inetsw中元素。
剩下的部分里,313-320 根据索引到的answer对sock进行赋值,初始化。
244 static int inet_create(struct net *net, struct socket *sock, int protocol)
245 {
246 struct sock *sk;
247 struct list_head *p;
248 struct inet_protosw *answer;
249 struct inet_sock *inet;
250 struct proto *answer_prot;
251 unsigned char answer_flags;
252 char answer_no_check;
253 int try_loading_module = 0;
254 int err;
256 if (net != &init_net)
257 return -EAFNOSUPPORT;
259 if (sock->type != SOCK_RAW &&
260 sock->type != SOCK_DGRAM &&
261 !inet_ehash_secret)
262 build_ehash_secret();
264 sock->state = SS_UNCONNECTED;
267 answer = NULL;
268 lookup_protocol:
269 err = -ESOCKTNOSUPPORT;
270 rcu_read_lock();
271 list_for_each_rcu(p, &inetsw[sock->type]) {
272 answer = list_entry(p, struct inet_protosw, list);
275 if (protocol == answer->protocol) {
276 if (protocol != IPPROTO_IP)
277 break;
278 } else {
280 if (IPPROTO_IP == protocol) {
281 protocol = answer->protocol;
282 break;
283 }
284 if (IPPROTO_IP == answer->protocol)
285 break;
286 }
287 err = -EPROTONOSUPPORT;
288 answer = NULL;
289 }
313 err = -EPERM;
314 if (answer->capability > 0 && !capable(answer->capability))
315 goto out_rcu_unlock;
317 sock->ops = answer->ops;
318 answer_prot = answer->prot;
319 answer_no_check = answer->no_check;
320 answer_flags = answer->flags;
这个函数余下的代码下次再说。不难看到,通过一次创建socket的函数调用,可以把整个协议栈的数据结构实例串起来了。