原址:
http://fpcfjf.blog.163.com/blog/static/55469793201372033430691/
在内核的sys_bind(sys_bind()---inet_bind()---inet_csk_get_port())过程中,在函数int inet_csk_get_port(struct sock *sk, unsigned short snum)中有一段代码
net\inet_connection_sock.c
……..
ret= 1;
if(inet_csk(sk)->icsk_af_ops->bind_conflict(sk,tb)) {
if(sk->sk_reuse && sk->sk_state != TCP_LISTEN &&
smallest_size != -1 && --attempts>= 0) {
spin_unlock(&head->lock);
gotoagain;
}
…….
success:
if(!inet_csk(sk)->icsk_bind_hash)
inet_bind_hash(sk,tb, snum);
……
Net\ipv4\inet_hashtables.c
void inet_bind_hash(struct sock *sk, structinet_bind_bucket *tb,
const unsigned short snum)
{
structinet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
atomic_inc(&hashinfo->bsockets);
inet_sk(sk)->num= snum;
sk_add_bind_node(sk,&tb->owners);
tb->num_owners++;
inet_csk(sk)->icsk_bind_hash= tb;
}
其实上面的代码主要是引出下面的两个函数,这两个函数很简单,只是把sock的变量sk强制转换成inet_sock、inet_connection_sock两个结构体。但大家有没有注意到,在前面SOCKET最初建立的时候只是sk = sk_alloc(net, PF_INET,GFP_KERNEL, answer_prot);(在net\ipv4\af_inet.c的inet_create()函数中),也就是说,只是创建一个struct sock *sk类型的结构体,那么,在这里强制转换后,在后面直接操作这两个类型中的变量,为什么却可以使用呢?今天的问题就从这里开始。先看一下这两个强制转换的函数。
Include\net\inet_sock.h:
static inline struct inet_sock*inet_sk(const struct sock *sk)
{
return (struct inet_sock *)sk;
}
Include\net\inet_connection_sock.h:
static inline struct inet_connection_sock*inet_csk(const struct sock *sk)
{
return (struct inet_connection_sock *)sk;
}
再来看几个结构体,为了简单说明问题,不全部拷贝:
struct tcp_sock {
struct inet_connection_sock inet_conn; //inet_connection_sock has to bethe first member of tcp_sock
...
};
inet_connection_sock - INET connectionoriented sock
struct inet_connection_sock {
struct inet_sock icsk_inet; //inet_sock has to be thefirst member!
...
};
struct inet_sock - representation of INETsockets
struct inet_sock {
struct sock sk; // sk and pinet6 has to be the first twomembers of inet_sock ...
};
一定要注意看英文注释,第一个变量必须且不得改变,所以才可以强制进行换,这是强制转换的前提。好,下面看既然可以强制转换了,那么是不是就可以使用了呢?大家可以发现,从上到下,也就是说sock是最小的,而上面的程序也说明了,只是分配了一个这样类型的结构体,既然可以强制转换后可以使用,就不得不想到C和C++里的结构体空间的大小分配后进行强制转换,动态取得适应自己类型空间大小的指针,在C++中可以用父子类更为形象的来理解。(即分配一个子类指针,然后可以强制转成父类指针,这样只访问父类的对象),那就很明显了:
一定是分配了一个比STRUCT SOCK大的空间大小的内存区间。这就需要回到SOCKET的创建过程中去了。
好,跳到真正的分配函数中去:
sk = sk_alloc(net, PF_INET, GFP_KERNEL,answer_prot);---- sk = sk_prot_alloc(prot, priority | __GFP_ZERO, family);
net\core\sock.c
static struct sock *sk_prot_alloc(structproto *prot, gfp_t priority,
intfamily)
{
structsock *sk;
structkmem_cache *slab;
slab =prot->slab;
if (slab !=NULL)
sk =kmem_cache_alloc(slab, priority); (1)
else
sk =kmalloc(prot->obj_size, priority); (2)
if(sk != NULL) {
if(security_sk_alloc(sk, family, priority))
gotoout_free;
if(!try_module_get(prot->owner))
gotoout_free_sec;
}
returnsk;
out_free_sec:
security_sk_free(sk);
out_free:
if(slab != NULL)
kmem_cache_free(slab,sk);
else
kfree(sk);
returnNULL;
}
看红色标注的(1)、(2)两部分,第二部分prot->obj_size比较简单:
struct proto tcp_prot = {
.name = "TCP",
.init =tcp_v4_init_sock,
…..
.obj_size = sizeof(struct tcp_sock),
…..
.compat_getsockopt = compat_tcp_getsockopt,
#endif
};
很明显,分配的是tcp_sock的大小。
然后转回头来看第一部分,这部分是在高速slab中分配空间,那它的大小在哪儿呢,别急,咱们一点点往回推。
在代码中可以看到它使用的是struct proto *prot中自带的slab,好,那看一下这个prot是何方神圣?向上推进入上文提到的:
static int inet_create(struct net *net,struct socket *sock, int protocol)
{
structsock *sk;
structlist_head *p;
struct inet_protosw*answer;
structinet_sock *inet;
struct proto*answer_prot;
………
/*Look for the requested type/protocol pair. */
answer= NULL;
lookup_protocol:
err= -ESOCKTNOSUPPORT;
rcu_read_lock();
list_for_each_rcu(p,&inetsw[sock->type]) {
answer= list_entry(p, struct inet_protosw, list);
/*Check the non-wild match. */
if(protocol == answer->protocol) {
if(protocol != IPPROTO_IP)
break;
}else {
/*Check for the two wild cases. */
if(IPPROTO_IP == protocol) {
protocol= answer->protocol;
break;
}
if(IPPROTO_IP == answer->protocol)
break;
}
err= -EPROTONOSUPPORT;
answer= NULL;
}
if(unlikely(answer == NULL)) {
if(try_loading_module < 2) {
rcu_read_unlock();
/*
* Be more specific, e.g.net-pf-2-proto-132-type-1
*(net-pf-PF_INET-proto-IPPROTO_SCTP-type-SOCK_STREAM)
*/
if(++try_loading_module == 1)
request_module("net-pf-%d-proto-%d-type-%d",
PF_INET, protocol, sock->type);
/*
* Fall back to generic, e.g.net-pf-2-proto-132
* (net-pf-PF_INET-proto-IPPROTO_SCTP)
*/
else
request_module("net-pf-%d-proto-%d",
PF_INET, protocol);
gotolookup_protocol;
}else
gotoout_rcu_unlock;
}
……
sock->ops= answer->ops;
answer_prot =answer->prot;
}
大家可以发现这个分配用的prot是从answer的一个变量中来,那么自然就来到list_for_each_rcu宏及下面的几行代码,这个从数组变量inetsw[sock->type](这个变量定义在net\ipv4\af_inet.c: static struct list_head inetsw[SOCK_MAX];)它的作用是:
将inet_protosw结构体数组注册到inetsw数组中元素的链表中去:这个动作是在
Net\ipv4\af_inet.c:
static int __init inet_init(void)
{
structsk_buff *dummy_skb;
structinet_protosw *q;
structlist_head *r;
intrc = -EINVAL;
BUILD_BUG_ON(sizeof(structinet_skb_parm) > sizeof(dummy_skb->cb));
rc = proto_register(&tcp_prot,1);
if(rc)
gotoout;
rc= proto_register(&udp_prot, 1);
if(rc)
gotoout_unregister_tcp_proto;
rc= proto_register(&raw_prot, 1);
if(rc)
gotoout_unregister_udp_proto;
/*
* TellSOCKET that we are alive...
*/
(void)sock_register(&inet_family_ops);
#ifdef CONFIG_SYSCTL
ip_static_sysctl_init();
#endif
/*
* Addall the base protocols.
*/
if(inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
printk(KERN_CRIT"inet_init: Cannot add ICMP protocol\n");
if(inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
printk(KERN_CRIT"inet_init: Cannot add UDP protocol\n");
if(inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
printk(KERN_CRIT"inet_init: Cannot add TCP protocol\n");
#ifdef CONFIG_IP_MULTICAST
if(inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
printk(KERN_CRIT"inet_init: Cannot add IGMP protocol\n");
#endif
/*Register the socket-side information for inet_create. */
for (r =&inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
INIT_LIST_HEAD(r);
for (q =inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
inet_register_protosw(q);
/*
* Setthe ARP module up
*/
arp_init();
/*
* Setthe IP module up
*/
ip_init();
tcp_v4_init();
/*Setup TCP slab cache for open requests. */
tcp_init();
/*Setup UDP memory threshold */
udp_init();
/*Add UDP-Lite (RFC 3828) */
udplite4_register();
/*
* Setthe ICMP layer up
*/
if(icmp_init() < 0)
panic("Failedto create the ICMP control socket.\n");
/*
* Initialisethe multicast router
*/
#if defined(CONFIG_IP_MROUTE)
if(ip_mr_init())
printk(KERN_CRIT"inet_init: Cannot init ipv4 mroute\n");
#endif
/*
* Initialiseper-cpu ipv4 mibs
*/
if(init_ipv4_mibs())
printk(KERN_CRIT"inet_init: Cannot init ipv4 mibs\n");
ipv4_proc_init();
ipfrag_init();
dev_add_pack(&ip_packet_type);
rc= 0;
out:
returnrc;
out_unregister_udp_proto:
proto_unregister(&udp_prot);
out_unregister_tcp_proto:
proto_unregister(&tcp_prot);
gotoout;
}中完成的。
注意看上面代码中两个标红的for循环,第一个是初始化inetsw数组,第二个则是将下面的inetsw_array加入到inetsw中相应的链表中。
static struct inet_protosw inetsw_array[] =
{
{
.type= SOCK_STREAM,
.protocol= IPPROTO_TCP,
.prot = &tcp_prot,
.ops= &inet_stream_ops,
.capability= -1,
.no_check= 0,
.flags= INET_PROTOSW_PERMANENT |
INET_PROTOSW_ICSK,
},
{
.type= SOCK_DGRAM,
.protocol= IPPROTO_UDP,
.prot= &udp_prot,
.ops= &inet_dgram_ops,
.capability= -1,
.no_check= UDP_CSUM_DEFAULT,
.flags= INET_PROTOSW_PERMANENT,
},
{
.type = SOCK_RAW,
.protocol = IPPROTO_IP, /*wild card */
.prot = &raw_prot,
.ops = &inet_sockraw_ops,
.capability = CAP_NET_RAW,
.no_check = UDP_CSUM_DEFAULT,
.flags = INET_PROTOSW_REUSE,
}
};
而从这里大家又可以看出prot是从tcp_prot 赋值而来的,可是大家可以跳转到在前面看到tcp_prot这个静态变量中,发现并没有对slab进行初始化的定义的地方。那么,找来找去,还是没有找到slab分配的空间大小,在哪儿呢?
看到inet_init(void)函数中第一个标红的函数么?
Net\core\sock.c
int proto_register(struct proto *prot, intalloc_slab)
{
if(alloc_slab) {
prot->slab= kmem_cache_create(prot->name, prot->obj_size, 0,
SLAB_HWCACHE_ALIGN| prot->slab_flags,
NULL);
……
}
千辛万苦啊,总算见到了它。这就说明了,在struct sock*sk的分配过程中其实是分配的struct tcp_sock这个结构体的大小,再加上前面tcp_sockt,sock以及inet_connection_sock三个结构体的关系,大家就非常清晰的看到了,强制转换为什么可以的原因。
另外在inet_create()这个函数还得多说一句,在这个函数的最后:
if(sk->sk_prot->init) {
err= sk->sk_prot->init(sk);
这个init就是在前面的tcp_prot中定义的,可以去看,他调用的函数是static int tcp_v4_init_sock(struct sock *sk),这个函数主要是tcp_sock和inet_connection_sock进行一些初始化。在这个函数所在的文件中,还有一个void__init tcp_v4_init(void)函数,长得差不多,是在前面的proto_register中调用,(也就是说这个要先调用)大家不要和这个混淆。
用了比较长得时间整理这篇文档,非常有裨益。
最后,再次感谢网络上无私奉献资源的网友。