2012-11-24 8 views
3

OS X (10.8.2)에서 이상한 동작을 추적하려고합니다. 기본적으로 필자는 파이프를 열어서 쓰기가 불가능해질 때까지 데이터로 채 웁니다. 그러나 내가 쓰려고 시도한 청크의 크기에 따라 select() 파이프가 여전히 쓰기 가능하다고 주장하더라도 write() 호출에서 EAGAIN을 얻을 것입니다. 여기비 차단 fd에 write()를 할 수 있습니까? 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"); 
    } 

} 

출력됩니다 : 여기에 몇 가지 테스트 코드는 실패 케이스 (3)은 쓰기()가 호출이 -1 반환하지만 선택 경우 여전히 파일 핸들이 쓰기 가능한 것으로보고

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 

하는 것으로 .

(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 

이 버그 사양의 오해 인 경우 내가 확실히 말할 수 없다 :

여기에 같은 실패를 보여줍니다 루비의 짧은 예이다. 생각?

+1

Mac OS X 10.7.5에서 나는 도움이되는지 잘 모르겠다. (미안하지만 주석이 있어야하고 형식이 형편 없다) :'1 : byte count 15873 바이트 카운트 15,873이 통과 4 : 바이트 수 15,876이 통과 5 바이트 카운트 15,875이 통과 6 바이트 카운트 15,876이 통과 7 : 바이트 수 15,876, PASS 3 패스 바이트 15,874 카운트 : 2 PASS (14)을 통과 할 바이트 수 15,873 : (13)을 통과 할 바이트 수 15,876 : (12)을 통과 할 바이트 수 15,873 : (11)을 통과 할 바이트 수 15,880 : (10)을 통과 할 바이트 수 15,876 : 바이트 수 15,880는 9 합격 : 바이트 c ount 15876, PASS 15 : 바이트 수 15885, 패스 '. –

+1

'select()'의 POSIX 스펙은 "O_NONBLOCK clear 함수를 사용하여 출력 함수를 호출하면 함수가 데이터를 성공적으로 전송하는지 여부에 관계없이 쓰기가 준비된 것으로 간주됩니다" 'select()'가 얼마나 많은 바이트가 출력 요청인지 알 수 없으므로 그 결정을 어떻게 내릴 수 있는지 모르겠습니다. 스펙에서는 "O_NONBLOCK 클리어로 1 바이트를 전송하는 출력 함수에 대한 호출이 차단되지 않을 것입니다 ..."라고 말할 것입니다. –

+0

10.8.2에서 오류가 발생했기 때문에 Mountain Lion에 도입 된 오류라고 생각합니다. ML은 또한 64kB로 파이프를 확장하는 "특징"을 가지고 있는데, 이는 모든 실패들 사이에서 공통적 인 것처럼 보입니다. –

답변

3

여기에 파이프를 사용하고 있습니다. 파이프는 재미있는 원자 쓰기 특성을 가지고 있습니다. PIPE_BUF (여기 4096) 바이트보다 작게 작성하면 바이트가 원자적일 수 있습니다. 따라서 파이프에 더 적은 수의 바이트를 쓸 수있는 경우에도 EAGAIN을 사용하여 파이프에 쓰는 것이 실패 할 수 있습니다.

내가 여기에 뛰어 다니고 있는지 (나는 너무 가깝게 보지 못했다) 확실하지는 않지만이 행동은 사람 7 파이프에 기록되어있다.

관련 문제