![](/img/trans.png)
[英]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.