c 中令人恼火的 select() 行为

发布于 2024-07-08 23:58:00 字数 580 浏览 7 评论 0原文

while (xxx) {
    timeout.tv_sec=TIMEOUT;
    timeout.tv_usec=0;
    FD_ZERO(&set); 
    FD_SET(sd,&set);

    switch (select(FD_SETSIZE,&set,NULL,NULL,&timeout))
    xxxxx
}

工作正常,但

FD_ZERO(&set); 
FD_SET(sd,&set);

while (xxx) {
    timeout.tv_sec=TIMEOUT;
    timeout.tv_usec=0;

    switch (select(FD_SETSIZE,&set,NULL,NULL,&timeout))
    xxxxx
}

不行。 它第一次工作,但下次运行 while 循环时,即使 sd 套接字接收到数据,它也会超时。 在我看来,每次都必须清空并填充集合是对资源的浪费。

任何人都可以很好地解释为什么会出现这种情况,甚至更好,也许可以建议如何避免这种情况?

while (xxx) {
    timeout.tv_sec=TIMEOUT;
    timeout.tv_usec=0;
    FD_ZERO(&set); 
    FD_SET(sd,&set);

    switch (select(FD_SETSIZE,&set,NULL,NULL,&timeout))
    xxxxx
}

works fine, however

FD_ZERO(&set); 
FD_SET(sd,&set);

while (xxx) {
    timeout.tv_sec=TIMEOUT;
    timeout.tv_usec=0;

    switch (select(FD_SETSIZE,&set,NULL,NULL,&timeout))
    xxxxx
}

doesn't. It works the first time around, but the next time it runs through the while loop it gets a timeout even if the sd socket receives data. It seems to me to be a waste of resources to have to empty and fill set every time.

Anybody have a good explanation why this is, and even better, perhaps a suggestion how to avoid it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

固执像三岁 2024-07-15 23:58:00

select 修改其参数。 您确实每次都必须重新初始化它。

如果您担心开销,那么在内核中处理完整 FD_SET 的成本比 FD_ZERO 的成本要大一些。 您只想传递最大 fd,而不是 FD_SETSZIZE,以最小化内核处理。 在您的示例中:

switch (select((sd + 1),&set,NULL,NULL,&timeout))

对于具有多个 fd 的更复杂的情况,您通常最终会维护一个 max 变量:

FD_SET(sd,&set);
if (sd > max) max = sd;
... repeat many times...

switch (select((max + 1),&set,NULL,NULL,&timeout))

如果您有大量文件描述符并且担心拖拽它们的开销,您应该考虑 select() 的一些替代方法。 你没有提到你正在使用的操作系统,但对于类 Unix 操作系统,有几个:

  • 对于 Linux,epoll()
  • 对于 FreeBSD/NetBSD/OpenBSD/MacOS X,kqueue()
  • ,/dev/poll

对于Solaris API 有所不同,但它们本质上都是一个有状态的内核接口,用于维护一组活动文件描述。 一旦将一个 fd 添加到集合中,您将收到该 fd 上的事件通知,而无需不断地再次传递它。

select modifies its arguments. You really do have to re-initialize it each time.

If you're concerned about overhead, the cost of processing the complete FD_SET in the kernel is somewhat more significant than the cost of FD_ZERO. You'd want to only pass in your maximum fd, not FD_SETSZIZE, to minimize the kernel processing. In your example:

switch (select((sd + 1),&set,NULL,NULL,&timeout))

For a more complex case with multiple fds, you typically end up maintaining a max variable:

FD_SET(sd,&set);
if (sd > max) max = sd;
... repeat many times...

switch (select((max + 1),&set,NULL,NULL,&timeout))

If you will have a large number of file descriptors and are concerned about the overhead of schlepping them about, you should look at some of the alternatives to select(). You don't mention the OS you're using, but for Unix-like OSes there are a few:

  • for Linux, epoll()
  • for FreeBSD/NetBSD/OpenBSD/MacOS X, kqueue()
  • for Solaris, /dev/poll

The APIs are different, but they are all essentially a stateful kernel interface to maintain a set of active file descriptions. Once an fd is added to the set, you will be notified of events on that fd without having to continually pass it in again.

病毒体 2024-07-15 23:58:00

阅读选择的手册页。 返回的集合只是准备使用的文件描述符。 您应该使用 FD_ISSET 检查每一项是否已设置。

始终在使用 fd_set 之前对其进行初始化。

Read the select man page. The returned set is only the file descriptors that are ready to be used. You are supposed to use FD_ISSET to check each one if it is set or not.

Always initialize the fd_set right before using it.

最冷一天 2024-07-15 23:58:00

这就是 select 的工作方式。 如果您有多个插座,那么它的效果最好,而且更有意义。 这就是要点:您正在许多套接字中进行选择。 如果您想从一个套接字读取数据,只需读取或接收它即可。

That's the way select works. It works best, and makes more sense, if you have more than one socket. That's kind of the point: you are selecting across many sockets. If you want to read from one socket, just read or recv it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文