使用 Linux (Ubuntu) 应用程序工作。我需要以非阻塞方式读取许多文件。不幸的是,epoll 不支持真正的文件描述符(来自文件的文件描述符),它支持网络套接字的文件描述符。
select
确实适用于真实的文件描述符,但它有两个缺点,1)它很慢,线性地遍历所有设置的文件描述符,2)它是有限的,它通常不允许超过 1024 个文件描述符。
我可以将每个文件描述符更改为非阻塞并使用非阻塞“读取”进行轮询,但它非常昂贵,尤其是当有大量文件描述符时。
这里有哪些选项?
谢谢。
更新1 这里的用例是创建某种文件服务器,许多客户端请求文件,以非阻塞方式提供它们。由于网络端实现(不是标准 TCP/IP 堆栈),无法使用 sendfile()。
您可以将多个
select
调用与线程或分叉结合使用。这将减少每组 FD_ISSET
的 select
调用数量。
也许您可以提供有关您的用例的更多详细信息。听起来您正在使用
select
来监视文件更改,这与您对常规文件的预期不同。也许您只是在寻找flock
您可以在 Linux 上使用异步 IO。 相关 AIO 手册页(全部在第 3 节中)似乎包含相当多的信息。我认为
aio_read()
可能对您最有用。
这里有一些代码,我相信您应该能够适应您的用法:
...
#define _GNU_SOURCE
#include <aio.h>
#include <unistd.h>
typedef struct {
struct aiocb *aio;
connection_data *conn;
} cb_data;
void callback (union sigval u) {
// recover file related data prior to freeing
cb_data data = u.sival_ptr;
int fd = data->aio->aio_fildes;
uint8_t *buffer = data->aio->aio_buf;
size_t len = data->aio->aio_nbytes;
free (data->aio);
// recover connection data pointer then free
connection_data *conn = data->conn;
free (data);
...
// finish handling request
...
return;
}
...
int main (int argc, char **argv) {
// initial setup
...
// setup aio for optimal performance
struct aioinit ainit = { 0 };
// online background threads
ainit.aio_threads = sysconf (_SC_NPROCESSORS_ONLN) * 4;
// use defaults if using few core system
ainit.aio_threads = (ainit.aio_threads > 20 ? ainit.aio_threads : 20)
// set num to the maximum number of likely simultaneous requests
ainit.aio_num = 4096;
ainit.aio_idle_time = 5;
aio_init (&ainit);
...
// handle incoming requests
int exit = 0;
while (!exit) {
...
// the [asynchronous] fun begins
struct aiocb *cb = calloc (1, sizeof (struct aiocb));
if (!cb)
// handle OOM error
cb->aio_fildes = file_fd;
cb->aio_offset = 0; // assuming you want to send the entire file
cb->aio_buf = malloc (file_len);
if (!cb->aio_buf)
// handle OOM error
cb->aio_nbytes = file_len;
// execute the callback in a separate thread
cb->aio_sigevent.sigev_notify = SIGEV_THREAD;
cb_data *data = malloc (sizeof (cb_data));
if (!data)
// handle OOM error
data->aio = cb; // so we can free() later
// whatever you need to finish handling the request
data->conn = connection_data;
cb->aio_sigevent.sigev_value.sival_ptr = data; // passed to callback
cb->aio_sigevent.sigev_notify_function = callback;
if ((err = aio_read (cb))) // and you're done!
// handle aio error
// move on to next connection
}
...
return 0;
}
这将导致您不再需要等待主线程中读取文件。当然,您可以使用 AIO 创建性能更高的系统,但这些系统自然可能会更复杂,这应该适用于基本用例。