What exactly does init do?

System 5 init will tell you only a small part of the story.

There's a sort of myopia that affects the Linux world. People think that they use a thing called "System 5 init", and that is both what is traditional and the best place to start. Neither is in fact the case.

Tradition isn't in fact what such people say it to be, for starters. System 5 init and System 5 rc date to AT&T UNIX System 5, which was almost as far after the first UNIX as we are now (say) after the first version of Linux-Mandrake.

1st Edition UNIX only had init. It did not have rc. The 1st Edition assembly language init (whose code has been restored and made available by Warren Toomey et al.) directly spawned and respawned 12 getty processes, mounted 3 hardwired filesystems from a built-in table, and directly ran a program from the home directory of a user named mel. The getty table was also directly in the program image.

It was another decade after UNIX System 5 that the so-called "traditional" Linux init system came along. In 1992, Miquel van Smoorenburg (re-)wrote a Linux init+rc, and their associated tools, which people now refer to as "System 5 init", even though it isn't actually the software from UNIX System 5 (and isn't just init).

System 5 init/rc isn't the best place to start, and even if one adds on knowledge of systemd that doesn't cover half of what there is to know. There's been a lot of work in the area of init system design (for Linux and the BSDs) that has happened in the past two decades alone. All sorts of engineering decisions have been discussed, made, designed, implemented, and practised. The commercial Unices did a lot, too.

Existing systems to study and and learn from

Here is an incomplete list of some of the major init systems other than those two, and one or two of their (several) salient points:

  • Joachim Nilsson's finit went the route of using a more human-readable configuration file.
  • Felix von Leitner's minit went for a filesystem-is-the-database configuration system, small memory footprints, and start/stop dependencies amongst things that init starts.
  • Gerrit Pape's runit went for what I have previously described as the just spawn four shell scripts approach.
  • InitNG aimed to have dependencies, named targets, multiple configuration files, and a more flexible configuration syntax with a whole load more settings for child processes.
  • upstart went for a complete redesign, modelling the system not as services and interdependencies at all, but as events and jobs triggered by them.
  • The design of nosh includes pushing all of the service management out (including even the getty spawning and zombie reaping) into a separate service manager, and just handling operating-system-specific "API" devices/symlinks/directories and system events.
  • sinit is a very simple init. It executes /bin/rc.init whose job it is to start programs, mount filesystem, etc. For this you can use something like minirc.

Moreover, about 10 years ago, there was discussion amongst daemontools users and others of using svscan as process #1, which led to projects like Paul Jarc's svscan as process 1 study, Gerrit Pape's ideas, and Laurent Bercot's svscan as process 1.

Which brings us to what process #1 programs do.

What process #1 programs do

Notions of what process #1 is "supposed" to do are by their natures subjective. A meaningful objective design criterion is what process #1 at minimum must do. The kernel imposes several requirements on it. And there are always some operating-system-specific things of various kinds that it has to do. When it comes to what process #1 has traditionally done, then we are not at that minimum and never really have been.

There are several things that various operating system kernels and other programs demand of process #1 that one simply cannot escape.

People will tell you that fork()ing things and acting as the parent of orphaned processes is the prime function of process #1. Ironically, this is untrue. Dealing with orphaned processes is (with recent Linux kernels, as explained at https://unix.stackexchange.com/a/177361/5132) a part the system that one can largely factor out of process #1 into other processes, such as a dedicated service manager. All of these are service managers, that run outwith process #1:

  • the IBM AIX srcmstr program, the System Resource Controller
  • Gerrit Pape's runsvdir from runit
  • Daniel J. Bernstein's svscan from daemontools, Adam Sampson's svscan from freedt, Bruce Guenter's svscan from daemontools-encore, and Laurent Bercot's s6-svscan from s6
  • Wayne Marshall's perpd from perp
  • the Service Management Facility in Solaris 10
  • the service-manager from nosh

Similarly, as explained at https://superuser.com/a/888936/38062, the whole /dev/initctl idea doesn't need to be anywhere near process #1. Ironically, it is the highly centralized systemd that demonstrates that it can be moved out of process #1.

Conversely, the mandatory things for init, that people usually forget in their off-the-top-of-the-head designs, are things such as handling SIGINT, SIGPWR, SIGWINCH, and so forth sent from the kernel and enacting the various system state change requests sent from programs that "know" that certain signals to process #1 mean certain things. (For example: As explained at https://unix.stackexchange.com/a/196471/5132, BSD toolsets "know" that SIGUSR1 has a specific meaning.)

There are also once-off initialization and finalization tasks that one cannot escape, or will suffer greatly from not doing, such as mounting "API" filesystems or flushing the filesystem cache.

The basics of dealing with "API" filesystems are little different to the operation of init rom 1st Edition UNIX: One has a list of information hardwired into the program, and one simply mount()s all of the entries in the list. You'll find this mechanism in systems as diverse as BSD (sic!) init, through the nosh system-manager, to systemd.

"set the system up for a simple shell"

As you have observed, init=/bin/sh doesn't get "API" fileystems mounted, crashes in an ungainly fashion with no cache flush when one types exit (https://unix.stackexchange.com/a/195978/5132), and in general leaves it to the (super)user to manually do the actions that make the system minimally usable.

To see what one actually has no choice but to do in process #1 programs, and thus set you on a good course for your stated design goal, your best option is to look at the overlaps in the operation of Gerrit Pape's runit, Felix von Leitner's minit, and the system-manager program from the nosh package. The former two show two attempts to be minimalist, yet still handle the stuff that it is impossible to avoid.

The latter is useful, I suggest, for its extensive manual entry for the system-manager program, which details exactly what "API" filesystems are mounted, what initialization tasks are run, and what signals are handled; in a system that by design has the system manager just spawn three other things (the service manager, an accompanying logger, and the program to run the state changes) and only do the unavoidable in process #1.


System V init on Debian (there are other variants and variations) does the following:

  • When entering a runlevel, it calls scripts in /etc/rcX.d/S* in alphanumeric order, where X is the runlevel. These scripts should setup the runlevel. Typical setup is starting daemons and performs setup tasks for that run level. This is a one-time thing done when entering the runlevel.
  • While in a run level, it starts daemons that are listed in the /etc/inittab as needing to be active during that run level. If those daemons stop running, it restarts them. While you can have any daemon you want managed by init, at a minimum you want a few getty's so you can log in. getty exits once a log in is completed, then init restarts it, providing a fresh login prompt.
    • If the daemon restarts too many times in too short of a time, it stops trying to restart it for a while.
    • Just because something was started by the kickoff scripts when entering the run level does not make init automatically try to keep it running. You need to specify that separately in the /etc/inittab.
  • When exiting a runlevel, it calls scripts in /etc/rcX.d/K* in alphanumeric order, where X is the runlevel. A way to implement shutdown or reboot is to define a runlevel for those events and make the last task executed the halt or reboot command.
  • It will call executables in response to certain events, such as power events or Ctrl-Alt-Del.
  • It listens on a socket, if it receives certain messages it will change runlevel.

So you can use init as rudimentary service manager if you want, but it's main task these days is to keep getty's available so a user can login, and kick off runlevel transitions.

I was just wondering, what tasks does init do to set the system up for a simple shell?

Whatever you want. On Debian, in each /etc/rcX.d directory is a symlink to a script in /etc/init.d and you can fully customize or remove those scripts. The order is established by preceding each script with a 00, 01, etc.

You can also specify a -b option to init (i.e. via kernel command line) if you just want init to spawn a shell. When you exit the shell, init dies and when init dies, the kernel will panic.


The absolute bare minimum that init must do is run at least one other program and not ever exit. If init exits the system crashes. I suppose that even running the one other program is not strictly necessary, but If you don't do that init would have to be responsible for doing every thing that the system is expected to do, or it would not be very useful.