4

I have met a problem when acting function fcntl on stdin, when I set the stdin FD status flag to O_NONBLOCK, It works well but within a side effect. The status flag of stdout and stderr has also changed to O_NONBLOCK.

I investigated the source code of function fcntl, SYSCALL_DEFINE3 and do_fcntl, but got nothing helps. Also stackoverflow or google. I consider that it may related to kernel or glibc implementation.

My computer is Ubuntu 12.04 on x86_64, within gcc 4.6.3 installed.

  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);

This is my code for this problem.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294

2 Answers2

5

One of the 'tricks' traditionally used by the login process (or terminal opening process) is to open the terminal in read-write mode for file descriptor 0 (standard input), and then duplicate that for file descriptors 1 and 2 (standard output and standard error). This means that:

  1. All three standard file descriptors share the same open file description.
  2. You can write to standard input and read from standard output or standard error.
  3. Changing the file description information for one changes it for the others.

The F_GETFL and F_SETFL options to fcntl() are related to the open file description. The F_GETFD and F_SETFD options to fcntl() are related to the file descriptor.

A given open file description may have several file descriptors associated with, either within a single process (after dup() or dup2()) or across processes (because of fork()).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Yes. Thanks, CSAPP say there are three types of opened file, file descriptor, xxx(I forget its name, who holds quote count and offset) and vnode. There is only one open file descriptor exactly. Thanks very much, help a lot. – Incarnation P. Lee Oct 21 '13 at 03:56
2

Following from Jonathan's answer, here's some saner code:

#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;
}

which gives the following output under various uses:

paul@local:~/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
paul@local:~/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
paul@local:~/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
paul@local:~/src/c/scratch$

In the first case, the file description is the same, so O_NONBLOCK is set for all three.

In the second case, a file is piped to stdin, so that has a different file description to stdout and stderr, both of which have O_NONBLOCK set.

In the third case, a file is piped to stdin, and stderr is redirected to /dev/null, so all 3 file descriptions are different, and O_NONBLOCK is only set for stdout.

Crowman
  • 25,242
  • 5
  • 48
  • 56