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
相关产品推荐
相关产品推荐

