繁体   English   中英

在选择将其报告为可写时,可以将()写入非阻塞FD Return eagain?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM