📜 ⬆️ ⬇️

Accelerate the launch of BeagleBone or runit not for dummies



Devices that we develop and produce require a quick start after a cold start. For devices without a full-fledged operating system (in which we use NutOS, also known as EtherNut), there is no such problem - they are ready to work in a couple of seconds after switching on. But in more complex and advanced, with linux inside, and especially in portable measuring systems, the issue of acceleration of initialization algorithms is more than relevant.
In the pilot version of our 10G ethernet switch, we used the well-known Beaglebone board and the boot process, except for the qemu-emulator, was happy to debug it. By the way, this pilot version of a 10-gigabit switch with a control beaglebone board (in the photo for the article) has been in our server room and has been working successfully for a couple of years,
I’ll say right away that the switch to runit gave an acceleration of the launch of the system on a 500MHz arm processor from half a minute to six kopecks seconds .

Disclaimer: this note was written for our company's internal wiki, and since not all software developers are system administrators, I found it necessary to explain some points in the most simple and understandable language.

')

Introduction: how to eat and what can be changed


I will try to briefly describe the problem.
Normally the linux system is loaded as follows:
  1. bootloader (lilo, grub, u-boot, ...)
  2. core
  3. init program
  4. everything else (as described in / etc / inittab) is started by this same init program


I will not talk about runlevels. There is a man for this: man runlevel, man init, man inittab.

init is the main process of the system and it, in one way or another, manages all other programs, and even the process number is 1. If it’s completely simplified, then its task is to start and restart the programs listed in the / etc / inittab file.

But back to the boot process.

For some reason, the authors of the distributions sticking to the so-called System V (sysv) boot order decided that init should invoke the same rc script with a parameter defining the runlevel level. This can be interpreted in the same way as, for example, if all the children went to school with one textbook, only opened it on different pages, depending on what class they are in. One can imagine how thick such a textbook should have been!

With the startup sequence of the linux system is almost the same situation.

Here, for example, is a piece of / etc / inittab:

 id: 2: initdefault:

 # Boot-time system configuration / initialization script.
 # This is a run first except when booting in emergency (-b) mode.
 si :: sysinit: /etc/init.d/rcS

 # What to do in single-user mode.
 ~~: S: wait: / sbin / sulogin

 # Runlevel 0 is halt.
 # Runlevel 1 is single-user.
 # Runlevels 2-5 are multi-user.
 # Runlevel 6 is reboot.

 l0: 0: wait: /etc/init.d/rc 0
 l1: 1: wait: /etc/init.d/rc 1
 l2: 2: wait: /etc/init.d/rc 2
 l3: 3: wait: /etc/init.d/rc 3
 l4: 4: wait: /etc/init.d/rc 4
 l5: 5: wait: /etc/init.d/rc 5
 l6: 6: wait: /etc/init.d/rc 6


The /etc/init.d/rc script contains more than 300 (!) Lines of shell code. And he does, in general, nothing at all: sequentially runs programs from /etc/rc?.d, depending on the stage of execution (runlevel'a).

We go further. At the initial stage (runlevel S, see above or inside / etc / inittab) everything that is in the /etc/rcS.d/ directory is executed. these are 20 scripts from the hostname to mount nfs and initialize the random generator.

After this part of the boot process is completed, init goes into multi-user mode and executes scripts from /etc/rc2.d/. In the simplest case, there are about ten of them.

When adding new programs (services, daemons etc) installation packages usually add one or two more scripts to the boot process and the system startup time increases again.

If a desktop computer or laptop or even a server may not be in a hurry to boot, the devices we produce should be ready for operation after switching on as soon as possible. for devices such as Bercut-ET , there are no problems - there we completely control the process, and for Bercut-MMT the question of a quick start remained open until recently.

So, a set of programs runit allows you to most easily and fully manage the procedure for starting the system.

In fact, starting linux (not only linux, you can also download bsd-like, in general) when using runit comes down to starting the kernel with the program runit-init, which controls the operation of three shell scripts:
  1. / etc / runit / 1 - initial start
  2. / etc / runit / 2 - multi-user mode (similar to runlevel 2)
  3. / etc / runit / 3 - shutdown / reboot (poweroff, shutdown, halt)


Each script usually takes one or two command screens and does only what is needed. Due to simplicity, transparency and, most importantly, a small amount of scripts, the administrator or developer of the installation image of the system can easily and completely manage the process.

The rest of the programs and services usually start under the control of the runsvdir program, and dependencies, priorities, and the startup order are determined by the administrator. An example of working with runsvdir is already on Habré, so I will not describe it in detail here.

In the Bercut.BeagleBone download log, you can pay attention to the time it took the system to execute the initial script (/ etc / runit / 1) and the time until the “login:” prompt appears:

 20: 06: 22.33157 Uncompressing Linux ... done, booting the kernel.
 20: 06: 22.99351 - runit: $! Id: 25da3b86f7bed4038b8a039d2f8e8c9bbcf0822b $: booting.
 20: 06: 22.99351 - runit: enter stage: / etc / runit / 1
 20: 06: 24.07955 - runit: leave stage: / etc / runit / 1
 20: 06: 24.07955 - runit: enter stage: / etc / runit / 2
 20: 06: 25.41956
 20: 06: 25.41957 Debian GNU / Linux 7.0 x10-02 ttyO0
 20: 06: 25.41957
 20: 06: 25.41957 x10-02 login:


As you can see, the system boot process (processor arm 500 MHz) took about three seconds. And this is taking into account the start of the kernel!

The transition process: step by step


The process is specifically described in general terms to make you think and understand how it works, and not stupidly copylessly copy scripts into the system and wonder why it does not work.

Step 1


Install the system runit. Whatever, in the form of stuffed or carcass package or collect from source .

Step 2


Ensure that the services under the control of runsvdir start, work and log for them. If you have never used programs from the daemontools or runit series (without replacing init), then you can stop reading.

Step 3


Configure the service for the console login. If you have never used programs from the daemontools or runit series (without replacing init), then it will be hard for you and you can stop reading. Sorry.

The run script for the fifth console (Alt-F5) may, for example, look like this:
#!/bin/sh exec 2>&1 exec setsid /sbin/agetty --nohostname tty5 38400 linux 


Step 4


Install shell scripts with memorable names 1, 2, 3 (one, two, three) in the / etc / runit directory. In the examples, these are bash scripts, but, in principle, no one bothers them to be sh-scripts or even perl-programs. But why?

Script 1 is executed at the first stage of loading (similar to rcS, single-user mode), script 2 is the main mode of the system, 3 is shutdown, reboot and poweroff.

Step 5


Reboot the system by passing the init = / sbin / runit-init parameter to the kernel.

Step 6


Look at the result and try to understand what is wrong;)

Step 7


If you manage to figure out what's wrong and get an invitation to enter your login on the 5th console, then I congratulate you: Most of the work is done! Now you can tie the remaining services (udev etc.)

Scripts 1, 2, 3 - at the bottom of this page under the spoilers (see below). Pay attention to the amount of code. surprisingly, this is enough for a full load of the operating system!

Bercut.BeagleBone Boot Log


From the moment of the “cold” start until the login: prompt appears, it takes only 6.1019 seconds. In my opinion, very good.

 20: 06: 19.31769 U-Boot SPL 2011.09-00000-gf63b270-dirty (Apr 24 2012 - 09:51:01)
 20: 06: 19.52354 Texas Instruments Revision detection unimplemented
 20: 06: 19.94867 No AC power, disabling frequency switch
 20: 06: 19.94868 OMAP SD / MMC: 0
 20: 06: 19.94868 reading u-boot.img
 20: 06: 19.94868 reading u-boot.img
 20: 06: 19.94868
 20: 06: 19.94868
 20: 06: 19.94869 U-Boot 2011.09-00000-gf63b270-dirty (Apr 24 2012 - 09:51:01)
 20: 06: 20.20756
 20: 06: 20.20756 I2C: ready
 20: 06: 20.20756 DRAM: 256 MiB
 20: 06: 20.39067 No daughter card present
 20: 06: 20.39067 NAND: HW ECC Hamming Code selected
 20: 06: 20.39067 nand_get_flash_type: unknown NAND device: Manufacturer ID: 0x10, Chip ID: 0x10
 20: 06: 20.39970 No NAND device found !!!
 20: 06: 20.39970 0 MiB
 20: 06: 20.39970 MMC: OMAP SD / MMC: 0
 20: 06: 20.39970 *** Warning - readenv () failed, using default environment
 20: 06: 20.65954
 20: 06: 20.65954 Net: cpsw
 20: 06: 20.65955 Hit any key to stop autoboot: 0
 20: 06: 21.54266 SD / MMC found on device 0
 20: 06: 21.54266 reading uEnv.txt
 20: 06: 21.54266
 20: 06: 21.54266 202 bytes read
 20: 06: 21.54266 Loaded environment from uEnv.txt
 20: 06: 21.54266 Importing environment from mmc ...
 20: 06: 21.74756 reading uimage
 20: 06: 21.87867
 20: 06: 21.87868 3083432 bytes read
 20: 06: 21.87869 ## Booting kernel from Legacy Image at 80007fc0 ...
 20: 06: 21.87870 Image Name: Angstrom / 3.2.18 / beaglebone
 20: 06: 21.88761 Image Type: ARM Linux Kernel Image (uncompressed)
 20: 06: 21.88761 Data Size: 3083368 Bytes = 2.9 MiB
 20: 06: 21.94465 Load Address: 80008000
 20: 06: 21.94465 Entry Point: 80008000
 20: 06: 21.94465 Verifying Checksum ... OK
 20: 06: 21.94465 XIP Kernel Image ... OK
 20: 06: 22.33156 OK
 20: 06: 22.33156
 20: 06: 22.33156 Starting kernel ...
 20: 06: 22.33156
 20: 06: 22.33157 Uncompressing Linux ... done, booting the kernel.
 20: 06: 22.99351 - runit: $! Id: 25da3b86f7bed4038b8a039d2f8e8c9bbcf0822b $: booting.
 20: 06: 22.99351 - runit: enter stage: / etc / runit / 1
 20: 06: 24.07955 - runit: leave stage: / etc / runit / 1
 20: 06: 24.07955 - runit: enter stage: / etc / runit / 2
 20: 06: 25.41956
 20: 06: 25.41957 Debian GNU / Linux 7.0 x10-02 ttyO0
 20: 06: 25.41957
 20: 06: 25.41957 x10-02 login:


Runit script examples


/ etc / runit / 1
 #!/bin/bash # system one time tasks PATH=/sbin:/bin:/usr/sbin:/usr/bin # re-exec this script with a controlling tty if [ "$(tty)" = "/dev/console" ]; then mountpoint -q /sys || \ mount -t sysfs sys /sys -o nosuid,noexec,nodev mountpoint -q /proc || \ mount -t proc proc /proc -o nosuid,noexec,nodev tty=$(</sys/class/tty/console/active) tty=/dev/${tty##* } exec setsid sh -c "exec bash /etc/runit/1 <$tty >$tty 2>&1" fi trap : INT TSTP QUIT trap 'sulogin -p <>$(tty) 2>&1' ERR mountpoint -q /proc || \ mount -t proc proc /proc -o nosuid,noexec,nodev mountpoint -q /sys || \ mount -t sysfs sys /sys -o nosuid,noexec,nodev mountpoint -q /dev || \ mount -t devtmpfs dev /dev -o mode=0755,nosuid test -e /dev/fd || ln -s /proc/self/fd /dev/fd exec 2> >(sed 's/^/:: /') # set -v . /etc/rc.conf mountpoint -q /run || \ mount -t tmpfs run /run -o mode=0755,nosuid,nodev mountpoint -q /dev || \ mount -t devtmpfs dev /dev -o mode=0755,nosuid mkdir -p -m0755 /run/runit /run/lock /run/user /dev/pts /dev/shm \ /run/network /run/runit mountpoint -q /dev/pts || mount -n -t devpts devpts /dev/pts -o mode=0620,gid=5,nosuid,noexec mountpoint -q /dev/shm || \ mount -n -t tmpfs shm /dev/shm -o mode=1777,nosuid,nodev mkdir -p -m 0755 /var/lib/supervise mountpoint -q /var/lib/supervise || \ mount -n -t tmpfs tmpfs /var/lib/supervise -o mode=1777,nosuid,nodev #mkdir -p -m 0755 /var/log #mountpoint -q /var/log || \ # mount -n -t tmpfs tmpfs /var/log -o mode=1777,nosuid,nodev mount -o remount,ro / ip link set up dev lo echo $HOSTNAME > /proc/sys/kernel/hostname mount -o remount,rw / mount -a -t "nosysfs,nonfs,nonfs4,nosmbfs,nocifs" -O no_netdev #cp /var/lib/random-seed /dev/urandom &>/dev/null || true #( umask 077; dd if=/dev/urandom of=/var/lib/random-seed count=1 #bs=512 &>/dev/n ull ) install -m0664 -o root -g utmp /dev/null /var/run/utmp install -m0 /dev/null /run/runit/stopit install -m0 /dev/null /run/runit/reboot install -m0644 -o root -g utmp /dev/null /var/log/lastlog dmesg > /var/log/dmesg 



/ etc / runit / 2
 #!/bin/bash PATH=/bin:/sbin:/usr/bin:/usr/sbin exec 2> >(sed 's/^/:: /') # set -v . /etc/rc.conf # sysctl --system for i in ${DAEMONS[@]}; do d=/var/lib/supervise/${i} mkdir -p $d ${d}.log l=/var/log/${i} mkdir -p $l done exec env - PATH=$PATH \ runsvdir -P /service 'log: ...........................................................................................................................................................................................................................................................................................................................................................................................................' 



/ etc / runit / 3
 #!/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin exec 2> >(sed 's/^/:: /') # set -v LAST=0 test -x /etc/runit/reboot && LAST=6 echo 'Waiting for services to stop...' sv -w196 force-stop /etc/service/* sv exit /etc/service/* stty onlcr echo Shutdown... udevadm control --exit killall5 -15 i=10; while killall5 -18 && (( i-- )) ; do echo -n .; sleep 0.5; done; echo killall5 -9 umount /tmp mount -o remount,ro / sleep 1 sync 



Links



cr.yp.to/daemontools.html - ancestor runit (from DJ Bernstein)
smarden.org/runit - runit in person
smarden.org/socklog - alternative to syslog
github.com/chneukirchen/ignite - a set of init scripts for runit
lwn.net/Articles/331818 - about how you can live without udevd
metrotek.spb.ru/x10-24.html - 10G switch, which uses the described mechanism
habrahabr.ru/post/21205 - How Linux boots

Source: https://habr.com/ru/post/264263/


All Articles