socket的创建
int socket(int domain, int type, int protocol);
socket返回了一个fd的值。
当在用户态调用socket创建套接字时发生了什么呢?
fd和socket之间的关系
socket结构体
struct socket { socket_state state; unsigned long flags; const struct proto_ops *ops; struct fasync_struct *fasync_list; struct file *file; struct sock *sk; wait_queue_head_t wait; short type; };
sys_socket
socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
主要看一下创建一个套接字过程中都分配了哪些资源。
// net/socket.c 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); }
可以看出,创建一个套接字需要从两方面入手:
1) 根据family, type, protocol分配socket资源
2) 把socket和VFS关联起来。
先看如何分配socket相关的资源。
1) 分配套接字资源sock_create
分配的过程是:从上层开始,分配上层对应的数据结构,然后根据参数设置函数。然后再分配下层对应的数据结构。
static int __sock_create(int family, int type, int protocol, struct socket **res, int kern) { sock = sock_alloc(); sock->type = type; net_families[family]->create(sock, protocol) }
1) sock = sock_alloc();
分配最上层的代表:socket结构体。
这个结构体和具体的协议无关。
有了这个结构体就可以设置相关的协议选项和buffer了。
另外,这个sock_alloc()同时分配了file的结构体,在内存布局上是相邻的。
2) net_families[family]->create(sock, protocol)
net_families[family]通过套接字domain的值family,选择出一个协议族,然后调用其create函数,同时把socket的结构体和下层的参数protocol传进入。
这里的family为AF_INET,根据net_families的初始化,可以知道net_families[family]->create()对应inet_create()函数。
inet层的构造:inet_create
// net/ipv4/af_inet.c static int inet_create(struct socket *sock, int protocol) { sock->state = SS_UNCONNECTED; // 根据type找到inetsw,这里的type是SOCK_RAW 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; } answer = NULL; } // 通过上面的循环,此处的answer是raw_proto,把协议的ops设置给socket层 sock->ops = answer->ops; answer_prot = answer->prot; answer_no_check = answer->no_check; answer_flags = answer->flags; // 分配sock结构体 sk = sk_alloc(PF_INET, GFP_KERNEL, answer_prot->slab_obj_size, answer_prot->slab); // 把协议的结构体设置给sock sk->sk_prot = answer_prot; // 把sock结构体转换成inet_sock,因为此时的上下文正是inet的create方法 inet = inet_sk(sk); // 如果是协议是raw,则把protocol赋值给inet->num,这个值本身应该是端口的数值,但raw没有端口。 if (SOCK_RAW == sock->type) { inet->num = protocol; if (IPPROTO_RAW == protocol) inet->hdrincl = 1; } // 初始化socket,sock结构体 // 初始化sock结构体中的receive,write,error queue // 初始化sock结构体中的revbuf,sendbuf大小 // 初始化sock中其他的回调:sk_state_change(), sk_data_ready()等 sock_init_data(sock, sk); // 把sock结构体添加到raw协议中的hash中? if (inet->num) { inet->sport = htons(inet->num); /* Add to protocol hash chains. */ sk->sk_prot->hash(sk); } }
sock结构体是用来管理具体协议的结构体,协议不同这个结构体的意义也不同,sock在不同协议的数据块:
在sk_alloc中会根据具体的协议proto的obj_size大小分配一片内存:
struct sock *sk_alloc(int family, gfp_t priority, struct proto *prot, int zero_it) { struct sock *sk = NULL; kmem_cache_t *slab = prot->slab; if (slab != NULL) sk = kmem_cache_alloc(slab, priority); else // 根据协议proto的obj_size分配一片内存!!!proto是在inet_init中初始化的 sk = kmalloc(prot->obj_size, priority); if (sk) { if (zero_it) { memset(sk, 0, prot->obj_size); sk->sk_family = family; /* * See comment in struct sock definition to understand * why we need sk_prot_creator -acme */ sk->sk_prot = sk->sk_prot_creator = prot; sock_lock_init(sk); } if (security_sk_alloc(sk, family, priority)) goto out_free; if (!try_module_get(prot->owner)) goto out_free; } return sk; out_free: if (slab != NULL) kmem_cache_free(slab, sk); else kfree(sk); return NULL; }
2) 把socket和VFS关联起来:sock_map_fd()
int sock_map_fd(struct socket *sock) { fd = get_unused_fd(); struct file *file = get_empty_filp(); file->f_dentry = d_alloc(sock_mnt->mnt_sb->s_root, &this); // 设置d_op file->f_dentry->d_op = &sockfs_dentry_operations; d_add(file->f_dentry, SOCK_INODE(sock)); file->f_vfsmnt = mntget(sock_mnt); file->f_mapping = file->f_dentry->d_inode->i_mapping; // 设置socket结构体中file的指针,关联起socket和file sock->file = file; // 设置f_op file->f_op = SOCK_INODE(sock)->i_fop = &socket_file_ops; file->f_mode = FMODE_READ | FMODE_WRITE; file->f_flags = O_RDWR; file->f_pos = 0; fd_install(fd, file); }
fd,socket,sock之间的关系: