When non-blocking I/O is turned on for stdout, is it correct for the OS to turn it on for stdin too?

c, linux, macos, posix

Solution

When a process is started from the shell, `stdin`, `stdout` and `stderr` point to the same file description. This file description is marked as `O_NONBLOCK` by your `fcntl(1)` call and therefore expectedly both file descriptors are marked as `O_NONBLOCK`.

If you want to indeed write to the same file from two file descriptors but want one to be marked as `O_NONBLOCK`, you need to create a new file description for the same file. If you know the file name of the file in question, you can achieve this by simply calling `open()` with the desired file name and flags. On some operating systems, you can find the file name using a platform-specific API, such as the `/proc/fd` virtual file system (many Unices including Linux) or the `fd2path()` function from Plan 9. Refer to this question for more details.

Note that simply calling `open("/dev/fd/0", ...)` may not work as intended as it returns a pointer to the same file description on some platforms if I recall correctly.

Problem

I have noticed some unexpected behaviour on both OS X and Linux. Turning on non-blocking I/O (using `O_NONBLOCK`) for standard output turns it on for standard input too! Are these OSes behaving correctly? If so, is this behaviour defined by POSIX? Please point me to the relevant documentation if this is the case. Here's a example program I used to test this: ``` #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> int main (int argc, char *argv[]) { int flags = fcntl(STDOUT_FILENO, F_GETFL); if (argc > 1 && strcmp(argv[1], "1") == 0) { fcntl(STDOUT_FILENO, F_SETFL, flags | O_NONBLOCK); } printf("stdout O_NONBLOCK is: %d\n", fcntl(STDOUT_FILENO, F_GETFL) & O_NONBLOCK); printf("stdin O_NONBLOCK is: %d\n", fcntl(STDIN_FILENO, F_GETFL) & O_NONBLOCK); return 0; } ``` On OS X: ``` $ clang -o fd-test fd-test.c $ ./fd-test stdout O_NONBLOCK is: 0 stdin O_NONBLOCK is: 0 $ ./fd-test 1 stdout O_NONBLOCK is: 4 stdin O_NONBLOCK is: 4 ``` On Linux: ``` $ gcc -o fd-test fd-test.c $ ./fd-test stdout O_NONBLOCK is: 0 stdin O_NONBLOCK is: 0 $ ./fd-test 1 stdout O_NONBLOCK is: 2048 stdin O_NONBLOCK is: 2048 ```

Original source

Related problems