【问题标题】:Is there any way to associate a file descriptor with user defined data?有没有办法将文件描述符与用户定义的数据相关联?
【发布时间】:2011-11-17 23:15:38
【问题描述】:

我正在编写一个客户端-服务器应用程序,它使用 POSIX poll 函数来提供一种并发客户端处理的形式。客户端还有状态和其他相关数据,这些数据存储在客户端结构中。

我的直接问题是,当我从poll 得到提示要对与客户端关联的套接字文件描述符(从概念上)执行 I/O 时,我必须实际将文件描述符与其关联的客户端数据匹配结构体。目前我进行O(n_clients) 查找(我的客户端数据结构存储描述符),但我想知道是否存在更好的选择?

【问题讨论】:

  • 你有没有分析过你的程序,你发现这是瓶颈吗?
  • 不,我没有 :( 但是内存比 CPU 便宜你不觉得吗?我厌倦了我的笔记本电脑变热...

标签: c sockets posix


【解决方案1】:

没有。如果有,它必须由内核跟踪,因此查找该数据将涉及系统调用。系统调用的成本比在用户空间中进行 O(n) 查找要贵一个数量级。

您同时与多少客户打交道?除非它在数百或更多的数量级上,否则与执行任何类型的 I/O 的成本相比,查找的成本将是微不足道的。

除了使用 O(n) 查找之外,您还可以只使用由文件描述符索引的数组,假设您一次打开的描述符数量不超过一定数量。例如:

#define MY_MAX_FD 1024  // Tune this to your needs
void *per_fd_data[MY_MAX_FD];

void *get_per_fd_data(int fd)
{
    assert(fd >= 0);
    if(fd < MY_MAX_FD)
        return per_fd_data[fd];
    else
    {
        // Look up fd in a dynamic associative array (left as an exercise to the
        // reader)
    }
}

【讨论】:

  • +1 指出文件描述符可以合理地用作数组索引
  • @R 这不是一个很大的数组吗?我的意思是,POSIX 文件描述符是ints,对吧?
  • 顺便说一下@Adam,已经涉及到一个系统调用-poll,因此当您暗示需要额外调用来查找用户数据时,这并不完全正确。例如,如果pollfd 有空间存放void*,这将是用户定义的数据,而poll 调用将在返回时简单地“通过”该数据,那么我们就准备好了。
【解决方案2】:

最便宜的是只制作一个固定大小的连接结构数组,每个条目带有 {state, *context, ..., 也许回调函数},由 fd 索引 (=O(1 ))。内存很便宜,你可以承受几百或几千个文件描述符和表条目。

编辑:您不需要 使其固定大小。如果您的 pollstructure 或 fdset 是固定的:将其固定;否则使用 getdtablesize() 或 getrlimit() 来获取要分配的条目数。

【讨论】:

  • 数组的一个问题是,为了遍历有效的 fd,您还必须遍历所有无效的插槽......考虑到使用 select() 或 poll()通常必须在每个事件循环迭代中迭代 FD 至少两次(一次在 select() 之前进行 FD_SET() 调用,然后再进行 FD_ISSET() 调用)。最好使用地图(或类似地图)。
  • 不是迭代,而是索引。 “数组[fd].stuff”。记住 fd 是一个小的 int 值 [0, fd_max)。您只需要捕捉 fd= -1 的情况,这可能是 open()、listen() 或 accept() 的结果。您遭受的唯一迭代是来自 pollfd[] 或 fd_set 数组。
【解决方案3】:

如果您使用poll()select()/pselect(),那么您应该自己保留数据,例如正如其他人提到的那样,在哈希表或数组中。这是最便携的解决方案。一些替代界面确实可以关联您自己的用户数据。例如使用asynchronous I/O(例如aio_read()),您可以提供一个用户值sigev_value,在异步请求完成后可以将其传递给信号处理程序或线程。 Linuxepoll 接口还允许为集合中的每个文件描述符指定用户数据。

【讨论】:

  • 哈希表只有在你的哈希函数是识别函数并且你的桶都是大小为 1 时才是一个合理的解决方案... :-)
【解决方案4】:

除了所有其他非常有用的答案之外,我想提供以下信息,希望本着知识库的精神对其他人有用。

问题是,如果我们假设一个符合 POSIX 的系统 http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_14,它会指定以下内容(强调我的):

除非另有规定,所有打开一个或多个文件描述符的函数都应在每次执行时自动分配编号最小的可用(即尚未在调用进程中打开)文件描述符分配。在单个函数分配两个文件描述符(例如,pipe()socketpair())的情况下,分配可能是独立的,因此应用程序不应期望它们具有相邻的值或依赖于哪个具有更高的值。

这允许程序简单地保留一个数组,最多可以支持多个它想要支持的描述符,其中打开的描述符可以仅用作数组下标来引用诸如客户端连接相关数据之类的东西。基本上,此类系统上的打开文件描述符可以直接用作以数组形式实现的表的索引。毕竟,文件描述符编号不仅会从最低可用值向上增长,它们似乎也被重用了——如果你关闭描述符 10,而你仍然打开描述符 11 及更高版本,下次打开描述符时,POSIX-兼容的系统将打开索引为 10 的描述。这也使得重用 fd 索引表中的行非常简单。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-19
    • 1970-01-01
    • 2012-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多