2013-10-21 62 views
3

當代理功能fcntlstdin,當我設置stdin FD狀態標誌爲O_NONBLOCK時,我遇到了一個問題,它工作正常,但副作用。 stdout和stderr的狀態標誌也已更改爲O_NONBLOCKLinux C,爲什麼fcntl在STDIN上運行也會影響STDOUT和STDERR?

我調查的功能fcntlSYSCALL_DEFINE3do_fcntl的源代碼,但沒有什麼幫助。還stackoverflow或谷歌。我認爲它可能與內核或glibc實現有關。

我的電腦是x86_64上的Ubuntu 12.04,安裝了gcc 4.6.3。

int flag = 0; 
    int value = O_NONBLOCK; 
    int fd = open("./tmp", O_RDONLY); 

    if(-1 == (flag = fcntl(fd, F_GETFL))) 
     fprintf(stdout, "%d:%s\n", errno, strerror(errno)); 

    flag = fcntl(stdin->_fileno, F_GETFL); 
    flag = fcntl(stderr->_fileno, F_GETFL); 
    if(-1 == (flag = fcntl(stdout->_fileno, F_GETFL))) 
     fprintf(stdout, "%d:%s\n", errno, strerror(errno)); 

    flag = fcntl(stdout->_fileno, F_SETFL, flag | O_NONBLOCK); 

    flag = fcntl(fd, F_GETFL); 
    flag = fcntl(stdin->_fileno, F_GETFL); 
    flag = fcntl(stdout->_fileno, F_GETFL); 
    flag = fcntl(stderr->_fileno, F_GETFL); 

    flag = fcntl(stdin->_fileno, F_SETFL, flag | O_APPEND); 

    flag = fcntl(fd, F_GETFL); 
    flag = fcntl(stdin->_fileno, F_GETFL); 
    flag = fcntl(stdout->_fileno, F_GETFL); 
    flag = fcntl(stderr->_fileno, F_GETFL); 

    close(fd); 

這是我對這個問題的代碼。

+0

如何發佈一些代碼? –

+0

您是否嘗試過不使用tty(例如'echo「| |。/ your_program')? – SheetJS

+0

是的。謝謝!我只是'gdb a.out',並顯示標誌,當設置O_NONBLOCK爲stdout時,我得到的stdin和stderr都有O_NONBLOCK。它很困惑。〜 –

回答

3

傳統上由登錄過程(或終端打開過程)使用的'技巧'之一是以讀寫模式打開文件描述符0(標準輸入)的終端,然後複製文件描述符1和2(標準輸出和標準錯誤)。這意味着:

  1. 所有三個標準文件描述符共享相同的打開文件描述。
  2. 您可以寫入標準輸入並從標準輸出或標準錯誤讀取。
  3. 更改文件描述信息爲其中一個更改它。

fcntl()的F_GETFL和F_SETFL選項與打開的文件描述有關。 fcntl()的F_GETFD和F_SETFD選項與文件描述符有關。

一個給定的打開的文件的描述可以具有相關聯的若干文件描述符,無論是在單個進程內(後dup()dup2())或穿過(因爲fork())工藝。

+0

是的。謝謝,CSAPP說有三種類型的打開文件,文件描述符,xxx(我忘記它的名字,誰擁有報價計數和偏移量)和vnode。完全只有一個打開的文件描述符。非常感謝,幫助了很多。 –

1

從喬納森的回答之後,這裏的一些理智的代碼:

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <fcntl.h> 

void show_nonblock_status(void) { 
    char streams[3][7] = {"stdin", "stdout", "stderr"}; 

    for (int i = 0; i < 3; ++i) { 
     int flag = fcntl(i, F_GETFL); 
     if (flag == -1) { 
      perror("error getting flags"); 
      exit(EXIT_FAILURE); 
     } 

     if (flag & O_NONBLOCK) { 
      printf("O_NONBLOCK is set for %s\n", streams[i]); 
     } else { 
      printf("O_NONBLOCK is not set for %s\n", streams[i]); 
     } 
    } 
} 

int main(void) { 
    show_nonblock_status(); 

    int flag = fcntl(1, F_GETFL); 
    if (flag == -1) { 
     perror("error getting flags"); 
     exit(EXIT_FAILURE); 
    } 

    flag = fcntl(1, F_SETFL, flag | O_NONBLOCK); 
    if (flag == -1) { 
     perror("error getting flags"); 
     exit(EXIT_FAILURE); 
    } 

    show_nonblock_status(); 

    return 0; 
} 

這使得在各種用途中的以下的輸出:

[email protected]:~/src/c/scratch$ ./fct 
O_NONBLOCK is not set for stdin 
O_NONBLOCK is not set for stdout 
O_NONBLOCK is not set for stderr 
O_NONBLOCK is set for stdin 
O_NONBLOCK is set for stdout 
O_NONBLOCK is set for stderr 
[email protected]:~/src/c/scratch$ cat testfile | ./fct 
O_NONBLOCK is not set for stdin 
O_NONBLOCK is not set for stdout 
O_NONBLOCK is not set for stderr 
O_NONBLOCK is not set for stdin 
O_NONBLOCK is set for stdout 
O_NONBLOCK is set for stderr 
[email protected]:~/src/c/scratch$ cat testfile | ./fct 2>/dev/null 
O_NONBLOCK is not set for stdin 
O_NONBLOCK is not set for stdout 
O_NONBLOCK is not set for stderr 
O_NONBLOCK is not set for stdin 
O_NONBLOCK is set for stdout 
O_NONBLOCK is not set for stderr 
[email protected]:~/src/c/scratch$ 

在第一種情況下,該文件的描述是一致的,所以所有三個都設置爲O_NONBLOCK

在第二種情況下,一個文件被管道輸送到stdin,從而具有不同的文件描述到stdoutstderr,兩者都具有O_NONBLOCK集。

在第三種情況下,一個文件被管道輸送到stdin,和stderr被重定向到/dev/null,因此,所有3周文件的描述是不同的,並且O_NONBLOCK僅設置stdout

+0

明白了。謝謝,非常清楚。 –

相關問題