我有一个程序,使用 async_read 和来自 unix 域套接字的收益。
void readSocket(const std::list<std::shared_ptr<IpcSocket>>::iterator &socket,
boost::asio::yield_context yield) {
.....
while (1) {
HeaderStruct hdr;
boost::asio::async_read((*socket)->socket,
boost::asio::buffer(&hdr, sizeof(hdr)),
boost::asio::transfer_all(), yield);
.....
}
}
似乎在极端情况下,当系统因到达进程可以处理的更高粘贴的请求而过载时,我达到进程打开文件描述符已超过最大值的临时状态。
查看
lsof
显示几乎所有打开的文件描述符都是为双向管道创建的,还有一个是为 KQUEUE 创建的。根据我的观察,async_read 中的协程暂停是通过触发这 3 个文件描述符的 select
系统调用来实现的。
myProg 83099 root 184u KQUEUE count=1, state=0x8
myProg 83099 root 185 PIPE 0x432799c5a6f5943f 16384 ->0x28a17a0822a260f7
myProg 83099 root 186 PIPE 0x28a17a0822a260f7 16384 ->0x432799c5a6f5943f
我想知道我关于 select 系统调用触发这 3 个文件描述符的理论是否正确(我使用 dtrace 在处理来自套接字的读取请求时监视系统调用),如果是,我该如何避免呢?我应该用常规异步调用替换协程,还是在分离线程上使用同步读取?
谢谢
您提到的文件描述符与异步I/O的内部机制有关,并不直接对应于您打开的Unix域套接字。您关于触发这些描述符的 select 系统调用的理论并不完全准确。
在您提供的代码中,async_read 是一种执行非阻塞 I/O 的协程友好方式。它不直接使用 select 或 poll,而是依赖 Boost.Asio 的内部机制来处理 I/O 事件,其中可能涉及 epoll 或 kqueue(在类 Unix 系统上)等机制。
您可以尝试增加程序可以使用的文件描述符的最大数量。在许多系统上,您可以通过修改 /etc/security/limits.conf 或 /etc/security/limits.d/ 文件或使用 ulimit 命令来执行此操作。但是,通常不建议这样做,因为它可能会对系统范围产生影响。
实现请求限流机制,限制可以并发处理的请求数量。这可以帮助减轻文件描述符的压力并防止系统过载。
如果增加文件描述符限制还不够,您可以考虑将部分处理移至单独的线程。这可以帮助分配负载并减少每个线程打开的文件描述符的数量。