HiHuo
首页
博客
手册
工具
首页
博客
手册
工具
  • 系统底层修炼

    • 操作系统核心知识学习指南
    • CPU调度与上下文切换
    • CFS调度器原理与源码
    • 内存管理与虚拟内存
    • PageCache与内存回收
    • 文件系统与IO优化
    • 零拷贝与Direct I/O
    • 网络子系统架构
    • TCP协议深度解析
    • TCP问题排查实战
    • 网络性能优化
    • epoll与IO多路复用
    • 进程与线程管理
    • Go Runtime调度器GMP模型
    • 系统性能分析方法论
    • DPDK与用户态网络栈
    • eBPF与内核可观测性
    • 综合实战案例

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模式

对比实验

  1. 编译两个版本:
# LT版本
gcc -o echo_server_lt echo_server.c -DLT_MODE

# ET版本 (默认)
gcc -o echo_server_et echo_server.c
  1. 测试并观察触发次数:
# 使用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);

学习要点

  1. epoll的三个函数:

    • epoll_create1(): 创建epoll实例
    • epoll_ctl(): 添加/删除/修改监听的fd
    • epoll_wait(): 等待事件
  2. 边缘触发的注意事项:

    • 必须使用非阻塞I/O
    • 必须循环读/写直到EAGAIN
    • 否则会丢失事件
  3. 性能优化技巧:

    • 使用ET模式减少触发次数
    • 合理设置epoll_wait超时
    • 避免频繁的add/del操作
  4. 常见错误:

    • 忘记设置非阻塞
    • ET模式下没有读完数据
    • 忘记处理EINTR
    • 没有正确关闭fd

扩展实验

  1. 多线程epoll:一个epoll被多个线程共享
  2. 多epoll:每个线程一个epoll (更好的扩展性)
  3. EPOLLEXCLUSIVE:多个epoll实例监听同一个fd (Linux 4.5+)

参考资料

  • man epoll
  • man epoll_create
  • man epoll_ctl
  • man epoll_wait