accept
<h2>概述</h2>
<p>1、从全连接队列中取出队头的连接,并返回对应的子 sk,并释放 request_sock 对象</p>
<h2>分析</h2>
<p>应用原型:</p>
<pre><code class="language-c">int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);</code></pre>
<p>系统调用代码:</p>
<pre><code class="language-c">// file: net/socket.c
SYSCALL_DEFINE3(accept, int, fd, struct sockaddr __user *, upeer_sockaddr,
int __user *, upeer_addrlen)
{
return sys_accept4(fd, upeer_sockaddr, upeer_addrlen, 0);
}
</code></pre>
<h2>核心</h2>
<p>中间源码没有找到,直接分析核心逻辑吧:</p>
<pre><code class="language-c">// file: net/ipv4/inet_connection_sock.c
/*
* This will accept the next outstanding connection.
*/
struct sock *inet_csk_accept(struct sock *sk, int flags, int *err)
{
struct inet_connection_sock *icsk = inet_csk(sk);
struct request_sock_queue *queue = &amp;icsk-&gt;icsk_accept_queue; // 接收队列
struct sock *newsk;
struct request_sock *req;
int error;
lock_sock(sk);
/* We need to make sure that this socket is listening,
* and that it has something pending.
*/
error = -EINVAL;
if (sk-&gt;sk_state != TCP_LISTEN) // 状态检查
goto out_err;
/* Find already established connection */
if (reqsk_queue_empty(queue)) {
long timeo = sock_rcvtimeo(sk, flags &amp; O_NONBLOCK);
/* If this is a non blocking socket don't sleep */
error = -EAGAIN;
if (!timeo)
goto out_err;
error = inet_csk_wait_for_connect(sk, timeo); // 如果全连接队列没有内容,则根据情况进行超时等待
if (error)
goto out_err;
}
req = reqsk_queue_remove(queue); // 从队头取出一个连接
newsk = req-&gt;sk; // 这是子连接。在第 3 次握手时添加到全连接队列时设置的
sk_acceptq_removed(sk); // 即 sk-&gt;sk_ack_backkog--;
if (sk-&gt;sk_protocol == IPPROTO_TCP &amp;&amp; queue-&gt;fastopenq != NULL) {
spin_lock_bh(&amp;queue-&gt;fastopenq-&gt;lock);
if (tcp_rsk(req)-&gt;listener) {
/* We are still waiting for the final ACK from 3WHS
* so can't free req now. Instead, we set req-&gt;sk to
* NULL to signify that the child socket is taken
* so reqsk_fastopen_remove() will free the req
* when 3WHS finishes (or is aborted).
*/
req-&gt;sk = NULL;
req = NULL;
}
spin_unlock_bh(&amp;queue-&gt;fastopenq-&gt;lock);
}
out:
release_sock(sk);
if (req)
__reqsk_free(req); // 释放 request_sock 对象
return newsk; // 返回子 sk
out_err:
newsk = NULL;
req = NULL;
*err = error;
goto out;
}
EXPORT_SYMBOL(inet_csk_accept);
</code></pre>