【问题标题】:Efficient way to handle/define the array of struct pollfd for poll syscall为 poll syscall 处理/定义 struct pollfd 数组的有效方法
【发布时间】:2013-05-21 00:15:54
【问题描述】:

我正在使用 poll 来实现具有多路复用的客户端-服务器模型的服务器端。

服务器正在运行一个主循环。
在每个循环中,我都会检查 struct pollfd 数组中的所有 fd,直到找到包含内容的 n 套接字(其中 n 是 poll 返回的数字)。
另外,poll 是使用时没有超时(-1 作为参数)。

因此,如果在侦听套接字上请求新连接,我会将套接字和一些客户端信息插入到我保留的活动连接列表中。

处理投票数组最有效的方法是什么?

我应该定义一个大小为 10 的小数组,如果有更多的客户端为大小为 20 或 50 的数组重新分配内存?

或者我应该:(a) 释放 struct pollfd 类型的数组,并且 (b) 重新分配它的大小等于列表的大小(侦听套接字 +1)=> 每次客户端关闭一个连接(因此我必须从数组中删除一个元素(可能将套接字设置为-1,以便 poll 忽略它)导致数组中未使用空间)

你有什么推荐的?

感谢您的宝贵时间


编辑: 我想我已经找到了一个使用 realloc 的解决方案,并在每次客户端断开连接以覆盖 fds 数组中的套接字时使用 memmove 进行转移。

【问题讨论】:

  • 我认为我们需要更多信息。 1. 您希望最多同时处理多少个客户?它是一个很大的数字(例如10,000)吗? 2. 客户端连接/断开连接的频率如何?他们的模式是什么?他们是否打开连接、发送命令、收到响应然后立即断开连接?
  • @undefinedbehaviour: 1. 不多,因为这是一项任务。客户可能会在大约 5 分钟内完成他的工作。不,还有一些交互,因为它是一个文件同步应用程序。我有事,请看更新。
  • 您的程序是否需要满足特定的性能标准?如果是这样,请提供标准和证据证明poll 是您的应用程序中最重要的瓶颈(例如,使用分析器)。
  • @undefinedbehaviour:它通常应该很快(这就是我们使用线程池多路复用的原因),但除此之外别无其他。他们只是告诉我服务器不应该浪费空间(例如,如果我分配了一个大小为 1024 的数组)。
  • 我认为涉及线程池 来处理仅仅50 个客户端的设计比涉及固定大小的50 struct pollfds 数组的设计浪费得多。我会考虑使用分析器来确定哪个是暴露并发的最佳位置。是 50 还是 1024? “不多”有多少?

标签: c sockets client-server polling


【解决方案1】:

处理 poll 数组最有效的方法是什么?

实现定义。在您知道必须优化之前不要优化;这称为过早优化,这是浪费时间。

我是否应该定义一个大小为 10 的小数组以及是否有更多客户端 为大小为 20 或 50 的数组重新分配内存?

如果您的分析器确定您的pollfds 是您程序中的一个重大瓶颈,并且您的老板(或教授)说您的程序“不够快”,那就去吧。如果您的列表将获得的最大列表是 50,那么只需使用静态数组,将 fd 成员设置为 -1,当您从数组中删除时,不要费心转移东西......你指的是瓶颈对于这么小的数字来说,这将是微不足道的。

如果您在最坏的情况下尝试处理大量客户端,您可能会担心在处理较小数字时数组尾随未使用的空间......因此会选择可调整大小的 pollfd 数组。

或者我应该:(a) 释放 struct pollfd 类型的数组,并且 (b) 重新分配它的大小等于列表的大小(+1 监听套接字)=> 每次客户端关闭连接时(和 因此我必须从数组中删除一个元素(可能设置 socket 到 -1,因此 poll 忽略它)导致未使用的空间 数组)

我能想到的最简单的算法在调整大小时将空间加倍。与线性调整大小相比,加倍调整大小的最大好处可能是减少了对realloc 的调用;与其接受 1024 连接并调用 realloc 1024 次,我更愿意接受 1024 连接并调用 realloc 10 或 11 次。这对我来说似乎也更简单,因为不需要将数组容量单独存储到已用计数;您可以使用二进制数的属性来发挥自己的优势:(n - 1) & n == 0 n 是 2 的幂。

#define is_power_of_two(n) !(((n) - 1) & (n))

struct pollfd *pollfd_array_resize(struct pollfd *array, size_t size) {
    const size_t max_size = SIZE_MAX / sizeof *array;
    if (size == max_size) { return NULL; }
    if (is_power_of_two(size)) {
        array = realloc(array, (size > 0 ? size * 2 : 1) * sizeof *array);
    }
    return array;
}

编辑:我想我已经找到了一个使用 realloc 的解决方案,并在每次客户端断开连接以覆盖 fds 处的套接字时使用 memmove 进行转移 数组。

这似乎是一个相当不错的解决方案。它增加了缓存局部性,但代价是每次客户端断开连接时都必须调用 memmoverealloc

在我的分析器指出poll 占用了太多处理器时间之前,我什至不会考虑对数组进行“碎片整理”。发生这种情况时,我会考虑将fd 成员设置为负值(就像您在编辑之前正在所做的那样)并将要删除的项目的索引放入recently_freed 堆栈中。插入时,如果可能,我会从该堆栈中选择项目。如果您必须进行碎片整理,我建议您根据堆栈的大小进行。

size_t pollfd_array_defrag(struct pollfd *array, size_t size) {
    size_t new_size = 0;
    for (size_t x = 0; x + 1 < size; x++) {
        if (array[x].fd < 0) {
            continue;
        }

        array[new_size++] = array[x];
    }
    return new_size;
}

int main(void) {
    size_t size = 0, index;
    struct pollfd *array = NULL;
    struct index_stack *recently_freed = NULL;

    /*----------------------------*
     * ... snip for insertion ... */
    if (recently_freed && recently_freed->size > 0) {
        index = index_stack_pop(&recently_freed);
    }
    else {
        struct pollfd *temp = pollfd_array_resize(array, size);

        if (temp == NULL) {
            /* TODO: Handle memory allocation errors */
        }

        array = temp;
        index = size++;
    }

    array[index] = (struct pollfd) { .fd = your_fd,
                                     .events = your_events,
                                     .revents = your_revents };

    /*--------------------------*
     * ... snip for removal ... */
    index_stack_push(&recently_freed, index);
    array[index] = -1;

    /*----------------------------------*
     * ... snip for defragmentation ... */
    if (is_power_of_two(recently_freed->size) && recently_freed->size * 2 + 1 > size) {
        size = pollfd_array_defrag(array);
        /* array will shrink in future insertions */
        index_stack_destroy(&recently_freed);
        recently_freed = NULL;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-27
    • 1970-01-01
    • 2013-05-16
    • 2020-05-09
    相关资源
    最近更新 更多