A POSIX-compliant program can expect to inherit file descriptors #0, #1 and #2 (also known by programming constants stdin
, stdout
and stderr
, respectively) from its parent process, in an already-opened, ready-to-use state.
In the simplest case, of a command-line program in a session logged in on the text console, with no redirections applied, this chain of inheritance goes all the way back to the getty
process that initialized the TTY device for the login session.
When logging in using a GUI, the display manager process (gdm/kdm/sddm/lightdm/xdm/<whatever>dm
) will usually set the standard input and output to /dev/null
and standard error to $HOME/.xsession-errors
or something similar for the first process of the session, and these file descriptors are likewise inherited by all the GUI programs started in the session, either as parts of the desktop environment, or started using desktop menus or icons.
For e.g. SSH sessions, the sshd
process that forked to initialize the session would have allocated a pseudo-TTY device pair, pointed the stdin/out/err
file descriptors to one half of it, and then exec()
ed the user's shell. The other side of that fork will keep hold of the other half of the pseudo-TTY device pair, and will be handling the en/decryption of the outgoing/incoming traffic between the network and the pseudo-TTY device, until the session ends.
When a terminal emulator is started within a GUI session, it behaves essentially the same as the sshd
process when initializing a new session: it allocates a pseudo-TTY, fork()
s itself, and one copy sets up the session, including pointing file descriptors #0, #1 and #2 to the pseudo-TTY and finally exec()
s the user's shell, and the other side of the fork will remain handling the task of actually maintaining the visual representation of the terminal window.
So, in a nutshell, the (pseudo?)TTY device was connected to stdin/stdout/stderr by the thing that initialized your terminal session, and all the processes that may be between that and your application simply passed them down in a chain of inheritance, by doing nothing at all to those file descriptors, just letting them pass to their child process as-is.
When redirection operators are used in shell command line, as the shell fork()
s a temporary copy of itself in preparation to actually exec()
ing the command, just after the fork()
the temporary copy will close the respective file descriptor, open the thing specified by the redirection operator in its place, and then exec()
the command so that it will inherit the modified stdin/out/err file descriptor(s).
In some Unix-style systems /dev/std*
devices might be handled by the shell. But Linux makes them a bit more "real".
In Linux, /dev/stdin
, /dev/stdout
and /dev/stderr
are just plain old symbolic links pointing to the /proc
filesystem:
$ ls -l /dev/std*
lrwxrwxrwx 1 root root 15 Feb 4 08:22 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 Feb 4 08:22 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 Feb 4 08:22 /dev/stdout -> /proc/self/fd/1
These links are created as the udev
RAM-based /dev
filesystem is initialized at system boot-up. They are just regular plain symbolic links, nothing magical there.
But /proc
is an entirely virtual filesystem that reflects the state of the processes in the system in real-time, and so it has several "magical" properties:
/proc/self
is a symbolic link that points to the /proc/<PID>
directory of the process that looks at it:
$ ls -l /proc/self # the PID of this ls command will be 10839
lrwxrwxrwx 1 root root 0 Feb 4 08:22 /proc/self -> 10839/
$ ls -l /proc/self # the PID of this ls command will be 10843
lrwxrwxrwx 1 root root 0 Feb 4 08:22 /proc/self -> 10843/
/proc/<PID>/fd
is a directory that contains symbolic links with their names corresponding to file descriptors opened by the process with <PID>
, and pointing to whatever that file descriptor is associated with.
So, when a process on /dev/pts/10
tries to access /dev/stdin
, the symbolic link points it to /proc/self/fd/0
instead... and when /proc/self/fd/0
is accessed, the /proc
filesystem driver looks at the kernel's process table, uses it to find the file descriptor table of the process that is accessing it, and presents /proc/self/fd/0
as a symbolic link to /dev/pts/10
... precisely because that process currently has /dev/pts/10
associated with its file descriptor #0.
On Solaris 11, the /dev/std*
devices are symbolic links to /dev/fd/
directory, which is similarly "magical":
$ uname -sr
SunOS 5.11
$ ls -l /dev/std*
lrwxrwxrwx 1 root root 0 Jun 17 2019 /dev/stderr -> ./fd/2
lrwxrwxrwx 1 root root 0 Jun 17 2019 /dev/stdin -> ./fd/0
lrwxrwxrwx 1 root root 0 Jun 17 2019 /dev/stdout -> ./fd/1
Here, the Solaris /dev
filesystem driver implements the magic using device nodes in the /dev/fd
directory instead of redirecting to /proc
filesystem, as Linux does for historical reasons.
Somewhat similarly, /dev/tty designates the terminal to which the process is connected
. What were you hoping to communicate out with the link? – TheMeaningfulEngineer Jan 25 '20 at 11:51/dev/pts/10
is not "connected to" stdin,/dev/pts/10
IS stdin. This question looks purposely obtuse and a bit of text analysis "connects it" (LOL) to other similar questions on different matters. – Jan 29 '20 at 13:20IS
part. The/dev/std*
are indeed just links to/dev/pts/10
. Something must create those links and make it point to the actual terminal. When does that happen and what is responsible for it? – TheMeaningfulEngineer Jan 29 '20 at 13:36