Notes on Linux's printk
Some of the content might be incorrect, since I am still trying to understand it thoroughly.
So I started studying the Linux Kernel more in depth, so I decided it would be nice for me to document my findings, so I can read it from time to time. I will be daily updating this post, as I am studying it everyday.
Keep in mind that I am using the x86
architecture.
printk(const char * fmt, ...)
#
The printk
is meant to print a kernel message, which you can later read using
the dmesg
command. It provides a printf
-like abstraction. It can be used as
a debugging tool for logging messages from the kernel.
In kernel mode, you cannot use printf
.
The * fmt
argument is a format string, whereas the +...+
are variable
arguments.
The include/linux/kern_levels.h
defines 8 different log levels which specifies
the severity of the error message. Those are:
#define KERN_EMERG KERN_SOH "0" /* system is unusable */
#define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */
#define KERN_CRIT KERN_SOH "2" /* critical conditions */
#define KERN_ERR KERN_SOH "3" /* error conditions */
#define KERN_WARNING KERN_SOH "4" /* warning conditions */
#define KERN_NOTICE KERN_SOH "5" /* normal but significant condition */
#define KERN_INFO KERN_SOH "6" /* informational */
#define KERN_DEBUG KERN_SOH "7" /* debug-level messages */
Comments are pretty clear what each one means. With all that, we can easily call
printk
like this: printk(KERN_ERR "Something happend
). By default,
KERN_WARNING
is used when nothing is specified, though this can be changed by
setting CONFIG_DEFAULT_MESSAGE_LOGLEVEL
kernel option (+make menuconfig -> Kernel Hacking -> Default message log level+
).
For convenience, Linux also provides shorthand definition to those calls:
/*
* These can be used to print at the various log levels.
* All of these will print unconditionally, although note that pr_debug()
* and other debug macros are compiled out unless either DEBUG is defined
* or CONFIG_DYNAMIC_DEBUG is set.
*/
#define pr_emerg(fmt, ...) \
printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
#define pr_alert(fmt, ...) \
printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_crit(fmt, ...) \
printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_err(fmt, ...) \
printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warning(fmt, ...) \
printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warn pr_warning
#define pr_notice(fmt, ...) \
printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info(fmt, ...) \
printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
Meaning instead of calling printk(KERN_EMERG "System is corrupted!")
we could
call pr_emerg("System is corrupted")
which is basically the same thing. Unless
you compile your kernel in DEBUG
mode, you can also use pr_debug
and
KERN_DEBUG
.
Log level allows the kernel to determine the importance of a message, with that, it can decide whether it should present the message immediately to the user (printing in the console, etc).
The #define console_loglevel (console_printk[0])
is used to compare the log
level of the message against this defined variable. If the priority is >
than
this value, the message will then be printed to the current console. Note that
console_loglevel
’s value comes from console_printk[0]
which is defined as
an array (extern int console_printk[]
). kernel/printk/printk.c
defines each
index value:
int console_printk[4] = {
CONSOLE_LOGLEVEL_DEFAULT, /* console_loglevel */
MESSAGE_LOGLEVEL_DEFAULT, /* default_message_loglevel */
CONSOLE_LOGLEVEL_MIN, /* minimum_console_loglevel */
CONSOLE_LOGLEVEL_DEFAULT, /* default_console_loglevel */
};
Your current console_loglevel
can be found by printing
/proc/sys/kernel/printk
:
$ cat /proc/sys/kernel/printk
4 4 1 4
From left to right, the meaning of those values are as follow: current
,
default
, minimum
, boot-time-default
. For example, if you would like to get
all messages printed to your current console, you can simply change these values
to 8
:
$ echo 8 > /proc/sys/kernel/printk
Or by setting the log level through dmesg
using the -n
argument:
$ dmesg -n 8