Golang运行时中如何修改默认的Epoll模式

Golang运行时中如何修改默认的Epoll模式 你好,

在我的研究项目中,我正在尝试修改 Go 运行时,使其使用 “Epolloneshot” 模式,而不是默认的 “边沿触发” 模式。根据我对代码的分析,我已经修改了 src/runtime/netpoll_epoll.go 文件中的两个函数。

首先,我更新了 netpollopen 函数,使用 Epolloneshot 标志替代了 Epollet 标志。其次,我修改了在 Linux 上未使用的 netpollarm 函数,使其能为 Epolloneshot 模式重新启用通知。我还调整了 src/runtime/netpoll.go 中的 poll_runtime_pollWait() 函数,使其在 Linux 上调用 netpollarm

现在,我遇到了一个问题:Go 编译时的 HTTP 测试失败了。我看到的错误如下: “httptest.Server blocked in Close after 5 seconds, waiting for connections: *net.TCPConn 0xc000221520 127.0.0.1:51836 in state active panic: test timed out after 9m0s”

我怀疑重新启用通知的机制可能不正确,导致服务器无限期地停滞。

供参考,我使用的是 Ubuntu 22.04 和 Go 1.18.10。


更多关于Golang运行时中如何修改默认的Epoll模式的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang运行时中如何修改默认的Epoll模式的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在 Go 运行时中修改 epoll 模式需要谨慎处理,因为 netpoller 是 Go 调度器的核心组件。根据你的描述,问题可能出现在 netpollarm 的实现上。以下是关键修改点的示例代码:

  1. 修改 netpollopen 函数src/runtime/netpoll_epoll.go):
func netpollopen(fd uintptr, pd *pollDesc) int32 {
    var ev epollevent
    ev.events = _EPOLLIN | _EPOLLOUT | _EPOLLRDHUP | _EPOLLET | _EPOLLONESHOT  // 替换 EPOLLET 为 EPOLLONESHOT
    ev.data = uint64(fd)
    return -epollctl(epfd, _EPOLL_CTL_ADD, int32(fd), &ev)
}
  1. 实现 netpollarm 函数src/runtime/netpoll_epoll.go):
func netpollarm(pd *pollDesc, mode int) {
    var ev epollevent
    ev.events = _EPOLLIN | _EPOLLOUT | _EPOLLRDHUP | _EPOLLET | _EPOLLONESHOT
    if mode == 'r' {
        ev.events |= _EPOLLIN
    } else if mode == 'w' {
        ev.events |= _EPOLLOUT
    }
    ev.data = uint64(pd.fd)
    epollctl(epfd, _EPOLL_CTL_MOD, int32(pd.fd), &ev)
}
  1. 确保 poll_runtime_pollWait 调用 netpollarmsrc/runtime/netpoll.go):
func poll_runtime_pollWait(pd *pollDesc, mode int) int {
    // ... 现有代码 ...
    if GOOS == "linux" {
        netpollarm(pd, mode)
    }
    // ... 现有代码 ...
}

HTTP 测试超时的典型原因是:

  • EPOLLONESHOT 模式下事件被消费后未正确重新注册
  • 连接关闭时未正确处理 EPOLLRDHUP 事件
  • 边缘触发模式下未完全读取数据导致死锁

检查点:

  1. 确认 netpollarm 在每次事件处理后都被调用
  2. 验证 EPOLLRDHUP 事件的处理逻辑
  3. 检查 netpoll 函数中是否遗漏了 EPOLLONESHOT 的重置

调试建议:

// 在 netpoll 函数中添加调试输出
func netpoll(delay int64) gList {
    // ...
    for i := 0; i < n; i++ {
        ev := &events[i]
        if ev.events == 0 {
            continue
        }
        println("epoll event:", ev.events, "fd:", ev.data)  // 调试输出
        // ...
    }
    // ...
}

注意:修改运行时需要重新编译 Go 工具链,使用 ./make.bash./all.bash 重新构建。

回到顶部