TCP层accept系统调用的实现分析
Posted wanpengcoder
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TCP层accept系统调用的实现分析相关的知识,希望对你有一定的参考价值。
inet_csk_accept函数实现了tcp协议accept操作,其主要完成的功能是,从已经完成三次握手的队列中取控制块,如果没有已经完成的连接,则需要根据阻塞标记来来区分对待,若非阻塞则直接返回,若阻塞则需要在一定时间范围内阻塞等待;
1 /* 2 * This will accept the next outstanding connection. 3 */ 4 struct sock *inet_csk_accept(struct sock *sk, int flags, int *err, bool kern) 5 { 6 struct inet_connection_sock *icsk = inet_csk(sk); 7 struct request_sock_queue *queue = &icsk->icsk_accept_queue; 8 struct request_sock *req; 9 struct sock *newsk; 10 int error; 11 12 lock_sock(sk); 13 14 /* We need to make sure that this socket is listening, 15 * and that it has something pending. 16 */ 17 error = -EINVAL; 18 19 /* 不是listen状态 */ 20 if (sk->sk_state != TCP_LISTEN) 21 goto out_err; 22 23 /* Find already established connection */ 24 25 /* 还没有已完成的连接 */ 26 if (reqsk_queue_empty(queue)) { 27 28 /* 获取等待时间,非阻塞为0 */ 29 long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); 30 31 /* If this is a non blocking socket don‘t sleep */ 32 error = -EAGAIN; 33 /* 非阻塞立即返回错误 */ 34 if (!timeo) 35 goto out_err; 36 37 /* 等待连接到来 */ 38 error = inet_csk_wait_for_connect(sk, timeo); 39 if (error) 40 goto out_err; 41 } 42 43 /* 从已完成连接队列中移除 */ 44 req = reqsk_queue_remove(queue, sk); 45 46 /* 设置新控制块指针 */ 47 newsk = req->sk; 48 49 /* TCP协议 && fastopen */ 50 if (sk->sk_protocol == IPPROTO_TCP && 51 tcp_rsk(req)->tfo_listener) { 52 spin_lock_bh(&queue->fastopenq.lock); 53 if (tcp_rsk(req)->tfo_listener) { 54 /* We are still waiting for the final ACK from 3WHS 55 * so can‘t free req now. Instead, we set req->sk to 56 * NULL to signify that the child socket is taken 57 * so reqsk_fastopen_remove() will free the req 58 * when 3WHS finishes (or is aborted). 59 */ 60 req->sk = NULL; 61 req = NULL; 62 } 63 spin_unlock_bh(&queue->fastopenq.lock); 64 } 65 out: 66 release_sock(sk); 67 68 /* 释放请求控制块 */ 69 if (req) 70 reqsk_put(req); 71 72 /* 返回找到的连接控制块 */ 73 return newsk; 74 out_err: 75 newsk = NULL; 76 req = NULL; 77 *err = error; 78 goto out; 79 }
如果请求队列中没有已完成握手的连接,并且套接字已经设置了阻塞标记,则需要加入调度队列等待连接的到来,inet_csk_wait_for_connect函数完成了这个功能;
1 /* 2 * Wait for an incoming connection, avoid race conditions. This must be called 3 * with the socket locked. 4 */ 5 static int inet_csk_wait_for_connect(struct sock *sk, long timeo) 6 { 7 struct inet_connection_sock *icsk = inet_csk(sk); 8 DEFINE_WAIT(wait); 9 int err; 10 11 /* 12 * True wake-one mechanism for incoming connections: only 13 * one process gets woken up, not the ‘whole herd‘. 14 * Since we do not ‘race & poll‘ for established sockets 15 * anymore, the common case will execute the loop only once. 16 * 17 * Subtle issue: "add_wait_queue_exclusive()" will be added 18 * after any current non-exclusive waiters, and we know that 19 * it will always _stay_ after any new non-exclusive waiters 20 * because all non-exclusive waiters are added at the 21 * beginning of the wait-queue. As such, it‘s ok to "drop" 22 * our exclusiveness temporarily when we get woken up without 23 * having to remove and re-insert us on the wait queue. 24 */ 25 for (;;) { 26 27 /* 加入等待队列 */ 28 prepare_to_wait_exclusive(sk_sleep(sk), &wait, 29 TASK_INTERRUPTIBLE); 30 release_sock(sk); 31 32 /* 如果为空计算,进行调度 */ 33 if (reqsk_queue_empty(&icsk->icsk_accept_queue)) 34 timeo = schedule_timeout(timeo); 35 sched_annotate_sleep(); 36 lock_sock(sk); 37 err = 0; 38 /* 队列不为空*/ 39 if (!reqsk_queue_empty(&icsk->icsk_accept_queue)) 40 break; 41 err = -EINVAL; 42 /* 连接状态不是LISTEN */ 43 if (sk->sk_state != TCP_LISTEN) 44 break; 45 /* 信号打断 */ 46 err = sock_intr_errno(timeo); 47 if (signal_pending(current)) 48 break; 49 err = -EAGAIN; 50 /* 调度超时 */ 51 if (!timeo) 52 break; 53 } 54 /* 结束等待 */ 55 finish_wait(sk_sleep(sk), &wait); 56 return err; 57 }
reqsk_queue_remove函数完成了将完成握手的控制块从请求队列移除的工作;
1 static inline struct request_sock *reqsk_queue_remove(struct request_sock_queue *queue, 2 struct sock *parent) 3 { 4 struct request_sock *req; 5 6 spin_lock_bh(&queue->rskq_lock); 7 8 /* 找到队列头 */ 9 req = queue->rskq_accept_head; 10 if (req) { 11 /* 减少已连接计数 */ 12 sk_acceptq_removed(parent); 13 /* 头部指向下一节点 */ 14 queue->rskq_accept_head = req->dl_next; 15 16 /* 队列为空 */ 17 if (queue->rskq_accept_head == NULL) 18 queue->rskq_accept_tail = NULL; 19 } 20 spin_unlock_bh(&queue->rskq_lock); 21 return req; 22 }
以上是关于TCP层accept系统调用的实现分析的主要内容,如果未能解决你的问题,请参考以下文章
linux网络协议栈源码分析 - 传输层(TCP连接的终止)
linux网络协议栈源码分析 - 传输层(TCP连接的终止)