C 阅读许多真实的文件描述符

C 阅读许多真实的文件描述符,c,select,epoll,C,Select,Epoll,在Linux Ubuntu应用程序上工作。我需要以非阻塞方式读取许多文件。不幸的是,epoll不支持真正的文件描述符文件描述符,它支持文件描述符,即网络套接字。select确实适用于实际的文件描述符,但它有两个缺点:1它速度慢,线性遍历所有已设置的文件描述符;2它是有限的,通常不允许超过1024个文件描述符 我可以将每个文件描述符更改为非阻塞,并使用非阻塞读取进行轮询,但这非常昂贵,尤其是当有大量文件描述符时 这里有什么选择 谢谢 更新1 这里的用例是创建某种文件服务器,许多客户端请求文件,以非

在Linux Ubuntu应用程序上工作。我需要以非阻塞方式读取许多文件。不幸的是,epoll不支持真正的文件描述符文件描述符,它支持文件描述符,即网络套接字。select确实适用于实际的文件描述符,但它有两个缺点:1它速度慢,线性遍历所有已设置的文件描述符;2它是有限的,通常不允许超过1024个文件描述符

我可以将每个文件描述符更改为非阻塞,并使用非阻塞读取进行轮询,但这非常昂贵,尤其是当有大量文件描述符时

这里有什么选择

谢谢

更新1 这里的用例是创建某种文件服务器,许多客户端请求文件,以非阻塞方式为它们提供服务。由于网络端实现不是标准的TCP/IP堆栈,因此无法使用sendfile。

您可以使用多个select调用并结合线程或分叉。这将减少每个选择集的FD_ISSET调用数


也许您可以提供关于您的用例的更多细节。听起来像是在使用select来监视文件更改,但这并不像您对常规文件所期望的那样有效。也许您只是想在Linux上使用异步IO。第3节中的“全部”似乎包含了相当多的信息。我想这可能对你最有用

以下是一些我认为您应该能够根据自己的使用情况进行调整的代码:

...

#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创建更高性能的系统,但这些系统自然可能更复杂,这应该适用于基本用例。

更新了问题,并对应用程序进行了简短描述。谢谢。也许你在找inotify。@Dabo,我对inotify的理解是,它会在被监视文件发生事件时发出通知。不要认为它会通知数据是否已准备好读取,您认为如何?为什么要轮询文件?常规文件应随时准备好阅读和书写。如果是文件服务器,需要将文件发送到请求客户端,为什么要读取文件?请注意,将始终将普通磁盘文件描述符视为可读或可写。与常规文件相关联的文件描述符应始终为“ready to read”、“ready to write”和“error”条件选择“true”。如果磁盘I/O阻塞是可接受的,则立即读取真实文件即可;如果没有,您必须使用线程,这是AIO库内部所做的。非常感谢您提供的示例代码!在回调函数中,从文件读取的数据块在哪里?在将数据发送到套接字之前,应用程序需要对数据进行一些清理。@codingFun AIO的要点是它为您处理IO部分。一旦你在回调函数中,文件已经被读取,现在被放在缓冲区中。我根据你的代码片段创建了一个可编译的程序,它崩溃了。知道为什么吗?谢谢代码在