微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 `poll()`

如何解决使用 `poll()`

我想检查文件描述符是否会在某个事件上阻塞,我想到了使用 poll() 和 0 超时的想法:

int wouldblock(int fd,short event)
{
  struct pollfd pfd;
  pfd.fd = fd;
  pfd.events = event;
  return (poll(&pfd,1,0) == 0);
}

...
  if (wouldblock(0,POLLIN)) ...
...

要么流可用,poll() 应该返回 1,否则它会阻塞并且超时将返回 0。(让我们暂时搁置错误检查)。

它有效(至少“它在我的机器上有效”)但我想知道我是否遗漏了什么? 或许 poll() 太过分了,我对系统的压力太大了?

解决方法

我想知道我是否遗漏了什么?

poll 可以立即返回,操作仍然可以阻塞。这可能是因为从 poll() 返回和开始操作之间存在时间。这种情况发生在发生改变连接到文件描述符的事物的状态的事件,并且该事件发生在您的进程从 poll() 返回并即将开始操作之后。

最常见的情况,两个进程同时从一个管道 poll()ing 和 read()ing - 可能发生 poll 都返回,但只有一个 read 获胜。

#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <poll.h>
#include <sys/wait.h>
int main(void)
{
    system("mkfifo /tmp/fifo ; echo > /tmp/fifo & sleep 0.1");
    fork();
    int fd = open("/tmp/fifo",O_RDWR);
    sleep(1);
    printf("poll=%d\n",(int)poll(&(struct pollfd){fd,POLLIN},1,0));
    sleep(1);
    printf("read=%d\n",(int)read(fd,(char[1]){},1));
    printf("%d exiting!\n",(int)getppid());
    wait(0);
}

有足够的运气输出:

poll=1
poll=1
read=1
136769 exiting!

另一个 fork()ed 进程正在等待 read

我记得在内核邮件列表上也看到过一个线程,该线程具有丢弃旧网络数据包以防止停顿的功能。有了这样的功能,程序可以:poll() 在网络套接字上,然后内核决定丢弃数据包,然后进程执行 read() 只是为了发现数据包不再存在。

使用 poll() 作为“通知”系统 - poll() 返回文件描述符,您应该“检查”它们是否有可读取的内容。然后,您应该通过调用 read() 来检查“真实”是否有要阅读的内容。

在实际应用中,我会移动“wouldblock”函数up one layer,而不是检查文件描述符是否会阻塞,我会检查是否收到了我使用的任何协议的完整逻辑数据包。将其称为 has_received_a_packet,它将接收带有 read()O_NONBLOCK 的数据,在缓冲区中累积并检查是否收到了完整的数据包。

也许 poll() 太过分了,我对系统的压力太大了?

我相信是这样,您可以直接拨打 read 并立即接收该数据。类似于 ungetc 的实现方式:

struct buf {
   char data;
   bool hasit;
};
int readbuf_has_something(struct buf *readbuf,int fd) {
    if (!readbuf->hasit) {
        set_nonblock(fd);
        ssize_t r = read(fd,&readbuf->data,1);
        if (r == 1) readbuf->hasit = 1;
        if (ret < 0) return -EIO;
    }
    return readbuf->hasit;
}
int readbuf_something(struct buf *readbuf,int fd) {
    if (readbuf->hasit) {
      readbuf->hasit = 0;
      return readbuf.data;
    }
    set_block(fd);
    ssize_t r = read(fd,1);
    if (r == 1) return readbuf->data;
    return EOF;
}

与使用 poll 后跟 read 相比,这种实现会导致更少的上下文更改 - 只需读取数据,它们就在那里。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。