Linux内核网络协议栈4-创建socket(续)
接上篇“创建socket”一文;
?
5、分配sock结构:
本文中的例子会调用inet_family_ops.create方法即inet_create方法完成socket的创建工作;其调用链如下:
net/Socket.c:sys_socket()->sock_create()->__sock_create()->net/ipv4/Af_inet.c:inet_create();
inet_create()主要完成以下几个工作:
1) 设置socket的状态为SS_UNCONNECTED;
sock->state = SS_UNCONNECTED;
?
2) 根据socket的type找到对应的套接字类型:
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;}由于同一type不同protocol的套接字保存在inetsw中的同一链表中,因此需要遍历链表来查找;在上面的例子中,会将protocol重新赋值为answer->protocol,即IPPROTO_TCP,其值为6;sock->ops = answer->ops;answer_prot = answer->prot;// 供后面使用结合例子,sock变量的ops指向inet_stream_ops结构体变量;
sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);其中,answer_prot指向tcp_prot结构体变量;
struct sock *sk_alloc(struct net *net, int family, gfp_t priority, struct proto *prot) {struct sock *sk;sk = sk_prot_alloc(prot, priority | __GFP_ZERO, family);if (sk) {sk->sk_family = family;sk->sk_prot = sk->sk_prot_creator = prot;sock_lock_init(sk);sock_net_set(sk, get_net(net));}return sk;}其中,sk_prot_alloc分配sock结构体变量;由于在inet_init中为不同的套接字分配了高速缓冲区,因此该sock结构体变量会在该缓冲区中分配空间;分配完成后,对其做一些初始化工作:inet = inet_sk(sk);static inline struct inet_sock *inet_sk(const struct sock *sk){return (struct inet_sock *)sk;}这里是根据sk变量得到inet_sock变量的地址;细心的同学可能会问到:inet_sock是什么?之前分配的是sock变量,与inet_sock有什么关系啊?typedef enum {SS_FREE = 0,/* not allocated*/SS_UNCONNECTED,/* unconnected to any socket*/SS_CONNECTING,/* in process of connecting*/SS_CONNECTED,/* connected to socket*/SS_DISCONNECTING/* in process of disconnecting*/} socket_state;b. struct sock:它是网络层的socket;对应有TCP、UDP、RAW三种;enum {TCP_ESTABLISHED = 1,TCP_SYN_SENT,TCP_SYN_RECV,TCP_FIN_WAIT1,TCP_FIN_WAIT2,TCP_TIME_WAIT,TCP_CLOSE,TCP_CLOSE_WAIT,TCP_LAST_ACK,TCP_LISTEN,TCP_CLOSING,/* Now a valid state */TCP_MAX_STATES/* Leave at the end! */};c. struct inet_sock:它是INET域的socket表示,是对struct sock的一个扩展,提供INET域的一些属性,如TTL,组播列表,IP地址,端口等;inet = inet_sk(sk);这里为什么能直接将sock结构体变量强制转化为inet_sock结构体变量呢?只有一种可能,那就是在分配sock结构体变量时,真正分配的是inet_sock或是其他结构体;
static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, int family) {struct sock *sk;struct kmem_cache *slab;slab = prot->slab;if (slab != NULL)sk = kmem_cache_alloc(slab, priority);elsesk = kmalloc(prot->obj_size, priority);return sk;}上面的代码在分配sock结构体时,有两种途径,一是从tcp专用高速缓存中分配;二是从内存直接分配;前者在初始化高速缓存时,指定了结构体大小为prot->obj_size;后者也有指定大小为prot->obj_size,.obj_size= sizeof(struct tcp_sock),也就是说,分配的真实结构体是tcp_sock;由于tcp_sock、inet_connection_sock、inet_sock、sock之间均为0处偏移量,因此可以直接将tcp_sock直接强制转化为inet_sock;这几个结构体间的关系如下:

if (sk->sk_prot->init) { err = sk->sk_prot->init(sk); if (err) sk_common_release(sk);}例子中,这里调用的是tcp_prot的init钩子函数net/ipv4/Tcp_ipv4.c:tcp_v4_init_sock(),它主要是对tcp_sock和inet_connection_sock进行一些初始化;asmlinkage long sys_socket(int family, int type, int protocol){int retval;struct socket *sock;retval = sock_create(family, type, protocol, &sock);if (retval < 0)goto out;retval = sock_map_fd(sock);if (retval < 0)goto out_release;out:/* It may be already another descriptor 8) Not kernel problem. */return retval;out_release:sock_release(sock);return retval;}创建好与socket相关的结构后,需要与文件系统关联,详见sock_map_fd()函数: