What is `/dev/console` used for?

/dev/console exists primarily to expose the kernel’s console to userspace. The Linux kernel’s documentation on devices now says

The console device, /dev/console, is the device to which system messages should be sent, and on which logins should be permitted in single-user mode. Starting with Linux 2.1.71, /dev/console is managed by the kernel; for previous versions it should be a symbolic link to either /dev/tty0, a specific virtual console such as /dev/tty1, or to a serial port primary (tty*, not cu*) device, depending on the configuration of the system.

/dev/console, the device node with major 5 and minor 1, provides access to whatever the kernel considers to be its primary means of interacting with the system administrator; this can be a physical console connected to the system (with the virtual console abstraction on top, so it can use tty0 or any ttyN where N is between 1 and 63), or a serial console, or a hypervisor console, or even a Braille device. Note that the kernel itself doesn’t use /dev/console: devices nodes are for userspace, not for the kernel; it does, however, check that /dev/console exists and is usable, and sets init up with its standard input, output and error pointing to /dev/console.

As described here, /dev/console is a character device with a fixed major and minor because it’s a separate device (as in, a means of accessing the kernel; not a physical device), not equivalent to /dev/tty0 or any other device. This is somewhat similar to the situation with /dev/tty which is its own device (5:0) because it provides slightly different features than the other virtual console or terminal devices.

The “list of consoles” is indeed the list of consoles defined by the console= boot parameters (or the default console, if there are none). You can see the consoles defined in this way by looking at /proc/consoles. /dev/console does indeed provide access to the last of these:

You can specify multiple console= options on the kernel command line. Output will appear on all of them. The last device will be used when you open /dev/console.


"What is /dev/console?" is answered in the previous answer. Perhaps that answer is more clear when you know the answers to the other two questions.

Q1. "What is the device file representing the physical terminal itself?"

There is no such device file.

Q2. "What is /dev/console used for?"

On Linux, /dev/console is used to show messages during startup (and shutdown). It is also used for "single user mode", as pointed out in Stephen Kitt's answer. There is not much else it makes sense to use it for.

"In the good old days" of Unix, /dev/console was a dedicated physical device. But this is not the case in Linux.

Related evidence

1. "What is the device file representing the physical terminal itself?"

Let me try to understand this way. /dev/tty{1..63} and /dev/pts/n are device files representing devices themselves (although they are emulations), not in relation to process or kernel. /dev/tty0 reprsents the one in /dev/tty{1..63} which is currently used by something (maybe kernel or shell process?). /dev/tty represents the controlling terminal currently used by a process session. /dev/console represents the terminal currently used by the kernel?

What is the device file representing the physical terminal itself, not in relation to kernel or process?

The underlying device(s) for /dev/tty{1..63} are struct con_driver. To see all the possible drivers, check out https://elixir.bootlin.com/linux/v4.19/ident/do_take_over_console

There is no device file for these underlying device(s)!


There is only a minimal userspace interface to manage them.

$ head /sys/class/vtconsole/*/name
==> /sys/class/vtconsole/vtcon0/name <==
(S) dummy device

==> /sys/class/vtconsole/vtcon1/name <==
(M) frame buffer device

If you really want to know more, the (M) stands for module. I.e. the dummy console device is not provided by a loadable kernel module; it is part of the initial kernel image (aka "builtin").

Secondly, the bind file in each subdirectory of /sys/class/vtconsole appears to tell you which vtconsole device is active. If I write 0 to the active one, it appears to switch to the dummy one. (GUI VTs seem unaffected, but text VTs stop working). Writing 1 for the dummy one does not activate it. Either method works to switch back to the real one. If I read the code correctly, the trick is that echo 1 > bind is only supposed to work for console drivers which are built as a module (?!).

For framebuffer consoles specifically, there is some more information about binding different framebuffer devices (/dev/fb0...) to specific virtual consoles in https://kernel.org/doc/Documentation/fb/fbcon.txt . This involves a kernel option fbcon:map= or a command called con2fbmap.

Of course the details can vary with different kernel versions, architectures, firmwares, devices, drivers, etc. I've never really had to use any of the interfaces above. The kernel just lets i915 / inteldrmfb / whatever you want to call it take over when it loads, replacing e.g. vgacon.

It looks like my EFI machine never has vgacon. So firstly it uses a dummy console, and secondly after 1.2 seconds it switches to fbcon, running on top of efifb. But so far I haven't had to care what the details are; it just works.

$ dmesg | grep -C2 [Cc]onsole
[    0.230822] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=4
[    0.233164] NR_IRQS: 65792, nr_irqs: 728, preallocated irqs: 16
[    0.233346] Console: colour dummy device 80x25
[    0.233571] console [tty0] enabled
[    0.233585] ACPI: Core revision 20180810
[    0.233838] clocksource: hpet: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 133484882848 ns
--
[    1.228393] efifb: scrolling: redraw
[    1.228396] efifb: Truecolor: size=8:8:8:8, shift=24:16:8:0
[    1.230393] Console: switching to colour frame buffer device 170x48
[    1.232090] fb0: EFI VGA frame buffer device
[    1.232110] intel_idle: MWAIT substates: 0x11142120
--
[    3.595838] checking generic (e0000000 408000) vs hw (e0000000 10000000)
[    3.595839] fb: switching to inteldrmfb from EFI VGA
[    3.596577] Console: switching to colour dummy device 80x25
[    3.596681] [drm] Replacing VGA console driver
[    3.597159] [drm] ACPI BIOS requests an excessive sleep of 20000 ms, using 1500 ms instead
[    3.599830] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
--
[    3.657050] e1000e 0000:00:19.0 eth0: MAC: 11, PHY: 12, PBA No: FFFFFF-0FF
[    3.657869] e1000e 0000:00:19.0 eno1: renamed from eth0
[    4.711453] Console: switching to colour frame buffer device 170x48
[    4.734356] i915 0000:00:02.0: fb0: inteldrmfb frame buffer device
[    4.778813] Loading iSCSI transport class v2.0-870.

2. "What is /dev/console used for?"

You can use /dev/console as a TTY device. Writing to it, for example, will write to a specific underlying device, which will also have a character device number of its own.

Often /dev/console is tied to /dev/tty0, but sometimes it may be tied to a different device.

So in this case writing to /dev/console will write to /dev/tty0. And in turn, writing to /dev/tty0 is equivalent to writing to whichever /dev/ttyN device is currently active.

But this raises an interesting question. Accessing tty0 will access different virtual consoles, depending on which is currently active. What do people actually use tty0 for, and similarly what is console used for on Linux?

  1. Technically, you can read and write from console / tty0, for example running a getty to allow logging in on tty0. But this is only useful as a quick hack. Because it means you cannot take advantage of Linux's multiple virtual consoles.

  2. systemd looks in sysfs for an attribute associated with the /dev/console device, to detect the underlying TTY device. This allows systemd to automatically spawn a getty and allow logging in on e.g. a serial console, when the user set up a kernel console by booting with console=ttyS0. This is convenient; it avoids the need to configure this console in two different places. Again, see man systemd-getty-generator. However, systemd does not actually open /dev/console for this.

  3. During system bootstrap, you might not even have sysfs mounted yet. But you want to be able to show error and progress messages as soon as possible! So we circle around to point 1). The kernel starts PID 1 with stdin/stdout/stderr connected to /dev/console. It's very nice to have this simple mechanism set up right from the start.

  4. Inside a Linux container, the file at /dev/console may be created as something different - not the character device number 5:1. Instead, it may be created as a PTS device file. Then it would make sense to log in through this /dev/console file. systemd inside a container will allow logging in on such a device; see man systemd-getty-generator.

    This mechanism is used when you run a container with the systemd-nspawn command. (I think only when you run systemd-nspawn on a TTY, although I can't tell from searching the man page).

    systemd-nspawn creates the container's /dev/console as a bind mount of a PTS device from the host. This means that this PTS device is not visible inside /dev/pts/ inside the container.

    PTS devices are local to a specific devpts mount. PTS devices are an exception to the normal rule, that devices are identified by their device number. PTS devices are identified by the combination of their device number, and their devpts mount.

  5. You can write urgent messages to console / tty0, to write to the user's current virtual console. This could be useful for urgent userspace error messages, similar to urgent kernel messages which are printed to the console (see man dmesg). However it is not common to do this, at least once the system has finished booting.

    rsyslog has one example on this page, which prints kernel messages to /dev/console; this is pointless on Linux because the kernel will already do so by default. One example which I cannot find again says that it's not a good idea to use this for non-kernel messages because there are just too many syslog messages, you flood your console and it gets in the way too much.

    systemd-journald similarly has options to forward all logs to the console. In principle this might be useful for debugging in a virtual environment. Although, for debugging we usually forward to /dev/kmsg instead. This saves them in the kernel log buffer so you can read them with dmesg. Like messages generated by the kernel itself, these messages may be echo'ed to the console depending on the current kernel configuration.