3
我試圖追查下OS X(10.8.2)中的一些奇怪的行爲。基本上,我打開一個管道,並填充數據,直到它不可寫。然而,我發現根據我嘗試寫入的塊的大小,有時候我會從write()調用中獲得EAGAIN,即使select聲明管道仍然可寫。下面是一些測試代碼:當select選擇將其報告爲可寫時,可以將()寫入非阻塞fd返回EAGAIN?
#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,但仍然選擇報告的文件句柄是可寫的。
這裏是紅寶石更短的例子,顯示了同樣的故障:
(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
我不能告訴了肯定,如果這是一個錯誤或規範的誤解。思考?
我不知道它是否有幫助,但在Mac OS X 10.7.5上,我得到了輸出(對不起,它必須是註釋,格式會很糟糕):'1:byte count 15873 ,PASS 2:字節數15874,PASS 3:字節數15873,PASS 4:字節數15876,PASS 5:字節數15875,PASS 6:字節數15876,PASS 7:字節數15876,PASS 8:字節數15880,PASS 9:字節數15876,PASS 10:字節數15880,PASS 11:字節數15873,PASS 12:字節數15876,PASS 13:字節數15873,PASS 14 :字節c ount 15876,PASS 15:字節計數15885,合格 '。 –
對於'select()'的POSIX規範說,當清除O_NONBLOCK的調用對輸出函數的調用不會阻塞,函數是否會成功傳輸數據時,描述符應被視爲已準備好寫入「,但我不確定'select()'是如何做出這個決定的,因爲它不知道要輸出多少個字節。在我看來,規範應該可能會說,「...調用輸出函數傳輸一個字節與O_NONBLOCK清除不會阻止...」。 –
我在10.8.2中看到了失敗,所以我認爲這是在Mountain Lion中引入的錯誤。 ML還有一個「功能」,它將管道擴展到64kB,這在所有故障中似乎都是常見的。 –