You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下C++20中epoll_wait返回随机fd值的问题求助

问题描述

我在Ubuntu 22.04系统中使用C++20开发,尝试为Socket注册epoll事件。已确认传入epoll_ctl的fd正确且epoll实例已创建,但调用epoll_wait时,返回的events[active].data.fd为随机负数。相关代码片段及运行输出如下:

代码片段

bool
service::engine::BListenerRunner::do_start_telnet_listener(void)
{
    m_socket_tcp_telnet_listener_p = new service::network::BSocketTcp(0, 5000, (60 * 60 * 1000), true);
    if (!m_socket_tcp_telnet_listener_p->do_listen())
    {
        // todo: log error
        return false;
    }

    std::cout << "Listening to port: " << m_socket_tcp_telnet_listener_p->port() << " on fd: " << m_socket_tcp_telnet_listener_p->fd() << std::endl;
    service::engine::BEventPollRunner::get_instance()->attach(m_socket_tcp_telnet_listener_p->fd(), m_socket_tcp_telnet_listener_p, shared::EVENT_POLL_IN);
    return true;
}
void
service::engine::BEventPollRunner::attach(int _fd, core::epoll::BEPollSubscriber* _subscriber_p, int _flag)
{
    struct epoll_event event;
    event.events = 0;
    event.data.fd = 0;
    event.data.ptr = (void*) _subscriber_p;

    // EVENT_POLL_IN / OUT is just an enum due to my code architecture, which ensures epoll library is included only in the correct source file.
    if ((_flag & shared::EVENT_POLL_IN)) event.events |= EPOLLIN;
    if ((_flag & shared::EVENT_POLL_OUT)) event.events |= EPOLLOUT;

    if (m_subscribers.find(_fd) == m_subscribers.end())
    {
        if (do_epoll_ctl(EPOLL_CTL_ADD, _fd, &event)) m_subscribers[_fd] = _subscriber_p;
        return;
    }

    do_epoll_ctl(EPOLL_CTL_MOD, _fd, &event);
}
bool
service::engine::BEventPollRunner::do_epoll_ctl(int _operation, int _fd, epoll_event* _event_p)
{
    int err = epoll_ctl(m_epoll_fd, _operation, _fd, _event_p);
    std::cout << "Adding fd " << _fd << " for op " << _operation << " on epfd " << m_epoll_fd << " err " << err << std::endl;
    if (err != 0)
    {
        switch (errno) // The errors have not implemented printing out.
        {
            case EBADF:
            {} break;
            case EEXIST:
            {} break;
            case EINVAL:
            {} break;
            case ENOENT:
            {} break;
            case ENOMEM:
            {} break;
            case EPERM:
            {} break;
        }

        return false;
    }

    return true;
}
epoll_event events[_EVENT_POLL_SIZE_];
int active_epoll = epoll_wait(m_epoll_fd, events, _EVENT_POLL_SIZE_, _EVENT_POLL_TIMEOUT_MS_);
if (active_epoll < 0)
{
    if (errno == EINTR) return;
    else
    {
        // todo: log warn/error
    }
}
    
for (int active = 0; active < active_epoll; ++active)
{
std::cout << "event fpoll fd: " << events[active].data.fd << std::endl;
}

运行输出

Adding fd 4 for op 1 on epfd 3 err 0
event fpoll fd: -604570616
解决方法

问题原因

epoll_event结构体中的data是共用体(union),同一时间只能使用其中一个成员(fd或ptr),不能同时赋值并读取。你在attach函数中设置了event.data.ptr = (void*) _subscriber_p,此时data.fd的内容是_subscriber_p的内存地址被强制转换为int的结果,因此出现随机负数。

两种修复方案

方案1:使用data.fd传递文件描述符

修改attach函数,移除data.ptr的赋值,改为设置data.fd为目标文件描述符:

void
service::engine::BEventPollRunner::attach(int _fd, core::epoll::BEPollSubscriber* _subscriber_p, int _flag)
{
    struct epoll_event event;
    event.events = 0;
    // 设置data.fd为传入的文件描述符
    event.data.fd = _fd;
    // 移除data.ptr的赋值,避免共用体冲突
    // event.data.ptr = (void*) _subscriber_p;

    if ((_flag & shared::EVENT_POLL_IN)) event.events |= EPOLLIN;
    if ((_flag & shared::EVENT_POLL_OUT)) event.events |= EPOLLOUT;

    if (m_subscribers.find(_fd) == m_subscribers.end())
    {
        if (do_epoll_ctl(EPOLL_CTL_ADD, _fd, &event)) m_subscribers[_fd] = _subscriber_p;
        return;
    }

    do_epoll_ctl(EPOLL_CTL_MOD, _fd, &event);
}

方案2:使用data.ptr关联订阅者对象

保留attach函数中data.ptr的赋值,修改epoll_wait后的处理逻辑,读取data.ptr而非data.fd:

epoll_event events[_EVENT_POLL_SIZE_];
int active_epoll = epoll_wait(m_epoll_fd, events, _EVENT_POLL_SIZE_, _EVENT_POLL_TIMEOUT_MS_);
if (active_epoll < 0)
{
    if (errno == EINTR) return;
    else
    {
        // todo: log warn/error
    }
}
    
for (int active = 0; active < active_epoll; ++active)
{
    // 从data.ptr中取出订阅者对象
    core::epoll::BEPollSubscriber* subscriber = static_cast<core::epoll::BEPollSubscriber*>(events[active].data.ptr);
    // 通过订阅者对象获取fd
    std::cout << "event fpoll fd: " << subscriber->fd() << std::endl;
}

内容的提问来源于stack exchange,提问作者Benny Ng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 02:47:07