浏览 2556 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-06-17
最后修改:2011-06-25
接上篇“创建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; 3) 使用匹配的协议族操作集初始化socket; sock->ops = answer->ops; answer_prot = answer->prot;// 供后面使用结合例子,sock变量的ops指向inet_stream_ops结构体变量; 4) 分配sock结构体变量net/Socket.c:sys_socket()->sock_create()->__sock_create()->net/ipv4/Af_inet.c:inet_create()->net/core/Sock.c:sk_alloc(): 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结构体变量会在该缓冲区中分配空间;分配完成后,对其做一些初始化工作: i) 初始化sk变量的sk_prot和sk_prot_creator; ii) 初始化sk变量的等待队列; iii) 设置net空间结构,并增加引用计数; 6、建立socket结构与sock结构的关系: 1) socket, sock, inet_sock, tcp_sock的关系 创建完sk变量后,回到inet_create函数中: 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有什么关系啊? a. struct socket:这个是基本的BSD socket,应用程序通过系统调用开始创建的socket都是该结构体,它是基于虚拟文件系统创建出来的; 类型主要有三种,即流式、数据报、原始套接字协议; 其状态比较粗粒度,如下: 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三种; 其状态相比socket结构更精细: 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地址,端口等; d. struct raw_socket:它是RAW协议的一个socket表示,是对struct inet_sock的扩展,它要处理与ICMP相关的内容; e. sturct udp_sock:它是UDP协议的socket表示,是对struct inet_sock的扩展; f. struct inet_connection_sock:它是所有面向连接的socket表示,是对struct inet_sock的扩展; g. struct tcp_sock:它是TCP协议的socket表示,是对struct inet_connection_sock的扩展,主要增加滑动窗口,拥塞控制一些TCP专用属性; h. struct inet_timewait_sock:它是网络层用于超时控制的socket表示; i. struct tcp_timewait_sock:它是TCP协议用于超时控制的socket表示; 上面简单介绍了一下内核中不同的socket相关的结构体的作用;回到inet_create函数中: inet = inet_sk(sk);这里为什么能直接将sock结构体变量强制转化为inet_sock结构体变量呢?只有一种可能,那就是在分配sock结构体变量时,真正分配的是inet_sock或是其他结构体; 我们回到分配sock结构体的那块代码(参考前面的5.4小节:net/core/Sock.c): 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); else sk = kmalloc(prot->obj_size, priority); return sk; }上面的代码在分配sock结构体时,有两种途径,一是从tcp专用高速缓存中分配;二是从内存直接分配;前者在初始化高速缓存时,指定了结构体大小为prot->obj_size;后者也有指定大小为prot->obj_size, 根据这点,我们看下tcp_prot变量中的obj_size(net/ipv4/Tcp_ipv4.c): .obj_size = sizeof(struct tcp_sock),也就是说,分配的真实结构体是tcp_sock;由于tcp_sock、inet_connection_sock、inet_sock、sock之间均为0处偏移量,因此可以直接将tcp_sock直接强制转化为inet_sock;这几个结构体间的关系如下: 2) 建立socket, sock的关系 创建完sock变量之后,便是初始化sock结构体,并建立sock与socket之间的引用关系;调用链如下: net/Socket.c:sys_socket()->sock_create()->__sock_create()->net/ipv4/Af_inet.c:inet_create()->net/core/Sock.c:sock_init_data(): 该函数主要工作是: a. 初始化sock结构的缓冲区、队列等; b. 初始化sock结构的状态为TCP_CLOSE; c. 建立socket与sock结构的相互引用关系; 7、使用tcp协议初始化sock: inet_create()函数最后,通过相应的协议来初始化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进行一些初始化; 8、socket与文件系统关联: 回到net/Socket.c:sys_socket()函数: 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()函数: 1) 申请文件描述符,并分配file结构和目录项结构; 2) 关联socket相关的文件操作函数表和目录项操作函数表; 3) 将file->private_date指向socket; socket与文件系统关联后,以后便可以通过文件系统read/write对socket进行操作了; 小结: 1、socket库函数通过内核创建socket,并初始化其状态为TCP_CLOSE; 2、创建完后,与文件系统关联,其文件一般位于/proc/$pid/fd/目录下; 3、应用程序可以通过文件对socket进行操作; 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |