带命令队列的设备驱动:如何在串口发完数据后中断epoll_wait?
串口发送完成时中断epoll_wait的优雅实现方案
核心思路:结合串口硬件状态通知与epoll事件监听
你不需要依赖超时估算,完全可以通过串口的硬件状态反馈精准触发epoll_wait的中断,以下是两种可行方案:
方案1:基于线路状态寄存器(LSR)的事件监听
- 调用
write()发送数据后,将串口文件描述符注册到epoll实例,监听EPOLLOUT事件(建议使用边缘触发EPOLLET模式,避免重复触发)。 - 当epoll_wait捕获到
EPOLLOUT事件时,通过ioctl读取串口线路状态寄存器:int lsr; ioctl(fd, TIOCSERGETLSR, &lsr); - 检查
lsr中的TIOCSER_TEMT位(该位为1时,表示发送移位寄存器和输出缓冲区均为空,即数据已完全发送):if (lsr & TIOCSER_TEMT) { // 数据发送完成,处理命令队列下一个任务 epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); // 移除该事件监听 }
这种方式能精准捕获发送完成的瞬间,无需估算超时时间。
- 调用
方案2:异步信号通知结合epoll(适用于非阻塞场景)
- 配置串口文件描述符的异步通知属性:
fcntl(fd, F_SETOWN, getpid()); // 将SIGIO信号发送给当前进程 int flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | O_ASYNC); // 启用异步I/O通知 - 注册
SIGIO信号处理函数,在信号回调中标记发送完成状态;同时可以将一个管道的写端注册到epoll,当触发SIGIO时向管道写一个字节,间接中断epoll_wait。 - 发送数据后,epoll_wait会在收到管道的可读事件时被唤醒,此时再通过
TIOCSERGETLSR确认发送状态,执行后续逻辑。
- 配置串口文件描述符的异步通知属性:
补充说明
- 不要直接依赖
EPOLLOUT作为发送完成的标志:EPOLLOUT仅表示输出缓冲区有空闲空间可写入,而非数据已完全发送到硬件。必须结合线路状态寄存器的检查才能确认发送完成。 tcdrain(fd)是阻塞式等待发送完成的函数,如果你的驱动允许阻塞场景,也可以在write()后调用它,但这种方式无法和epoll的异步模型结合,适合简单场景。
内容的提问来源于stack exchange,提问作者konan
相关产品推荐
相关产品推荐

