[英]why non-blocking write to disk doesn't return EAGAIN or EWOULDBLOCK?
[英]Can write() to a non-blocking fd return EAGAIN when select reports it as writable?
我正在尝试追踪 OS X (10.8.2) 中的一些奇怪行为。 基本上,我正在打开一个管道,并用数据填充它直到它不可写。 然而,我发现根据我尝试写入的块的大小,有时我会从 write() 调用中获得 EAGAIN,即使 select 声称管道仍然可写。 下面是一些测试代码:
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/select.h>
#define START 1
#define END 16
int is_writeable(int fd) {
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
fd_set ws;
FD_ZERO(&ws);
FD_SET(fd, &ws);
if(select(fd+1, NULL, &ws, NULL, &timeout) < 0) {
return -1;
}
if(FD_ISSET(fd, &ws))
return 1;
else
return 0;
}
int main(int argc, char *argv[]) {
int pipes[2];
int rp, wp, i, errval, fails, tmp;
char testbuf[END];
char destbuf[128000];
for(i = START; i < END; i++) {
int byte_count = 0;
printf("%i: ", i);
fails = 0;
pipes[0] = 0;
pipes[1] = 0;
if(pipe(pipes) < 0) {
printf("PIPE FAIL\n");
break;
}
rp = pipes[0];
wp = pipes[1];
int flags = fcntl(wp, F_GETFL, 0);
if(fcntl(wp, F_SETFL, flags | O_NONBLOCK) == -1) {
fails = 4;
}
if(is_writeable(wp) != 1) {
fails = 1;
}
while(!fails) {
// printf(".");
if(is_writeable(wp) < 1) {
break;
}
tmp = write(wp, testbuf, i);
//No bytes written, fail
if(tmp < 0) {
if(errno == EAGAIN) {
if(is_writeable(wp) == 1) {
fails = 3;
break;
}
} else {
fails = 2;
perror("During write");
break;
}
} else {
byte_count += tmp;
}
//Errno is eagain, fail
}
printf("byte count %i, ", byte_count);
if(fails)
printf("FAIL, %i\n", fails);
else
printf("PASS\n");
if(close(wp) != 0)
printf("WP CLOSE FAIL\n");
if(close(rp) != 0)
printf("RP CLOSE FAIL\n");
}
}
这是输出:
1: byte count 16384, PASS
2: byte count 16384, PASS
3: byte count 65535, FAIL 3
4: byte count 16384, PASS
5: byte count 65535, FAIL 3
6: byte count 65532, FAIL 3
7: byte count 65534, FAIL 3
8: byte count 16384, PASS
9: byte count 65529, FAIL 3
10: byte count 65530, FAIL 3
11: byte count 65527, FAIL 3
12: byte count 65532, FAIL 3
13: byte count 65533, FAIL 3
14: byte count 65534, FAIL 3
15: byte count 65535, FAIL 3
请注意,失败案例 3 是 write() 调用返回 -1,但 select 仍报告文件句柄可写。
这是 Ruby 中一个较短的示例,显示了相同的失败:
(1..10).each do |i|
passes = true
begin
(rp, wp) = IO.pipe
wp.write_nonblock ("F" * i) while(select [], [wp], [], 0)
rescue Errno::EAGAIN
puts "#{i}: FAIL"
passes = false
ensure
rp.close
wp.close
end
puts "#{i}: PASS" if passes
end
我不能确定这是一个错误还是对规范的误解。 想法?
你在这里使用管道。 管道有一个有趣的原子写入属性 --- 小于 PIPE_BUF(此处为 4096)字节的写入保证是原子的。 因此,即使可以向管道写入较少数量的字节,写入管道也可能会因 EAGAIN 而失败。
我不确定这是否是您在这里遇到的情况(我没有仔细观察),但这种行为记录在 man 7 pipe 中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.