【C/C++】手写 DPDK 协议栈(十二):TCP 并发与自实现 epoll 的就绪事件分发

【C/C++】手写 DPDK 协议栈(十二):TCP 并发与自实现 epoll 的就绪事件分发 目录手写 DPDK 协议栈十二TCP 并发与自实现 epoll 的就绪事件分发1. 结构注册表和就绪表不能混在一起2. 从 TCP 数据到 EPOLLIN3. 使用方式4. 并发语义的三个难点小结手写 DPDK 协议栈十二TCP 并发与自实现 epoll 的就绪事件分发标签DPDKTCPepoll并发红黑树tcp_multi/在 TCP socket 基础上实现nepoll_create、nepoll_ctl与nepoll_wait。它展示了 epoll 的本质注册表记录“关心什么”就绪队列记录“现在有什么可处理”等待者只从就绪队列取事件。1. 结构注册表和就绪表不能混在一起epoll instance ├─ 红黑树 rbrsockfd - epitem支持 ctl ADD/MOD/DEL ├─ 就绪链表 rdlist仅保存已发生事件的 epitem └─ cond mutex没有事件时阻塞/唤醒 nepoll_wait课程eventpoll使用红黑树查找注册项使用链表维护 ready list并分别对树与就绪表设置同步机制。这样不会在每次wait时扫描全部 fd。structeventpoll{ep_rb_tree rbr;intrbcnt;LIST_HEAD(,epitem)rdlist;intrdnum;pthread_mutex_tmtx;pthread_spinlock_tlock;pthread_cond_tcond;};2. 从 TCP 数据到 EPOLLINTCP 协议线程收到 PSH 数据后将tcp_offload_t放入连接的inringsocket 层再调用事件回调将对应epitem放入 ready list 并唤醒等待线程。NIC RX - TCP ESTABLISHED - socket.in ring - nepoll_event_callback(ep, sockfd, EPOLLIN) - rdlist - nepoll_wait 返回 - nrecv 读取 socket.inEPOLLOUT通常在 socket 可继续发送时产生EPOLLERR、EPOLLHUP和EPOLLRDHUP则要在 FIN、RST、队列错误等状态转移中明确生成不能凭空假定。3. 使用方式intepfdnepoll_create(1024);structepoll_eventev{.eventsEPOLLIN,.data.fdconnfd};nepoll_ctl(epfd,EPOLL_CTL_ADD,connfd,ev);structepoll_eventevents[128];intnnepoll_wait(epfd,events,128,-1);for(inti0;in;i){if(events[i].eventsEPOLLIN)nrecv(events[i].data.fd,buf,sizeof(buf),0);}教学接口前缀为n用于与 Linux 系统调用区分。应用层永远应在读取前检查返回值处理短读、关闭与错误。4. 并发语义的三个难点只入 ready list 一次同一个 fd 已就绪时再次到包不能无限重复插入epitem.rdy是这一去重状态。事件和数据的顺序先把数据安全入 socket ring再发布 EPOLLIN否则wait返回后可能读到空队列。删除与回收EPOLL_CTL_DEL、socket close、协议线程回调可能并发必须先从注册树和 ready list 摘除再释放对象。小结DPDK 负责高性能报文收发TCP 状态机把包转为 socket 数据epoll 再把 socket 数据转为应用可等待的事件。三层之间通过明确的队列和所有权衔接才能在并发连接下既快又可维护。学习链接: https://github.com/0voice