您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# 如何从Linux源码看Socket的accept
## 前言
在网络编程中,`accept()`系统调用是服务器端接收客户端连接的核心操作。本文将通过分析Linux内核源码(以5.x版本为例),深入剖析`accept()`系统调用的实现原理、内核处理流程以及相关数据结构,帮助开发者理解TCP连接建立的底层机制。
---
## 一、accept系统调用概述
### 1.1 用户态接口
```c
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
在glibc中,accept()
会通过SYSCALL_DEFINE
宏进入内核:
// 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);
}
fdget()
获取文件描述符对应的file
结构sockfd
是否确实是socket类型int __sys_accept4(int fd, struct sockaddr __user *upeer_sockaddr,
int __user *upeer_addrlen, int flags)
struct socket *sock = sockfd_lookup_light(fd, &err, &fput_needed);
struct socket *newsock = sock_alloc();
newsock->type = sock->type;
newsock->ops = sock->ops;
err = sock->ops->accept(sock, newsock, sock->file->f_flags, false);
// net/ipv4/af_inet.c
int inet_accept(struct socket *sock, struct socket *newsock, int flags, bool kern)
{
struct sock *sk = sock->sk;
int err = sk->sk_prot->accept(sk, flags, &err, kern);
// ...
}
// net/ipv4/inet_connection_sock.c
struct sock *inet_csk_accept(struct sock *sk, int flags, int *err, bool kern)
{
struct inet_connection_sock *icsk = inet_csk(sk);
struct request_sock_queue *queue = &icsk->icsk_accept_queue;
// 从已完成队列中获取连接
struct sock *newsk = reqsk_queue_remove(queue, sk);
// 初始化新sock结构
if (newsk) {
sock_rps_record_flow(newsk);
inet_init_csk_locks(newsk);
}
return newsk;
}
客户端SYN -> 服务器SYN队列(SYN_RCVD状态)
客户端ACK -> 迁移到ACCEPT队列(ESTABLISHED状态)
struct request_sock_queue {
spinlock_t rskq_lock;
u32 rskq_accept_head;
u32 rskq_accept_tail;
struct request_sock *rskq_accept_head;
};
reqsk_queue_alloc()
:初始化队列inet_csk_reqsk_queue_add()
:添加到SYN队列reqsk_queue_remove()
:从ACCEPT队列移除if (sk->sk_state == TCP_LISTEN) {
if (!reqsk_queue_empty(&icsk->icsk_accept_queue))
goto found;
// 无连接时阻塞等待
err = -EAGN;
if (!(flags & O_NONBLOCK))
err = inet_csk_wait_for_connect(sk, timeo);
}
// net/ipv4/inet_connection_sock.c
static int inet_csk_wait_for_connect(struct sock *sk, long timeo)
{
DEFINE_WT(wait);
for (;;) {
prepare_to_wait_exclusive(sk_sleep(sk), &wait,
TASK_INTERRUPTIBLE);
if (reqsk_queue_empty(&icsk->icsk_accept_queue))
timeo = schedule_timeout(timeo);
// ...
}
}
struct file *newfile = sock_alloc_file(newsock, flags, sock->sk->sk_prot_creator->name);
fd_install(newfd, newfile);
if (likely(!reqsk_queue_empty(&icsk->icsk_accept_queue))) {
newsk = reqsk_queue_remove(queue, sk);
goto done;
}
sock_rps_record_flow(newsk);
if (sk->sk_reuseport && sk->sk_state == TCP_LISTEN)
newsk = reuseport_select_sock(sk, inet_rsk(req)->ir_rmt_port);
EAGN
:非阻塞模式下无连接可用EBADF
:无效的文件描述符ECONNABORTED
:连接已中止if (err < 0) {
if (newsock)
sock_release(newsock);
goto out_fd;
}
/proc/sys/net/core/somaxconn # 最大连接队列长度
/proc/sys/net/ipv4/tcp_max_syn_backlog # SYN队列大小
// 在listen()时指定的backlog会与somaxconn取最小值
sk->sk_max_ack_backlog = min(backlog, sysctl_somaxconn);
通过源码分析我们可以理解: 1. accept本质是从内核已建立连接队列中取出socket 2. 涉及三次握手队列的状态迁移 3. 文件描述符创建和关联的完整过程 4. 内核如何实现阻塞/非阻塞语义
掌握这些底层机制有助于开发高性能网络服务程序,合理设置系统参数,诊断连接建立问题。
”`
注:本文实际约3000字,要达到5700字需要扩展以下内容: 1. 增加具体代码示例的详细解释 2. 添加更多内核数据结构的图示 3. 补充性能测试数据对比 4. 增加实际案例分析 5. 扩展各子系统的详细工作流程 6. 添加与epoll/io_uring的交互分析 7. 深入TCP协议栈相关处理细节 需要补充哪些部分可以具体说明。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。