引言:从石器时代到工业革命——网络编程的进化论
2003年,Dan Kegel发布《The C10K Problem》时,服务器还在用fork()
应对并发连接。Apache采用"一连接一线程"模式,当并发突破1万,线程切换开销吞噬了90%的CPU资源。这如同用马车拉高铁——传统阻塞I/O在网络海啸前彻底失灵。
而Reactor模型配合epoll的诞生,让Nginx在2GB内存的机器上轻松处理百万级并发连接。本文将揭秘这套高并发引擎的运作机制,从理论到实战,手把手构建属于你的高性能网络框架!
高性能网络框架的核心价值:
- 高吞吐量:支持大量并发连接,提升系统的吞吐量。
- 低延迟:通过高效的事件处理机制,减少响应延迟。
- 可扩展性:支持水平扩展,适应业务增长的需求。
一、I/O多路复用:从select/poll到epoll的量子跃迁
epoll的工作原理
epoll是Linux系统提供的I/O多路复用接口,用于高效地监控多个文件描述符(如网络套接字)的状态变化。其核心原理包括:
- 注册事件:将需要监控的文件描述符注册到epoll实例中。
- 等待事件:阻塞等待I/O事件的发生。
- 处理事件:根据发生的事件,执行相应的处理逻辑。
2epoll的优势
- 高效处理大量并发连接:相比select和poll,epoll在处理大量文件描述符时性能更优。
- 边缘触发模式(EPOLLET):仅在事件状态发生变化时触发通知,减少不必要的事件处理。
- 支持非阻塞I/O:通过设置文件描述符为非阻塞模式,避免阻塞操作。
理论基石:文件描述符风暴
传统阻塞I/O的瓶颈在于同步等待:当read()
调用时,线程被挂起直到数据到达。C10K问题本质是FD(文件描述符)管理效率问题。
方案 | 时间复杂度 | 缺点 |
---|---|---|
select | O(n) | 1024 FD限制 |
poll | O(n) | 大量FD时性能骤降 |
epoll | O(1) | 无FD上限,高效就绪列表 |
实战示例:使用epoll实现高效的I/O多路复用
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#define MAX_EVENTS 10
#define BUFFER_SIZE 1024
void handle_client(int client_fd) {
char buffer[BUFFER_SIZE];
int read_size = read(client_fd, buffer, BUFFER_SIZE - 1);
if (read_size > 0) {
printf("Received data: %s\n", buffer);
// 发送响应
char response[] = "Hello, Client!";
send(client_fd, response, strlen(response), 0);
}
}
int main() {
int server_fd, epoll_fd;
struct sockaddr_in server_addr;
// 创建套接字
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("socket");
exit(1);
}
// 设置套接字选项
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 绑定地址
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8080);
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
exit(1);
}
// 监听连接
listen(server_fd, 3);
// 创建epoll实例
epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
perror("epoll_create1");
exit(1);
}
// 将服务器套接字添加到epoll监控列表
struct epoll_event event;
event.events = EPOLLIN | EPOLLET;
event.data.fd = server_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1) {
perror("epoll_ctl");
exit(1);
}
// 事件循环
struct epoll_event events[MAX_EVENTS];
while (1) {
int event_count = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (event_count == -1) {
perror("epoll_wait");
exit(1);
}
for (int i = 0; i < event_count; i++) {
int fd = events[i].data.fd;
if (events[i].events & EPOLLIN) {
if (fd == server_fd) {
// 处理新的连接
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd == -1) {
perror("accept");
continue;
}
// 将新连接添加到epoll监控列表
struct epoll_event client_event;
client_event.events = EPOLLIN | EPOLLET;
client_event.data.fd = client_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &client_event) == -1) {
perror("epoll_ctl");
close(client_fd);
continue;
}
} else {
// 处理数据读取
handle_client(fd);
}
}
}
}
// 关闭资源
close(epoll_fd);
close(server_fd);
return 0;
}
epoll的三位一体
#include <stdio.h> // 标准输入输出函数
#include <stdlib.h> // 标准库函数(内存分配、系统命令等)
#include <string.h> // 字符串处理函数
#include <unistd.h> // UNIX标准函数(文件操作、进程控制等)
#include <fcntl.h> // 文件控制选项(非阻塞IO设置)
#include <sys/epoll.h> // epoll相关函数和数据结构
#include <sys/socket.h> // 套接字编程接口
#include <netinet/in.h> // 互联网地址族定义
#include <arpa/inet.h> // 互联网地址转换函数
#define MAX_EVENTS 64 // epoll最大事件数
#define PORT 8080 // 服务器监听端口
#define BUFFER_SIZE 1024 // 数据缓冲区大小
// 设置文件描述符为非阻塞模式
void setnonblocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0); // 获取当前文件状态标志
fcntl(fd, F_SETFL, flags | O_NONBLOCK); // 添加非阻塞标志
}
int main() {
// 创建TCP套接字
int sockfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (sockfd == -1) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
// 配置服务器地址
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; // IPv4地址族
addr.sin_addr.s_addr = INADDR_ANY; // 监听所有网络接口
addr.sin_port = htons(PORT); // 端口号(主机字节序转网络字节序)
// 绑定套接字到端口
if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("bind failed");
close(sockfd);
exit(EXIT_FAILURE);
}
// 开始监听(设置等待连接队列最大长度)
if (listen(sockfd, SOMAXCONN) == -1) {
perror("listen failed");
close(sockfd);
exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n", PORT);
// 创建epoll实例
// 参数0: 已废弃,内核>2.6.8后自动忽略,保持兼容性
int epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
perror("epoll_create1 failed");
close(sockfd);
exit(EXIT_FAILURE);
}
// 配置epoll事件结构
struct epoll_event ev;
// 关注可读事件 | 边缘触发模式 | 异常断开检测
ev.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
// 关联监听套接字
ev.data.fd = sockfd;
// 将监听套接字加入epoll监控
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &ev) == -1) {
perror("epoll_ctl add sockfd failed");
close(sockfd);
close(epoll_fd);
exit(EXIT_FAILURE);
}
// 事件循环
while (1) {
// 等待事件发生(无限等待)
struct epoll_event events[MAX_EVENTS];
int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (n == -1) {
perror("epoll_wait error");
break;
}
// 处理所有就绪事件
for (int i = 0; i < n; i++) {
// 异常断开处理
if (events[i].events & EPOLLRDHUP) {
printf("Client disconnected (fd=%d)\n", events[i].data.fd);
close(events[i].data.fd);
continue;
}
// 监听套接字事件(新连接到达)
if (events[i].data.fd == sockfd) {
// 接受所有待处理连接
while (1) {
struct sockaddr_in client_addr;
socklen_t addrlen = sizeof(client_addr);
// 接受新连接(非阻塞模式)
int client_fd = accept4(sockfd, (struct sockaddr*)&client_addr,
&addrlen, SOCK_NONBLOCK);
if (client_fd == -1) {
// 当没有更多连接时退出循环
if (errno == EAGAIN || errno == EWOULDBLOCK)
break;
perror("accept failed");
break;
}
// 打印客户端信息
char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip));
printf("New connection from %s:%d (fd=%d)\n",
client_ip, ntohs(client_addr.sin_port), client_fd);
// 配置客户端事件
struct epoll_event client_ev;
// 关注可读事件 | 边缘触发模式 | 异常断开检测
client_ev.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
client_ev.data.fd = client_fd;
// 将新连接加入epoll监控
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &client_ev) == -1) {
perror("epoll_ctl add client_fd failed");
close(client_fd);
}
}
}
// 客户端数据到达
else if (events[i].events & EPOLLIN) {
int client_fd = events[i].data.fd;
char buffer[BUFFER_SIZE];
// ET模式需要循环读取直到数据读完
while (1) {
ssize_t count = read(client_fd, buffer, sizeof(buffer) - 1);
if (count == -1) {
// 当数据读取完毕时退出循环
if (errno == EAGAIN || errno == EWOULDBLOCK)
break;
perror("read error");
close(client_fd);
break;
}
// 客户端关闭连接
else if (count == 0) {
printf("Client closed connection (fd=%d)\n", client_fd);
close(client_fd);
break;
}
// 处理接收到的数据
buffer[count] = '\0';
printf("Received from fd=%d: %s\n", client_fd, buffer);
// 简易回显服务
write(client_fd, buffer, count);
}
}
}
}
// 清理资源
close(sockfd);
close(epoll_fd);
return 0;
}
边缘触发(ET) vs 水平触发(LT):
-
ET:状态变化时触发一次,需非阻塞I/O+循环读写
-
LT:就绪状态持续通知,可能引发多次唤醒
验证示例:用epoll实现端口扫描器,对比ET/LT模式下CPU占用率差异
二、Reactor模型解剖:事件驱动的交响乐团
Reactor模型的基本概念
Reactor模型是一种用于处理I/O多路复用的并发处理模型。其核心思想是通过事件驱动的方式,将I/O操作的处理与线程解耦,从而提高系统的吞吐量和响应速度。
2. Reactor模型的组成部分
- 事件检测器:负责检测I/O事件的发生。
- 事件分发器:将检测到的事件分发给对应的处理函数。
- 事件处理器:具体处理I/O事件,执行业务逻辑。
3. Reactor模型的优势
- 高效处理大量并发连接:通过单线程处理所有I/O事件,避免线程切换的开销。
- 良好的扩展性:支持水平扩展,适应业务增长的需求。
- 低延迟:通过事件驱动的方式,快速响应I/O操作。
模型核心四重奏
-
Reactor:事件循环调度中心
-
Demultiplexer:epoll实现的事件多路分发器
-
EventHandler:处理I/O事件的抽象接口
-
ConcreteHandler:处理具体业务逻辑
实战示例:实现一个简单的Reactor模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#define MAX_EVENTS 10
#define BUFFER_SIZE 1024
typedef struct {
int fd;
char buffer[BUFFER_SIZE];
size_t buffer_len;
} Connection;
void handle_read(Connection *connection) {
char buffer[BUFFER_SIZE];
int read_size = read(connection->fd, buffer, BUFFER_SIZE - 1);
if (read_size > 0) {
printf("Received data: %s\n", buffer);
// 将接收到的数据存储到连接的缓冲区
memcpy(connection->buffer + connection->buffer_len, buffer, read_size);
connection->buffer_len += read_size;
}
}
void handle_write(Connection *connection) {
if (connection->buffer_len > 0) {
int write_size = write(connection->fd, connection->buffer, connection->buffer_len);
if (write_size > 0) {
// 移除已发送的数据
memmove(connection->buffer, connection->buffer + write_size, connection->buffer_len - write_size);
connection->buffer_len -= write_size;
}
}
}
int main() {
int server_fd, epoll_fd;
struct sockaddr_in server_addr;
// 创建套接字
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("socket");
exit(1);
}
// 设置套接字选项
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 绑定地址
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8080);
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
exit(1);
}
// 监听连接
listen(server_fd, 3);
// 创建epoll实例
epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
perror("epoll_create1");
exit(1);
}
// 将服务器套接字添加到epoll监控列表
struct epoll_event event;
event.events = EPOLLIN | EPOLLET;
event.data.fd = server_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1) {
perror("epoll_ctl");
exit(1);
}
// 事件循环
struct epoll_event events[MAX_EVENTS];
while (1) {
int event_count = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (event_count == -1) {
perror("epoll_wait");
exit(1);
}
for (int i = 0; i < event_count; i++) {
int fd = events[i].data.fd;
if (events[i].events & EPOLLIN) {
// 处理读事件
if (fd == server_fd) {
// 处理新的连接
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd == -1) {
perror("accept");
continue;
}
// 将新连接添加到epoll监控列表
struct epoll_event client_event;
client_event.events = EPOLLIN | EPOLLET;
client_event.data.fd = client_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &client_event) == -1) {
perror("epoll_ctl");
close(client_fd);
continue;
}
} else {
// 处理数据读取
Connection *connection = (Connection *)events[i].data.ptr;
handle_read(connection);
}
}
if (events[i].events & EPOLLOUT) {
// 处理写事件
Connection *connection = (Connection *)events[i].data.ptr;
handle_write(connection);
}
}
}
// 关闭资源
close(epoll_fd);
close(server_fd);
return 0;
}
性能倍增秘籍
#include <vector>
#include <memory>
#include <chrono>
#include <sys/epoll.h> // 使用epoll作为多路复用实现
#include <unistd.h> // 文件描述符操作
// 前向声明
class EventHandler;
// 事件类型枚举
enum EventType {
READ_EVENT = EPOLLIN, // 可读事件
WRITE_EVENT = EPOLLOUT, // 可写事件
ERROR_EVENT = EPOLLERR, // 错误事件
HUP_EVENT = EPOLLHUP // 挂起事件
};
// 多路复用器抽象类(Demultiplexer)
class Demultiplexer {
public:
virtual ~Demultiplexer() = default;
// 等待事件发生
virtual int wait(std::vector<struct epoll_event>& events, int timeout) = 0;
// 注册/修改/删除事件监控
virtual void control(int fd, EventHandler* handler, int ops) = 0;
};
// 事件处理器接口(EventHandler)
class EventHandler {
public:
virtual ~EventHandler() = default;
// 获取关联的文件描述符
virtual int get_handle() const = 0;
// 处理事件
virtual void handle_event(EventType et) = 0;
};
// 基于epoll的具体多路复用器实现
class EpollDemultiplexer : public Demultiplexer {
public:
EpollDemultiplexer() {
epoll_fd_ = epoll_create1(0); // 创建epoll实例
if (epoll_fd_ == -1) {
throw std::runtime_error("Failed to create epoll instance");
}
}
~EpollDemultiplexer() override {
close(epoll_fd_); // 关闭epoll文件描述符
}
int wait(std::vector<struct epoll_event>& events, int timeout) override {
// 等待事件发生
int num_events = epoll_wait(
epoll_fd_,
events.data(),
static_cast<int>(events.size()),
timeout
);
if (num_events == -1 && errno != EINTR) {
throw std::runtime_error("epoll_wait error");
}
return num_events; // 返回就绪事件数量
}
void control(int fd, EventHandler* handler, int ops) override {
struct epoll_event ev;
ev.events = ops; // 设置关注的事件类型
ev.data.ptr = handler; // 关联事件处理器
// 根据操作类型执行相应epoll_ctl操作
int op = (ops == 0) ? EPOLL_CTL_DEL : EPOLL_CTL_MOD;
if (epoll_ctl(epoll_fd_, op, fd, &ev) == -1) {
if (errno == ENOENT) {
op = EPOLL_CTL_ADD; // 首次添加
if (epoll_ctl(epoll_fd_, op, fd, &ev) == -1) {
throw std::runtime_error("epoll_ctl add failed");
}
} else {
throw std::runtime_error("epoll_ctl mod/del failed");
}
}
}
private:
int epoll_fd_; // epoll文件描述符
};
// Reactor核心类
class Reactor {
public:
Reactor() : stop_(false) {
demultiplexer_ = std::make_unique<EpollDemultiplexer>();
}
// 注册事件处理器
void register_handler(EventHandler* handler, EventType et) {
demultiplexer_->control(
handler->get_handle(),
handler,
static_cast<int>(et)
);
}
// 移除事件处理器
void remove_handler(EventHandler* handler) {
demultiplexer_->control(
handler->get_handle(),
nullptr,
0
);
}
// 启动事件循环
void run() {
constexpr int MAX_EVENTS = 64;
std::vector<struct epoll_event> events(MAX_EVENTS);
// 主事件循环
while (!stop_) {
// 计算超时时间(毫秒)
// -1表示无限等待,0表示立即返回
int timeout = calculate_timeout();
// 等待事件发生
int num_events = demultiplexer_->wait(events, timeout);
// 处理所有就绪事件
for (int i = 0; i < num_events; ++i) {
// 获取关联的事件处理器
auto* handler = static_cast<EventHandler*>(events[i].data.ptr);
// 确定具体事件类型
EventType et = static_cast<EventType>(events[i].events);
// 委托给处理器处理事件
handler->handle_event(et);
}
}
}
// 停止事件循环
void stop() { stop_ = true; }
private:
// 计算下次等待的超时时间
int calculate_timeout() const {
// 实际实现中可能考虑:
// 1. 定时器队列中最接近的到期时间
// 2. 系统负载情况
// 3. 是否处于空闲状态
return -1; // 默认无限等待
}
std::unique_ptr<Demultiplexer> demultiplexer_; // 多路复用器
bool stop_; // 停止标志
};
// 示例:TCP连接处理器
class TcpConnectionHandler : public EventHandler {
public:
TcpConnectionHandler(int fd) : fd_(fd) {}
int get_handle() const override { return fd_; }
void handle_event(EventType et) override {
if (et & READ_EVENT) {
// 处理读事件
char buffer[1024];
ssize_t n = read(fd_, buffer, sizeof(buffer));
if (n > 0) {
// 处理接收到的数据
process_data(buffer, n);
} else if (n == 0) {
// 连接关闭
handle_close();
} else {
// 读错误
handle_error();
}
}
if (et & WRITE_EVENT) {
// 处理写事件
handle_write();
}
if (et & (ERROR_EVENT | HUP_EVENT)) {
// 处理错误或连接挂起
handle_error();
}
}
private:
void process_data(const char* data, size_t len) {
// 实际业务逻辑处理
}
void handle_write() {
// 处理可写事件
}
void handle_close() {
// 清理连接资源
close(fd_);
}
void handle_error() {
// 处理错误情况
close(fd_);
}
int fd_; // 关联的套接字描述符
};
// 使用示例
int main() {
Reactor reactor;
// 示例:创建一个监听套接字处理器(需实际实现)
// auto acceptor = std::make_shared<AcceptorHandler>();
// reactor.register_handler(acceptor.get(), READ_EVENT);
// 启动事件循环
reactor.run();
return 0;
}
惊群效应解决方案:
-
SO_REUSEPORT:内核级负载均衡
-
EPOLLEXCLUSIVE:避免多个进程同时唤醒
验证示例:实现多Reactor线程模型,对比单Reactor性能提升
三、epoll实战:从零构建百万并发框架
核心数据结构设计
// 事件处理器基类
typedef struct {
int fd;
void (*read_callback)(void* arg);
void (*write_callback)(void* arg);
void* arg; // 携带业务数据
} EventHandler;
// Reactor主体
typedef struct {
int epoll_fd;
EventHandler** handlers; // FD到处理器的映射
pthread_mutex_t lock;
} Reactor;
事件注册机制
#include <stdio.h> // 标准输入输出
#include <stdlib.h> // 内存分配、系统函数
#include <string.h> // 字符串处理
#include <unistd.h> // 文件描述符操作
#include <fcntl.h> // 文件控制选项
#include <sys/epoll.h> // epoll 接口
#include <sys/socket.h> // 套接字接口
#include <pthread.h> // 线程互斥锁
#include <errno.h> // 错误码定义
#define MAX_EVENTS 64 // epoll 最大事件数
#define MAX_FDS 1024 // 最大文件描述符数量
// 事件处理器结构体(基类)
typedef struct {
int fd; // 关联的文件描述符
void (*read_callback)(void* arg); // 读事件回调函数指针
void (*write_callback)(void* arg); // 写事件回调函数指针
void* arg; // 传递给回调函数的业务数据
} EventHandler;
// Reactor 核心结构体
typedef struct {
int epoll_fd; // epoll 实例的文件描述符
EventHandler* handlers[MAX_FDS]; // FD到处理器的映射数组
pthread_mutex_t lock; // 线程互斥锁(保护handlers数组)
} Reactor;
// 初始化 Reactor
Reactor* reactor_init() {
Reactor* reactor = (Reactor*)malloc(sizeof(Reactor));
if (!reactor) {
perror("Failed to allocate reactor");
return NULL;
}
// 创建 epoll 实例
reactor->epoll_fd = epoll_create1(0);
if (reactor->epoll_fd == -1) {
perror("Failed to create epoll instance");
free(reactor);
return NULL;
}
// 初始化 handlers 数组(全部置NULL)
memset(reactor->handlers, 0, sizeof(reactor->handlers));
// 初始化互斥锁
if (pthread_mutex_init(&reactor->lock, NULL) != 0) {
perror("Failed to initialize mutex");
close(reactor->epoll_fd);
free(reactor);
return NULL;
}
return reactor;
}
// 销毁 Reactor
void reactor_destroy(Reactor* reactor) {
if (!reactor) return;
// 关闭所有注册的文件描述符(实际项目可能需要更精细的管理)
for (int i = 0; i < MAX_FDS; i++) {
if (reactor->handlers[i]) {
close(i); // 文件描述符即数组下标
}
}
// 关闭 epoll 实例
close(reactor->epoll_fd);
// 销毁互斥锁
pthread_mutex_destroy(&reactor->lock);
// 释放 Reactor 内存
free(reactor);
}
// 向 Reactor 注册事件处理器
int reactor_register(Reactor* reactor, EventHandler* handler, int events) {
if (!reactor || !handler || handler->fd >= MAX_FDS || handler->fd < 0) {
return -1; // 参数检查失败
}
// 准备 epoll 事件结构
struct epoll_event ev;
ev.events = events; // 设置关注的事件类型(EPOLLIN | EPOLLOUT 等)
ev.data.fd = handler->fd; // 关联文件描述符
// 线程安全操作(保护handlers数组)
pthread_mutex_lock(&reactor->lock);
// 添加/修改 epoll 监控
int op = reactor->handlers[handler->fd] ? EPOLL_CTL_MOD : EPOLL_CTL_ADD;
if (epoll_ctl(reactor->epoll_fd, op, handler->fd, &ev) == -1) {
perror("epoll_ctl operation failed");
pthread_mutex_unlock(&reactor->lock);
return -1;
}
// 保存处理器到映射数组
reactor->handlers[handler->fd] = handler;
pthread_mutex_unlock(&reactor->lock);
return 0;
}
// 从 Reactor 注销事件处理器
int reactor_unregister(Reactor* reactor, int fd) {
if (!reactor || fd >= MAX_FDS || fd < 0) {
return -1; // 参数检查失败
}
pthread_mutex_lock(&reactor->lock);
// 从 epoll 移除监控
if (epoll_ctl(reactor->epoll_fd, EPOLL_CTL_DEL, fd, NULL) == -1) {
perror("epoll_ctl delete failed");
pthread_mutex_unlock(&reactor->lock);
return -1;
}
// 清除处理器映射
reactor->handlers[fd] = NULL;
pthread_mutex_unlock(&reactor->lock);
return 0;
}
// Reactor 事件循环
void reactor_run(Reactor* reactor) {
if (!reactor) return;
struct epoll_event events[MAX_EVENTS];
printf("Reactor started running...\n");
while (1) {
// 等待事件发生(无限等待)
int nfds = epoll_wait(reactor->epoll_fd, events, MAX_EVENTS, -1);
if (nfds == -1) {
// 被信号中断则继续等待
if (errno == EINTR) continue;
perror("epoll_wait error");
break;
}
// 处理所有就绪事件
for (int i = 0; i < nfds; i++) {
int fd = events[i].data.fd;
int revents = events[i].events;
// 线程安全获取处理器(防止处理过程中被注销)
pthread_mutex_lock(&reactor->lock);
EventHandler* handler = reactor->handlers[fd];
pthread_mutex_unlock(&reactor->lock);
if (!handler) continue; // 处理器已被注销
// 处理读事件(且确实有读回调)
if ((revents & EPOLLIN) && handler->read_callback) {
handler->read_callback(handler->arg);
}
// 处理写事件(且确实有写回调)
if ((revents & EPOLLOUT) && handler->write_callback) {
handler->write_callback(handler->arg);
}
// 处理错误事件(EPOLLERR | EPOLLHUP)
if (revents & (EPOLLERR | EPOLLHUP)) {
if (handler->read_callback) {
handler->read_callback(handler->arg); // 通常用读回调处理错误
}
reactor_unregister(reactor, fd); // 自动注销
close(fd); // 关闭文件描述符
}
}
}
}
/********************** 示例使用代码 **********************/
// 业务数据示例结构
typedef struct {
int fd;
char buffer[1024];
} ClientData;
// 读事件回调示例
void on_client_read(void* arg) {
ClientData* data = (ClientData*)arg;
ssize_t n = read(data->fd, data->buffer, sizeof(data->buffer) - 1);
if (n > 0) {
data->buffer[n] = '\0';
printf("Received from fd=%d: %s\n", data->fd, data->buffer);
// 简易回显
write(data->fd, data->buffer, n);
} else if (n == 0) {
printf("Client fd=%d disconnected\n", data->fd);
close(data->fd); // Reactor会自动注销
} else {
perror("read error");
close(data->fd); // Reactor会自动注销
}
}
// 写事件回调示例(略)
void on_client_write(void* arg) {
// 实际项目中处理写完成事件
}
int main() {
// 创建并初始化 Reactor
Reactor* reactor = reactor_init();
if (!reactor) {
return 1;
}
// 示例:创建监听套接字(实际项目需要完整实现)
int listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (listen_fd == -1) {
perror("socket creation failed");
reactor_destroy(reactor);
return 1;
}
// 绑定和监听(简略实现)
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(8080);
if (bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr)) {
perror("bind failed");
close(listen_fd);
reactor_destroy(reactor);
return 1;
}
if (listen(listen_fd, SOMAXCONN)) {
perror("listen failed");
close(listen_fd);
reactor_destroy(reactor);
return 1;
}
// 创建监听套接字的处理器
EventHandler* acceptor = (EventHandler*)malloc(sizeof(EventHandler));
ClientData* acceptor_data = (ClientData*)malloc(sizeof(ClientData));
acceptor->fd = listen_fd;
acceptor->read_callback = on_client_read; // 接受新连接也用读回调
acceptor->write_callback = NULL;
acceptor->arg = acceptor_data;
acceptor_data->fd = listen_fd;
// 注册到 Reactor(关注读事件)
if (reactor_register(reactor, acceptor, EPOLLIN) != 0) {
fprintf(stderr, "Failed to register acceptor handler\n");
close(listen_fd);
free(acceptor_data);
free(acceptor);
reactor_destroy(reactor);
return 1;
}
// 启动事件循环
reactor_run(reactor);
// 清理资源(正常情况下不会执行到这里)
reactor_destroy(reactor);
return 0;
}
ET模式下的读写黄金法则
#include <stdio.h> // 标准输入输出
#include <stdlib.h> // 内存分配、系统函数
#include <string.h> // 字符串处理
#include <unistd.h> // 文件描述符操作
#include <fcntl.h> // 文件控制选项
#include <sys/epoll.h> // epoll 接口
#include <sys/socket.h> // 套接字接口
#include <pthread.h> // 线程互斥锁
#include <errno.h> // 错误码定义
#include <stdint.h> // 标准整数类型
#define MAX_EVENTS 64 // epoll 最大事件数
#define MAX_FDS 1024 // 最大文件描述符数量
// 事件处理器结构体(基类)
typedef struct {
int fd; // 关联的文件描述符
void (*read_callback)(void* arg); // 读事件回调函数指针
void (*write_callback)(void* arg); // 写事件回调函数指针
void* arg; // 传递给回调函数的业务数据
} EventHandler;
// Reactor 核心结构体
typedef struct {
int epoll_fd; // epoll 实例的文件描述符
EventHandler* handlers[MAX_FDS]; // FD到处理器的映射数组
pthread_mutex_t lock; // 线程互斥锁(保护handlers数组)
} Reactor;
// 初始化 Reactor
Reactor* reactor_init() {
Reactor* r = (Reactor*)malloc(sizeof(Reactor));
if (!r) {
perror("malloc for reactor failed");
return NULL;
}
// 创建 epoll 实例
r->epoll_fd = epoll_create1(0);
if (r->epoll_fd == -1) {
perror("epoll_create1 failed");
free(r);
return NULL;
}
// 初始化 handlers 数组(全部置NULL)
memset(r->handlers, 0, sizeof(r->handlers));
// 初始化互斥锁(快速锁,无属性)
if (pthread_mutex_init(&r->lock, NULL) != 0) {
perror("pthread_mutex_init failed");
close(r->epoll_fd);
free(r);
return NULL;
}
return r;
}
// 添加事件处理器到 Reactor(边缘触发模式)
// 参数说明:
// r - Reactor 实例指针
// h - 事件处理器指针
// events - 需要监听的事件组合(EPOLLIN | EPOLLOUT 等)
void reactor_add(Reactor* r, EventHandler* h, uint32_t events) {
// 创建 epoll 事件结构并清零
struct epoll_event ev = {0};
// 设置事件类型:用户指定的事件 + 强制边缘触发模式
// EPOLLET 标志表示使用边缘触发(Edge Triggered)模式
ev.events = events | EPOLLET;
// 将事件处理器指针存入 data 字段(后续可通过ptr找回handler)
ev.data.ptr = h;
// 加锁保护共享资源(handlers数组和epoll_ctl操作)
pthread_mutex_lock(&r->lock);
// 将文件描述符添加到 epoll 监控
// EPOLL_CTL_ADD 表示添加新的监控项
if (epoll_ctl(r->epoll_fd, EPOLL_CTL_ADD, h->fd, &ev) == -1) {
// 如果添加失败,可能是fd已经存在,尝试修改
if (errno == EEXIST) {
if (epoll_ctl(r->epoll_fd, EPOLL_CTL_MOD, h->fd, &ev) == -1) {
perror("epoll_ctl mod failed");
pthread_mutex_unlock(&r->lock);
return;
}
} else {
perror("epoll_ctl add failed");
pthread_mutex_unlock(&r->lock);
return;
}
}
// 将处理器存入 handlers 数组(以fd为索引)
r->handlers[h->fd] = h;
// 解锁
pthread_mutex_unlock(&r->lock);
}
// 从 Reactor 移除事件处理器
void reactor_remove(Reactor* r, int fd) {
// 参数检查
if (fd < 0 || fd >= MAX_FDS) {
fprintf(stderr, "Invalid fd: %d\n", fd);
return;
}
pthread_mutex_lock(&r->lock);
// 从 epoll 移除监控
if (epoll_ctl(r->epoll_fd, EPOLL_CTL_DEL, fd, NULL) == -1) {
perror("epoll_ctl del failed");
}
// 清除 handlers 数组中的记录
r->handlers[fd] = NULL;
pthread_mutex_unlock(&r->lock);
}
// 修改已注册的事件类型
void reactor_modify(Reactor* r, EventHandler* h, uint32_t events) {
struct epoll_event ev = {0};
ev.events = events | EPOLLET; // 保持边缘触发模式
ev.data.ptr = h;
pthread_mutex_lock(&r->lock);
// 修改现有监控项
if (epoll_ctl(r->epoll_fd, EPOLL_CTL_MOD, h->fd, &ev) == -1) {
perror("epoll_ctl modify failed");
}
pthread_mutex_unlock(&r->lock);
}
// 运行 Reactor 事件循环
void reactor_run(Reactor* r) {
if (!r) return;
struct epoll_event events[MAX_EVENTS];
printf("Reactor event loop started (ET mode)\n");
while (1) {
// 等待事件发生(无限等待)
int n = epoll_wait(r->epoll_fd, events, MAX_EVENTS, -1);
if (n == -1) {
if (errno == EINTR) continue; // 被信号中断则重试
perror("epoll_wait error");
break;
}
// 处理所有就绪事件
for (int i = 0; i < n; i++) {
// 从事件数据中获取处理器指针
EventHandler* h = (EventHandler*)events[i].data.ptr;
uint32_t revents = events[i].events;
// 检查处理器有效性(可能已被移除)
pthread_mutex_lock(&r->lock);
int valid = (h && r->handlers[h->fd] == h);
pthread_mutex_unlock(&r->lock);
if (!valid) continue;
// 边缘触发模式必须处理所有可用数据
if ((revents & EPOLLIN) && h->read_callback) {
// 循环读取直到EAGAIN(非阻塞IO必需)
while (1) {
h->read_callback(h->arg);
// 实际项目中需要检查是否还有数据可读
// 可以通过额外系统调用判断,这里简化为单次回调
break;
}
}
if ((revents & EPOLLOUT) && h->write_callback) {
h->write_callback(h->arg);
}
// 处理错误和挂起事件
if (revents & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
if (h->read_callback) {
h->read_callback(h->arg); // 通常用读回调处理错误
}
reactor_remove(r, h->fd); // 自动移除
close(h->fd); // 关闭文件描述符
}
}
}
}
// 销毁 Reactor
void reactor_destroy(Reactor* r) {
if (!r) return;
// 清理所有处理器
for (int i = 0; i < MAX_FDS; i++) {
if (r->handlers[i]) {
close(i); // 关闭文件描述符
free(r->handlers[i]->arg); // 释放业务数据
free(r->handlers[i]); // 释放处理器
}
}
close(r->epoll_fd);
pthread_mutex_destroy(&r->lock);
free(r);
}
/********************** 示例使用 **********************/
typedef struct {
int fd;
char buf[1024];
} ClientData;
void on_read(void* arg) {
ClientData* data = (ClientData*)arg;
ssize_t n = read(data->fd, data->buf, sizeof(data->buf)-1);
if (n > 0) {
data->buf[n] = 0;
printf("FD %d: %s\n", data->fd, data->buf);
write(data->fd, data->buf, n); // 回显
} else if (n == 0 || errno == EAGAIN) {
// ET模式需处理EAGAIN
} else {
perror("read error");
}
}
int main() {
// 创建Reactor实例
Reactor* r = reactor_init();
// 示例:创建TCP服务器(简略版)
int listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
struct sockaddr_in addr = { .sin_family=AF_INET, .sin_port=htons(8080), .sin_addr.s_addr=INADDR_ANY };
bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr));
listen(listen_fd, SOMAXCONN);
// 创建监听处理器
EventHandler* h = malloc(sizeof(EventHandler));
ClientData* data = malloc(sizeof(ClientData));
data->fd = listen_fd;
h->fd = listen_fd;
h->read_callback = on_read;
h->write_callback = NULL;
h->arg = data;
// 添加到Reactor(边缘触发读事件)
reactor_add(r, h, EPOLLIN);
// 运行事件循环
reactor_run(r);
// 清理(正常情况下不会执行到这里)
reactor_destroy(r);
return 0;
}
验证示例:实现ECHO服务器,测试10K并发连接下数据传输完整性
四、性能优化:突破百万并发的核武器
锁优化三剑客
内存池设计
零拷贝技术矩阵
技术 | 原理 | 性能提升 |
---|---|---|
sendfile | 内核直接传输文件 | 40% |
splice | 管道中转数据 | 30% |
TCP_CORK | 合并小数据包 | 25% |
-
无锁队列:使用
__sync_fetch_and_add
实现任务分发#include <stdio.h> // 标准输入输出 #include <stdlib.h> // 内存分配 #include <stdint.h> // 标准整数类型 #include <unistd.h> // 系统调用 #define MAX_TASKS 1024 // 任务队列最大容量 // 任务结构体定义 typedef struct { void (*function)(void*); // 任务函数指针 void* arg; // 任务参数 } Task; // 线程安全环形队列结构体 typedef struct { Task tasks[MAX_TASKS]; // 任务数组 volatile uint32_t head; // 队列头指针(原子访问) volatile uint32_t tail; // 队列尾指针(原子访问) } TaskQueue; // 初始化任务队列 void init_queue(TaskQueue* q) { q->head = 0; // 初始化头指针为0 q->tail = 0; // 初始化尾指针为0 // 清空任务数组(可选) for (int i = 0; i < MAX_TASKS; i++) { q->tasks[i].function = NULL; q->tasks[i].arg = NULL; } } // 原子操作添加任务到队列 // 参数: // q - 任务队列指针 // t - 要添加的任务 void push_task(TaskQueue* q, Task t) { // 使用原子操作获取当前tail值并自增1 // __sync_fetch_and_add是GCC内置原子操作函数: // 1. 先返回q->tail的当前值 // 2. 然后将q->tail的值增加1 // 这个操作在多线程环境下是原子的 int idx = __sync_fetch_and_add(&q->tail, 1) % MAX_TASKS; // 将任务写入计算得到的位置 // 注意:这里没有检查队列是否已满,实际使用时需要添加容量检查 q->tasks[idx] = t; } // 原子操作从队列取出任务 // 参数: // q - 任务队列指针 // 返回: // 成功返回任务,队列为空返回空任务(function为NULL) Task pop_task(TaskQueue* q) { // 使用原子操作获取当前head值 uint32_t current_head = __sync_fetch_and_add(&q->head, 0); // 检查队列是否为空 if (current_head == __sync_fetch_and_add(&q->tail, 0)) { Task empty_task = { NULL, NULL }; return empty_task; // 队列为空 } // 计算实际索引(环形缓冲区) int idx = current_head % MAX_TASKS; // 读取任务 Task t = q->tasks[idx]; // 使用原子操作移动head指针 __sync_fetch_and_add(&q->head, 1); return t; } // 检查队列是否为空 int is_queue_empty(TaskQueue* q) { // 原子读取head和tail值进行比较 return __sync_fetch_and_add(&q->head, 0) == __sync_fetch_and_add(&q->tail, 0); } // 示例任务函数 void example_task(void* arg) { int* num = (int*)arg; printf("Processing task with number: %d\n", *num); } int main() { TaskQueue queue; init_queue(&queue); // 示例:添加任务 int arg1 = 42, arg2 = 100; Task t1 = { example_task, &arg1 }; Task t2 = { example_task, &arg2 }; // 线程安全地添加任务 push_task(&queue, t1); push_task(&queue, t2); // 处理任务 while (!is_queue_empty(&queue)) { Task t = pop_task(&queue); if (t.function) { t.function(t.arg); // 执行任务 } } return 0; }
-
RCU(Read-Copy-Update):减少处理器映射的锁竞争
-
FD分片:按FD哈希值分配到不同Reactor线程
#include <stdio.h> // 标准输入输出
#include <stdlib.h> // 内存分配
#include <string.h> // 内存操作
#define MAX_BLOCKS 1024 // 内存池最大块数
#define BLOCK_SIZE 4096 // 每个内存块大小(4KB)
// 内存池结构体
typedef struct {
void* blocks[MAX_BLOCKS]; // 内存块指针数组
int free_list[MAX_BLOCKS]; // 空闲块索引栈
int top; // 空闲栈顶指针
size_t block_size; // 每个块的大小
int initialized; // 初始化标志
} MemPool;
// 初始化内存池
// 参数:
// pool - 内存池指针
// block_size - 每个内存块的大小
void mempool_init(MemPool* pool, size_t block_size) {
// 检查参数有效性
if (pool == NULL || block_size == 0) {
fprintf(stderr, "Invalid parameters\n");
return;
}
// 初始化成员变量
memset(pool->blocks, 0, sizeof(pool->blocks)); // 清空块指针数组
memset(pool->free_list, 0, sizeof(pool->free_list)); // 清空空闲列表
pool->top = 0; // 初始时栈顶为0(栈空)
pool->block_size = block_size; // 设置块大小
pool->initialized = 1; // 标记为已初始化
// 预分配所有内存块
for (int i = 0; i < MAX_BLOCKS; i++) {
pool->blocks[i] = malloc(block_size); // 分配内存块
if (pool->blocks[i] == NULL) {
perror("Failed to allocate memory block");
// 如果分配失败,继续初始化已成功的块
break;
}
// 将块索引压入空闲栈
pool->free_list[pool->top++] = i;
}
}
// 从内存池分配内存块
// 参数:
// pool - 内存池指针
// 返回:
// 成功返回内存块指针,失败返回NULL
void* mempool_alloc(MemPool* pool) {
// 检查内存池是否初始化
if (!pool || !pool->initialized) {
fprintf(stderr, "Memory pool not initialized\n");
return NULL;
}
// 检查空闲栈中是否有可用块
if (pool->top > 0) {
// 从空闲栈弹出顶部索引(后进先出)
int block_idx = pool->free_list[--pool->top];
// 返回对应的内存块
return pool->blocks[block_idx];
}
// 空闲栈为空,直接分配新内存(超出池容量)
void* new_block = malloc(pool->block_size);
if (new_block) {
printf("Warning: Allocated beyond pool capacity\n");
}
return new_block;
}
// 释放内存块回内存池
// 参数:
// pool - 内存池指针
// ptr - 要释放的内存块指针
void mempool_free(MemPool* pool, void* ptr) {
// 检查参数有效性
if (!pool || !ptr || !pool->initialized) {
fprintf(stderr, "Invalid parameters\n");
return;
}
// 检查指针是否属于内存池
int is_pool_block = 0;
int block_idx = -1;
// 遍历所有块查找匹配指针
for (int i = 0; i < MAX_BLOCKS; i++) {
if (pool->blocks[i] == ptr) {
is_pool_block = 1;
block_idx = i;
break;
}
}
if (is_pool_block) {
// 如果指针属于内存池,将块索引压回空闲栈
if (pool->top < MAX_BLOCKS) {
pool->free_list[pool->top++] = block_idx;
} else {
fprintf(stderr, "Free list overflow\n");
}
} else {
// 不属于内存池的指针直接释放
free(ptr);
}
}
// 销毁内存池
void mempool_destroy(MemPool* pool) {
if (!pool || !pool->initialized) return;
// 释放所有内存块
for (int i = 0; i < MAX_BLOCKS; i++) {
if (pool->blocks[i]) {
free(pool->blocks[i]);
pool->blocks[i] = NULL;
}
}
// 重置内存池状态
pool->top = 0;
pool->initialized = 0;
}
// 示例使用
int main() {
MemPool pool;
// 初始化内存池(4KB块大小)
mempool_init(&pool, BLOCK_SIZE);
// 分配内存块
void* block1 = mempool_alloc(&pool);
void* block2 = mempool_alloc(&pool);
if (block1 && block2) {
printf("Allocated blocks: %p and %p\n", block1, block2);
// 使用内存块(示例)
strcpy((char*)block1, "Hello from memory pool");
printf("Block1 content: %s\n", (char*)block1);
// 释放内存块
mempool_free(&pool, block1);
mempool_free(&pool, block2);
}
// 销毁内存池
mempool_destroy(&pool);
return 0;
}
验证示例:实现内存泄漏检测系统,对比优化前后内存碎片率
五、从Reactor到Proactor:异步I/O的圣杯
Reactor的局限性
-
仍依赖同步I/O操作
-
磁盘I/O可能阻塞事件循环
Proactor架构跃迁
Linux AIO实战
#include <stdio.h> // 标准输入输出
#include <stdlib.h> // 内存分配
#include <string.h> // 字符串操作
#include <fcntl.h> // 文件控制选项
#include <unistd.h> // UNIX标准函数
#include <libaio.h> // Linux异步I/O库
#include <errno.h> // 错误码定义
#include <inttypes.h> // 整数类型定义
#define BUFFER_SIZE 4096 // 读写缓冲区大小
int main(int argc, char *argv[]) {
// 检查参数是否正确(需要文件名参数)
if (argc != 2) {
fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
return 1;
}
// 1. 创建异步I/O上下文
io_context_t aio_ctx = 0;
if (io_setup(10, &aio_ctx) < 0) { // 参数10表示同时支持的最大请求数
perror("io_setup failed");
return 1;
}
// 2. 打开目标文件(以只读方式)
int fd = open(argv[1], O_RDONLY | O_DIRECT); // O_DIRECT启用直接I/O(绕过缓存)
if (fd < 0) {
perror("open failed");
io_destroy(aio_ctx);
return 1;
}
// 3. 准备数据缓冲区(直接I/O需要内存对齐)
void *buffer = NULL;
if (posix_memalign(&buffer, 512, BUFFER_SIZE) != 0) { // 512字节对齐
perror("posix_memalign failed");
close(fd);
io_destroy(aio_ctx);
return 1;
}
memset(buffer, 0, BUFFER_SIZE); // 清空缓冲区
// 4. 准备I/O控制块(iocb)
struct iocb cb = {0}; // 清零初始化
cb.aio_fildes = fd; // 设置文件描述符
cb.aio_lio_opcode = IOCB_CMD_PREAD; // 异步读操作
cb.aio_buf = (uint64_t)buffer; // 数据缓冲区地址(转换为64位)
cb.aio_nbytes = BUFFER_SIZE; // 读取的字节数
cb.aio_offset = 0; // 从文件开头读取
// 5. 提交异步I/O请求
struct iocb *cbs[1] = {&cb}; // 请求指针数组
if (io_submit(aio_ctx, 1, cbs) != 1) { // 提交1个请求
perror("io_submit failed");
free(buffer);
close(fd);
io_destroy(aio_ctx);
return 1;
}
// 6. 等待I/O操作完成
struct io_event events[1]; // 事件数组
int ret = io_getevents(aio_ctx, 1, 1, events, NULL); // 等待1个事件完成
if (ret != 1) {
perror("io_getevents failed");
free(buffer);
close(fd);
io_destroy(aio_ctx);
return 1;
}
// 7. 检查操作结果
if (events[0].res != BUFFER_SIZE) {
fprintf(stderr, "Read incomplete: %lld bytes\n", (long long)events[0].res);
} else {
printf("Successfully read %d bytes\n", BUFFER_SIZE);
// 打印前16字节内容(示例)
printf("First 16 bytes: ");
for (int i = 0; i < 16; i++) {
printf("%02x ", ((unsigned char*)buffer)[i]);
}
printf("\n");
}
// 8. 清理资源
free(buffer);
close(fd);
io_destroy(aio_ctx); // 销毁异步I/O上下文
return 0;
}
六、Reactor模型与epoll的结合:高性能网络框架的实现
1. Reactor模型与epoll的结合
通过将Reactor模型与epoll结合,可以实现一个高效的网络框架。Reactor模型负责事件的分发和处理,而epoll负责高效的I/O多路复用。
2. 实现高性能网络框架的关键点
- 线程模型的选择:选择单线程模型,通过Reactor模型处理所有I/O事件。
- 非阻塞I/O:通过设置文件描述符为非阻塞模式,避免阻塞操作。
- 高效的事件处理:通过事件驱动的方式,快速响应I/O事件。
实战示例:实现一个高性能的网络服务器框架
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <pthread.h>
#define MAX_EVENTS 10
#define BUFFER_SIZE 1024
typedef struct {
int fd;
char buffer[BUFFER_SIZE];
size_t buffer_len;
pthread_t thread_id;
} Connection;
void *connection_handler(void *arg) {
Connection *connection = (Connection *)arg;
while (1) {
char buffer[BUFFER_SIZE];
int read_size = read(connection->fd, buffer, BUFFER_SIZE - 1);
if (read_size > 0) {
printf("Received data from %d: %s\n", connection->fd, buffer);
// 将接收到的数据存储到连接的缓冲区
memcpy(connection->buffer + connection->buffer_len, buffer, read_size);
connection->buffer_len += read_size;
}
if (connection->buffer_len > 0) {
int write_size = write(connection->fd, connection->buffer, connection->buffer_len);
if (write_size > 0) {
// 移除已发送的数据
memmove(connection->buffer, connection->buffer + write_size, connection->buffer_len - write_size);
connection->buffer_len -= write_size;
}
}
}
close(connection->fd);
free(connection);
return NULL;
}
int main() {
int server_fd, epoll_fd;
struct sockaddr_in server_addr;
// 创建套接字
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("socket");
exit(1);
}
// 设置套接字选项
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 绑定地址
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8080);
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
exit(1);
}
// 监听连接
listen(server_fd, 3);
// 创建epoll实例
epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
perror("epoll_create1");
exit(1);
}
// 将服务器套接字添加到epoll监控列表
struct epoll_event event;
event.events = EPOLLIN | EPOLLET;
event.data.fd = server_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1) {
perror("epoll_ctl");
exit(1);
}
// 事件循环
struct epoll_event events[MAX_EVENTS];
while (1) {
int event_count = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (event_count == -1) {
perror("epoll_wait");
exit(1);
}
for (int i = 0; i < event_count; i++) {
int fd = events[i].data.fd;
if (events[i].events & EPOLLIN) {
if (fd == server_fd) {
// 处理新的连接
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd == -1) {
perror("accept");
continue;
}
// 创建新的连接结构体
Connection *connection = (Connection *)malloc(sizeof(Connection));
connection->fd = client_fd;
connection->buffer_len = 0;
// 创建新的线程处理连接
if (pthread_create(&connection->thread_id, NULL, connection_handler, connection) != 0) {
perror("pthread_create");
close(client_fd);
free(connection);
continue;
}
} else {
// 处理数据读取
// (实际应用中,可以通过epoll_wait获取到连接的文件描述符,并分发给对应的线程处理)
}
}
}
}
// 关闭资源
close(epoll_fd);
close(server_fd);
return 0;
}
结语:永恒的性能追求
从C10K到C10M,Reactor+epoll的组合仍是Linux高性能网络的基石。Nginx、Redis、Memcached等顶级开源项目无不以此为核心:
-
Nginx:多Reactor进程+定时器红黑树
-
Redis:单Reactor+多I/O线程
-
DPDK:用户态协议栈+epoll模拟
但技术从未停止进化:io_uring正在颠覆传统异步I/O模型,eBPF让网络栈可编程化... 掌握核心原理,方能以不变应万变!
终极挑战:结合io_uring重构Reactor核心,实现200万QPS代理服务器