我的应用程序只有 1 个用于 recv() 和 send() 的 Unix TCP 套接字。套接字是非阻塞的。鉴于此,在 send()/recv() 之前执行 select() 是否有优势?
如果底层 TCP 管道没有为 I/O 做好准备,send()/recv() 应该立即返回一个 EWOULDBLOCK 或 EAGAIN。那么,执行 select() 有什么意义呢?看起来,在这种情况下,它可能只会导致额外的系统调用开销。我错过了什么吗?
编辑:忘了提:该应用程序是单线程的。
我的应用程序只有 1 个用于 recv() 和 send() 的 Unix TCP 套接字。套接字是非阻塞的。鉴于此,在 send()/recv() 之前执行 select() 是否有优势?
如果底层 TCP 管道没有为 I/O 做好准备,send()/recv() 应该立即返回一个 EWOULDBLOCK 或 EAGAIN。那么,执行 select() 有什么意义呢?看起来,在这种情况下,它可能只会导致额外的系统调用开销。我错过了什么吗?
编辑:忘了提:该应用程序是单线程的。
如果您的套接字是非阻塞的,那么您需要select
(或者最好poll
是没有破坏FD_SETSIZE
限制和相关危险的)为您阻塞以代替将发生的阻塞(如果套接字不是非阻塞的)在send
和recv
。否则你会旋转,使用 100% 的 cpu 时间无所事事。在大多数情况下,您可以轻松地使套接字阻塞并取消select
/ poll
。但是,有一个有趣的情况需要考虑:如果您的程序被阻塞并且套接字另一端的程序也被阻塞(或相反),阻塞 IO可能会死锁。具有非阻塞 IO 和/send
send
select
poll
,您自然会检测到这种情况并在无法编写输出时处理待处理的输入。
你可以在一个循环中执行 recv() ,但是你会消耗大量的 CPU 时间。
最好等待 select() 以避免额外的 CPU 开销。如果您需要执行后台任务,请为 select() 添加超时,以便您可以定期唤醒,即使没有网络流量。
如果您的应用程序对延迟敏感,那么 in 可能有理由在没有 select() 的情况下在紧密的 recv() 循环中旋转并给它一个专用的 CPU(否则调度程序会惩罚它并最终导致大量延迟)。如果您的应用负担不起,但仍然提供一个线程来服务此套接字,那么只需在读取端设置套接字阻塞,并让调度程序在数据可用时唤醒您的线程。在发送端再次取决于您的需要,使套接字阻塞或旋转。
只有当你的应用程序是单线程并且逻辑是“receive-process-reply”时,你才绝对需要一个非阻塞的读/写套接字、选择器和一个写队列,以便你在数据存在时接收、处理、坑响应队列,注册可写,可写时将队列刷新到套接字,取消可写注册。可读性将一直被注册。