分类: Android平台
2015-01-19 23:50:54
在看listen的代码之前.我们也先来看相关的数据结构:
inet_connection_sock它包含了一个icsk_accept_queue的域,这个域是一个request_sock_queue类型,.我们就先来看这个结构:
request_sock_queue也就表示一个request_sock队列.这里我们知道,tcp中分为半连接队列(处于SYN_RECVD状态)和已完成连接队列(处于established状态).这两个一个是刚接到syn,等待三次握手完成,一个是已经完成三次握手,等待accept来读取.
这里每个syn分节到来都会新建一个request_sock结构,并将它加入到listen_sock的request_sock hash表中.然后3次握手完毕后,将它放入到request_sock_queue的rskq_accept_head和rskq_accept_tail队列中.这样当accept的时候就直接从这个队列中读取了.
request_sock的结构就不在这里贴出来了,我们只要知道每一个SYN请求都会新建一个request_sock结构,并将它加入到listen_sock的syn_table哈希表中,然后接收端会发送一个SYN/ACK段给SYN请求端,当SYN请求端将3次握手的最后一个ACK发送给接收端后,并且接收端判断ACK正确,则将request_sock从syn_table哈希表中删除,将request_sock加入到request_sock_queue的rskq_accept_head和rskq_accept_tail队列中,最后的accept系统调用不过是判断accept队列是否存在完成3次请求的request_sock,从这个队列中将request_sock结构释放,然后在BSD层新建一个socket结构,并将它和接收端新建的子sock结构关联起来。
我们可以想到,listen系统调用必然要分配一个listen_sock结构,其实也正如此,inet_listen系统调用最终会调用?inet_csk_listen_start函数,
它的主要工作是新分配一个listen socket,将它加入到inet_connection_sock的icsk_accept_queue域的listen_opt中.然后对当前使用端口进行判断.最终返回,
inet_accept系统调用最终会调用inet_csk_accept,?inet_csk_accept调用reqsk_queue_get_child从accept队列中取一个request_sock,得到request_sock对应的sock,然后将request_scok删除,这里的目的主要是为了得到request_soc的对应的sock,还在BSD层生成相应的sock结构,?reqsk_queue_get_child代码如下(2.6.32内核):
static inline struct sock *reqsk_queue_get_child(struct request_sock_queue *queue,
struct sock *parent)
{
struct request_sock *req = reqsk_queue_remove(queue);
struct sock *child = req->sk;
WARN_ON(child == NULL);
sk_acceptq_removed(parent);
__reqsk_free(req);
return child;
}