在现代网络服务中,如 Web 服务器、即时通讯、API 网关等,处理成千上万的并发连接是关键。本章将带你深入理解 Linux 下的高并发服务器设计模式,重点介绍 epoll
的使用与优势。
🚦 一、服务器并发模型概述
模型 | 特点 | 缺点 |
---|---|---|
多进程模型 | 每个连接一个进程 | 进程开销大,切换频繁 |
多线程模型 | 每个连接一个线程 | 上下文切换,线程竞争资源 |
I/O 多路复用 | 单线程监控多个连接(如 select 、poll ) | select 有最大连接数限制 |
epoll 模型 | 高效、无连接数限制、支持边沿触发 | 编程稍复杂,但性能优越 |
🔄 二、三种 I/O 多路复用模型对比
模型 | 支持连接数 | 触发机制 | 性能 | 可移植性 |
---|---|---|---|---|
select | 1024(默认) | 水平触发 | 差 | 高(跨平台) |
poll | 无限制 | 水平触发 | 中 | 高 |
epoll | 无限制 | 水平 & 边沿触发 | 高(大并发) | Linux 专用 |
🔍 三、Epoll 的工作原理
epoll
是 Linux 2.6 内核引入的 I/O 多路复用机制。
-
epoll_create()
:创建 epoll 实例 -
epoll_ctl()
:添加/修改/删除感兴趣的事件 -
epoll_wait()
:等待事件发生
⚙️ 四、Epoll 工作模式示意图
+-------------------------+ | 应用程序 | +-----------+-------------+ | epoll_create() | epoll_ctl(注册fd) | epoll_wait() | +----------------+----------------+ | | 有事件发生 超时/阻塞 | | 执行read/write等操作 重试/下一轮循环
🧪 五、Epoll 服务器示例(C 语言)
🔧 基本代码框架(TCP Echo Server):
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#define MAX_EVENTS 1024
#define PORT 8888
int set_nonblocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
int main() {
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server_addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = INADDR_ANY,
.sin_port = htons(PORT)
};
bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
listen(listen_fd, 128);
set_nonblocking(listen_fd);
int epfd = epoll_create(1);
struct epoll_event ev, events[MAX_EVENTS];
ev.events = EPOLLIN;
ev.data.fd = listen_fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &ev);
while (1) {
int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
for (int i = 0; i < nfds; i++) {
if (events[i].data.fd == listen_fd) {
int conn_fd = accept(listen_fd, NULL, NULL);
set_nonblocking(conn_fd);
ev.events = EPOLLIN | EPOLLET; // 边缘触发
ev.data.fd = conn_fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, conn_fd, &ev);
} else {
char buf[512];
int len = read(events[i].data.fd, buf, sizeof(buf));
if (len > 0) {
write(events[i].data.fd, buf, len); // echo
} else {
close(events[i].data.fd);
}
}
}
}
}
📌 六、水平触发 vs 边沿触发
类型 | 说明 |
---|---|
水平触发 | 默认方式,只要缓冲区未读完,epoll_wait 每次返回 |
边沿触发 | 一次事件后不再通知,需一次性读完缓冲区,效率更高 |
建议:高并发时推荐使用 EPOLLET(边沿触发),结合 非阻塞 I/O 使用。
🔐 七、与线程池结合实现高性能架构
高性能服务器常用架构:
主线程负责监听连接 + Epoll 收发消息 子线程池负责处理业务逻辑(数据库、磁盘等)
优点:
-
主线程专注于 I/O,高效响应
-
子线程解耦耗时任务,提升吞吐量
🧠 八、本章小结
-
epoll
是 Linux 高并发服务器编程的核心技术; -
配合边沿触发与非阻塞 I/O 使用效果最佳;
-
与线程池搭配,可实现百万并发连接架构的雏形。