Is it possible that linux file descriptor 0 1 2 not for stdin, stdout and stderr?

At the file descriptor level, stdin is defined to be file descriptor 0, stdout is defined to be file descriptor 1; and stderr is defined to be file descriptor 2. See this.

Even if your program -or the shell- changes (e.g. redirect with dup2(2)) what is file descriptor 0, it always stays stdin (since by definition STDIN_FILENO is 0).

So of course stdin could be a pipe or a socket or a file (not a terminal). You could test with isatty(3) if it is a tty, and/or use fstat(2) to get status information on it.

Syscalls like open(2) or pipe(2) or socket(2) may give e.g. STDIN_FILENO (i.e. 0) if that file descriptor is free (e.g. because it has been close(2)-d before). But when that occurs, it is still stdin by definition.

Of course, in stdio(3), the FILE stream stdin is a bit more complex. Your program could fclose(3), freopen(3), fdopen(3) ...

Probably the kernel sets stdin, stdout, and stderr file descriptors to the console when magically starting /sbin/init as the first process.


Though a couple of answers already existed but I didn't find them informative enough that they explained the complete story.

Since I went ahead and researched more, I am adding my findings.

Whenever a process starts, an entry of the running process is added to the /proc/<pid> directory. This is the place where all of the data related to the process is kept. Also, on process start the kernel allocates 3 file-descriptors to the process for communication with the 3 data streams referred to as stdin, stdout and stderr.
the linux kernel uses an algorithm to always create a FD with the lowest possible integer value so these data-streams are mapped to the numbers 0, 1 and 2.

Since these are nothing but references to a stream and we can close a stream. One can easily call close(<fd>), in our case close(1), to close the file descriptor.

on doing ls -l /proc/<pid>/fd/, we see only 2 FDs listed there 0 and 2.
If we now do an open() call the kernel will create a new FD to map this new file reference and since kernel uses lowest integer first algorithm, it will pick up the integer value 1.

So now, the new FD created points to the file we opened (using the open() syscall)
Any of the data transfer that happens now is not via the default data-stream that was earlier linked but the new file that we opened.

So yes, we can map the FD 0, 1 or 2 to any of the file and not necessary stdin, stdout or stderr