- Mastering Embedded Linux Programming
- Chris Simmonds
- 1080字
- 2021-07-30 09:45:04
Booting your kernel
Booting is highly device-dependent, but here is an example using U-Boot on a BeagleBone Black and QEMU:.
BeagleBone Black
The following U-Boot commands show how to boot Linux on a BeagleBone Black:
U-Boot# fatload mmc 0:1 0x80200000 zImage reading zImage 4606360 bytes read in 254 ms (17.3 MiB/s) U-Boot# fatload mmc 0:1 0x80f00000 am335x-boneblack.dtb reading am335x-boneblack.dtb 29478 bytes read in 9 ms (3.1 MiB/s) U-Boot# setenv bootargs console=ttyO0,115200 U-Boot# bootz 0x80200000 - 0x80f00000 Kernel image @ 0x80200000 [ 0x000000 - 0x464998 ] ## Flattened Device Tree blob at 80f00000 Booting using the fdt blob at 0x80f00000 Loading Device Tree to 8fff5000, end 8ffff325 ... OK Starting kernel ... [ 0.000000] Booting Linux on physical CPU 0x0 ...
Note that we set the kernel command line to console=ttyO0,115200
. That tells Linux which device to use for console output which, in this case, is the first UART on the board, device ttyO0
, at a speed of 115,200 bits per second. Without this, we would not see any messages after Starting the kernel ...
and therefore would not know if it was working or not.
QEMU
Assuming that you have already installed qemu-system-arm
, you can launch it with the multi_v7 kernel and the .dtb
file for the ARM Versatile Express, as follows:
$ QEMU_AUDIO_DRV=none \ qemu-system-arm -m 256M -nographic -M vexpress-a9 -kernel zImage -dtb vexpress-v2p-ca9.dtb -append "console=ttyAMA0"
Note that setting QEMU_AUDIO_DRV
to none
is just to suppress error messages from QEMU about missing configurations for the audio drivers, which we do not use.
To exit from QEMU, type Ctrl-A
then x
(two separate keystrokes).
Kernel panic
While things started off well, they ended badly:
[ 1.886379] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) [ 1.895105] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0, 0)
This is a good example of a kernel panic. A panic occurs when the kernel encounters an unrecoverable error. By default, it will print out a message to the console and then halt. You can set the panic
command line parameter to allow a few seconds before it reboots following a panic.
In this case, the unrecoverable error is because there is no root filesystem, illustrating that a kernel is useless without a user space to control it. You can supply a user space by providing a root filesystem either as a ramdisk or on a mountable mass storage device. We will talk about how to create a root filesystem in the next chapter but, to get things up and running, assume that we have a ramdisk in the file uRamdisk
and you can then boot to a shell prompt by entering these commands into U-Boot:
fatload mmc 0:1 0x80200000 zImage fatload mmc 0:1 0x80f00000 am335x-boneblack.dtb fatload mmc 0:1 0x81000000 uRamdisk setenv bootargs console=ttyO0,115200 rdinit=/bin/sh bootz 0x80200000 0x81000000 0x80f00000
Here, I have added rdinit=/bin/sh
to the command line so that the kernel will run a shell and give us a shell prompt. Now, the output on the console looks like this:
... [ 1.930923] sr_init: No PMIC hook to init smartreflex [ 1.936424] sr_init: platform driver register failed for SR [ 1.964858] Freeing unused kernel memory: 408K (c0824000 - c088a000) / # uname -a Linux (none) 3.18.3 #1 SMP Wed Jan 21 08:34:58 GMT 2015 armv7l GNU/Linux / #
At last, we have a prompt and can interact with our device.
Early user space
In order to transition from kernel initialization to user space, the kernel has to mount a root filesystem and execute a program in that root filesystem. This can be via a ramdisk, as shown in the previous section, or by mounting a real filesystem on a block device. The code for all of this is in init/main.c
, starting with the function rest_init()
which creates the first thread with PID 1 and runs the code in kernel_init()
. If there is a ramdisk, it will try to execute the program /init
, which will take on the task of setting up the user space.
If it fails to find and run /init
, it tries to mount a filesystem by calling the function prepare_namespace()
in init/do_mounts.c
. This requires a root=
command line to give the name of the block device to use for mounting, usually in the form:
root=/dev/<disk name><partition number>
root=/dev/<disk name>p<partition number>
For example, for the first partition on an SD card, that would be root=/dev/mmcblk0p1
. If the mount succeeds, it will try to execute /sbin/init
, followed by /etc/init
, /bin/init
, and then /bin/sh
, stopping at the first one that works.
The init
program can be overridden on the command line. For a ramdisk, use rdinit=
, (I used rdinit=/bin/sh
earlier to execute a shell) and, for a filesystem, use init=
.
Kernel messages
Kernel developers are fond of printing out useful information through liberal use of printk()
and similar functions. The messages are categorized according to importance, 0 being the highest:

They are first written to a buffer, __log_buf
, the size of which is two to the power of CONFIG_LOG_BUF_SHIFT
. For example, if it is 16, then __log_buf
is 64 KiB. You can dump the entire buffer using the command dmesg
.
If the level of a message is less than the console log level, it is displayed on the console as well as being placed in __log_buf
. The default console log level is 7, meaning that messages of level 6 and lower are displayed, filtering out KERN_DEBUG
which is level 7. You can change the console log level in several ways, including by using the kernel parameter loglevel=<level>
or the command dmesg -n <level>
.
Kernel command line
The kernel command line is a string that is passed to the kernel by the bootloader, via the bootargs
variable in the case of U-Boot; it can also be defined in the device tree, or set as part of the kernel configuration in CONFIG_CMDLINE
.
We have seen some examples of the kernel command line already but there are many more. There is a complete list in Documentation/kernel-parameters.txt
. Here is a smaller list of the most useful ones:

The lpj
parameter is often mentioned in connection with reducing the kernel boot time. During initialization, the kernel loops for approximately 250 ms to calibrate a delay loop. The value is stored in the variable loops_per_jiffy
, and reported like this:
Calibrating delay loop... 996.14 BogoMIPS (lpj=4980736)
If the kernel always runs on the same hardware it will always calculate the same value. You can shave 250 ms off the boot time by adding lpj=4980736
to the command line.
- 大學計算機應用基礎實踐教程
- Reactive Programming with Swift
- Vue.js 3.0源碼解析(微課視頻版)
- Blender 3D Incredible Machines
- HTML5+CSS3+JavaScript Web開發案例教程(在線實訓版)
- Windows Server 2016 Automation with PowerShell Cookbook(Second Edition)
- R Data Analysis Cookbook(Second Edition)
- 精通Python自動化編程
- Orleans:構建高性能分布式Actor服務
- 30天學通C#項目案例開發
- Python預測之美:數據分析與算法實戰(雙色)
- LabVIEW數據采集
- Microsoft XNA 4.0 Game Development Cookbook
- Mastering Node.js
- Python編程基礎與數據分析