Epoll实验
本目录包含epoll相关的实验代码和说明。
实验1:Echo服务器
编译
gcc -o echo_server echo_server.c
运行
./echo_server 8080
测试
方法1:使用telnet
telnet localhost 8080
# 输入任意文本,服务器会回显
方法2:使用nc
echo "Hello, World!" | nc localhost 8080
方法3:使用自定义客户端
gcc -o client client.c
./client localhost 8080
性能测试
使用wrk进行压力测试:
# 安装wrk
sudo apt install wrk
# 10个线程,100个连接,持续30秒
wrk -t10 -c100 -d30s http://localhost:8080/
观察系统行为:
# 终端1:运行服务器
./echo_server 8080
# 终端2:观察连接
watch -n 1 'ss -tan | grep :8080 | wc -l'
# 终端3:观察epoll事件
sudo strace -p <pid> -e epoll_wait
# 终端4:观察CPU
top -p <pid>
实验2:LT vs ET模式对比
Level Triggered (LT) - 水平触发
特点:
- 只要缓冲区有数据,就会一直触发
- 更容易使用
- 默认模式
修改echo_server.c:
// 将这行
ev.events = EPOLLIN | EPOLLET;
// 改为
ev.events = EPOLLIN; // LT模式
Edge Triggered (ET) - 边缘触发
特点:
- 只在状态变化时触发一次
- 必须一次读完所有数据
- 更高效但更难用
echo_server.c已经使用ET模式:
ev.events = EPOLLIN | EPOLLET; // ET模式
对比实验
- 编译两个版本:
# LT版本
gcc -o echo_server_lt echo_server.c -DLT_MODE
# ET版本 (默认)
gcc -o echo_server_et echo_server.c
- 测试并观察触发次数:
# 使用strace观察epoll_wait
sudo strace -e epoll_wait ./echo_server_lt 8080 2>&1 | grep epoll_wait
sudo strace -e epoll_wait ./echo_server_et 8080 2>&1 | grep epoll_wait
实验3:性能对比
与select/poll对比
测试场景:
- 1000个并发连接
- 持续发送数据
- 对比QPS和CPU使用率
预期结果:
模型 QPS CPU使用率 最大连接数
select ~1000 30% 1024
poll ~5000 40% 无限制
epoll ~50000 20% 无限制
实验4:EPOLLONESHOT
**用途:**防止同一个socket的事件被多个线程同时处理。
示例代码修改:
// 添加EPOLLONESHOT标志
ev.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
// 处理完后重新arm
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
ev.data.fd = fd;
epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);
学习要点
epoll的三个函数:
epoll_create1()
: 创建epoll实例epoll_ctl()
: 添加/删除/修改监听的fdepoll_wait()
: 等待事件
边缘触发的注意事项:
- 必须使用非阻塞I/O
- 必须循环读/写直到EAGAIN
- 否则会丢失事件
性能优化技巧:
- 使用ET模式减少触发次数
- 合理设置epoll_wait超时
- 避免频繁的add/del操作
常见错误:
- 忘记设置非阻塞
- ET模式下没有读完数据
- 忘记处理EINTR
- 没有正确关闭fd
扩展实验
- 多线程epoll:一个epoll被多个线程共享
- 多epoll:每个线程一个epoll (更好的扩展性)
- EPOLLEXCLUSIVE:多个epoll实例监听同一个fd (Linux 4.5+)
参考资料
man epoll
man epoll_create
man epoll_ctl
man epoll_wait