使用 `poll()` 检查阻塞
Checking Blocking with `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() 应该 return 1,否则它将阻塞并且超时将开始 returning 0。(让我们暂时搁置错误检查).
它有效(至少“它在我的机器上有效”)但我想知道我是否遗漏了什么?
也许 poll()
有点矫枉过正,我对系统的压力太大了?
I wonder if I did miss anything?
poll
可以立即return,而且操作还可以阻塞。发生这种情况是因为从 poll()
开始 return 和开始操作之间有时间。当一个事件发生改变了连接到文件描述符的事物的状态时,就会发生这种情况,并且该事件发生在您的进程从 poll()
开始 returns 并且即将开始操作之后。
最常见的情况,两个进程同时从一个管道 poll()
ing 和 read()
ing - 可能发生两个 poll
return,但只有一个 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()
returns 文件描述符,您应该“检查”它们是否有可读取的内容。然后你应该通过调用 read()
.
检查“真实的”是否有可读的东西
在实际应用程序中,我会移动“wouldblock”函数 up one layer,而不是检查文件描述符是否会阻塞,我会检查我使用的任何协议中的完整逻辑数据包是否是已收到。将其称为 has_received_a_packet
,它将接收带有 read()
和 O_NONBLOCK
的数据,在缓冲区中累积并检查是否收到了完整的数据包。
Maybe poll() is overkill and I'm stressing the system too much?
我相信是的,您可以直接调用 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, &readbuf->data, 1);
if (r == 1) return readbuf->data;
return EOF;
}
与使用 poll
后跟 read
相比,这样的实施方式会减少一次上下文更改 - 只需读取数据,它们就在那里。
我想检查文件描述符是否会在某个事件上阻塞,我想到了使用 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() 应该 return 1,否则它将阻塞并且超时将开始 returning 0。(让我们暂时搁置错误检查).
它有效(至少“它在我的机器上有效”)但我想知道我是否遗漏了什么?
也许 poll()
有点矫枉过正,我对系统的压力太大了?
I wonder if I did miss anything?
poll
可以立即return,而且操作还可以阻塞。发生这种情况是因为从 poll()
开始 return 和开始操作之间有时间。当一个事件发生改变了连接到文件描述符的事物的状态时,就会发生这种情况,并且该事件发生在您的进程从 poll()
开始 returns 并且即将开始操作之后。
最常见的情况,两个进程同时从一个管道 poll()
ing 和 read()
ing - 可能发生两个 poll
return,但只有一个 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()
returns 文件描述符,您应该“检查”它们是否有可读取的内容。然后你应该通过调用 read()
.
在实际应用程序中,我会移动“wouldblock”函数 up one layer,而不是检查文件描述符是否会阻塞,我会检查我使用的任何协议中的完整逻辑数据包是否是已收到。将其称为 has_received_a_packet
,它将接收带有 read()
和 O_NONBLOCK
的数据,在缓冲区中累积并检查是否收到了完整的数据包。
Maybe poll() is overkill and I'm stressing the system too much?
我相信是的,您可以直接调用 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, &readbuf->data, 1);
if (r == 1) return readbuf->data;
return EOF;
}
与使用 poll
后跟 read
相比,这样的实施方式会减少一次上下文更改 - 只需读取数据,它们就在那里。