2017-05-17 14 views
4

:없는 것처럼 첫 번째의 printf없이표준 입출력 다음 프로그램에서 첫 번째의 printf 문은 그 이후의 행동을 변경하는 것이 왜 주석을 내가 궁금

#include <unistd.h> 
#include <stdio.h> 
#include <string.h> 
#include <errno.h> 

int main() { 
    //printf("hi from C \n"); 

    // Close underlying file descriptor: 
    close(STDOUT_FILENO); 

    if (write(STDOUT_FILENO, "Direct write\n", 13) != 13) // immediate error detected. 
    fprintf(stderr, "Error on write after close(STDOUT_FILENO): %s\n", strerror(errno)); 

    // printf() calls continue fine, ferror(stdout) = 0 (but no write to terminal): 
    int rtn; 
    if ((rtn = printf("printf after close(STDOUT_FILENO)\n")) < 0 || ferror(stdout)) 
    fprintf(stderr, "Error on printf after close(STDOUT_FILENO)\n"); 
    fprintf(stderr, "printf returned %d\n", rtn); 
    // Only on fflush is error detected: 
    if (fflush(stdout) || ferror(stdout)) 
    fprintf(stderr, "Error on fflush(stdout): %s\n", strerror(errno)); 
} 

, 이후의 printf rtns (34) stdout 사용자 버퍼에서 기본 fd 로의 연결이 닫혔더라도 오류가 발생했습니다. 수동 fflush (stdout)에서만 오류가 다시보고됩니다. 하지만 첫 번째 printf가 켜져 있으면 다음 printf는 내가 예상 한대로 오류를보고합니다. 물론 두 경우 모두 STDOUT_FILENO fd가 닫힌 후에는 아무 것도 (printf에 의해) 터미널에 기록되지 않습니다.

처음에는 close(STDOUT_FILENO)에 어리 석다는 것을 알고 있습니다. 이것은 내가 우연히 마주 쳤던 실험이고이 분야에 대해 더 잘 알고있는 누군가가 우리에게 그것에 대해 유익한 것을 볼 수 있다고 생각합니다 ..

저는 gcc를 사용하여 Linux를 사용하고 있습니다. 다음은 터미널의 경우 -

+0

기본 디스크립터 'STDOUT_FILENO'를 닫을 때'stdout' 상태가 잘못되었습니다.'fclose (stdout)'로 올바르게 작동합니다. –

답변

4

당신 strace 둘 다 프로그램의 경우, stdio 작품 쓰기에 따라 있도록이 파일의 종류가 stdout에 연결되고 있는지 확인하려면 fstat와 기술자를 확인 보인다 stdout라인 버퍼가이어야하며, 다른 것이면 stdout이 블록 버퍼가됩니다. 첫 번째 printf 앞에 close(1);을 호출하면 처음 fstatEBADF을 반환하고 1은 문자 장치를 가리키는 파일 설명자가 아니기 때문에 stdout을 블록 버퍼로 만듭니다.

내 컴퓨터에서 버퍼 크기는 8192 바이트입니다. 많은 바이트를 버퍼링하여 stdout에 기록한 후 첫 번째 오류가 발생합니다.


첫 번째 printf 주석을 제거하면

fstat(1, ...) 성공하고 Glibc의이 stdout 터미널에 연결되어 있음을 감지; stdout은 라인 버퍼로 설정되어 있기 때문에 printf after close(STDOUT_FILENO)\n이 개행 문자로 끝나기 때문에 버퍼가 즉시 플러시됩니다. 즉각적인 오류가 발생합니다.

+0

이상한 점은 1,024자를 인쇄하면 상태가 변경된다는 것입니다. printf를 반복하면 쉽게 이것을 나타낼 수 있습니다. –

+1

예, 당신이 설명했던'fstat' 호출을 통한 버퍼의 내부 설정은'man setbuf'가 말하는 것과 일치하는 것으로 보입니다 : "setvbuf() 함수는 스트림을 열고 다른 연산이 수행되기 전에 만 사용될 수 있습니다 그것을 수행했다. " 첫 번째'printf'를'setlinebuf (stdout);로 바꾸면 똑같은 동작을합니다. –

+0

그래서''man 2 write'의 Notes에 나타나는 것으로서 printf에 적용되는 것 같습니다 : "write()로부터의 성공적인 반환은 데이터가 디스크에 커밋되었음을 보증하지 않습니다." 'write()'를 'printf()'로 대체하고 'disk'를 'kernel file buffer'로 대체합니다. 여기서'fflush'와'fsync'는 아날로그가됩니다. 나는 커널 파일 버퍼에서 디스크로 연결을 끊는'close (fd) '에 대한 아날로그를 모른다. –