Mastering Linux printk: Levels, Macros, and Kernel Logging Mechanics
This article provides a comprehensive guide to Linux's printk system, covering its ring‑buffer design, log level definitions, early boot usage, helper macros, rate‑limiting techniques, kernel‑side implementation details, and ways to access logs from user space.
Overview
The Linux kernel uses printk as its primary logging mechanism, outputting messages through a simple three‑pointer ring buffer that works in interrupt, process, and SMP contexts without requiring locks.
Printk Basics
While printk is ubiquitous, it cannot display messages before the console is initialized; early messages are stored in the ring buffer until the console becomes available. For early‑boot output, the early_printk() variant works with its own small buffer (typically 512 B) and prints directly to hardware.
Log Levels
Each printk call can specify a log level, defined by macros such as:
#define KERN_EMERG "<0>" /* system unusable */
#define KERN_ALERT "<1>" /* immediate action needed */
#define KERN_CRIT "<2>" /* critical condition */
#define KERN_ERR "<3>" /* error condition */
#define KERN_WARNING "<4>" /* warning */
#define KERN_NOTICE "<5>" /* normal but noteworthy */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug */
#define KERN_DEFAULT ""
#define KERN_CONT ""If no level is supplied, the kernel uses DEFAULT_MESSAGE_LOGLEVEL (configurable via CONFIG_DEFAULT_MESSAGE_LOGLEVEL and defaulting to <4>, i.e., KERN_WARNING).
Example:
printk(KERN_EMERG "log_level:%s
", KERN_EMERG);Helper Macros
To simplify usage, the kernel defines wrapper macros that embed the appropriate level and format handling:
#define pr_fmt(fmt) fmt
#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_notice(fmt, ...) printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info(fmt, ...) printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
#define pr_debug(fmt, ...) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) /* active only when DEBUG is defined */These macros allow developers to insert debug output that can be stripped simply by undefining DEBUG, avoiding manual code removal.
Rate Limiting
Excessive printk output can overflow the ring buffer or overwhelm slow consoles (e.g., serial ports). The kernel provides printk_ratelimit() to guard against repeated messages:
if (printk_ratelimit())
printk(KERN_NOTICE "The printer is still on fire
");The rate‑limit parameters are tunable via /proc/sys/kern/printk_ratelimit (time window) and /proc/sys/kernel/printk_ratelimit_burst (maximum messages per window).
Kernel Implementation Details
The underlying storage is a static character array:
static char __log_buf[__LOG_BUF_LEN];Three indices manage the buffer:
static unsigned log_start; /* next char read by syslog() */
static unsigned con_start; /* next char to send to console */
static unsigned log_end; /* one past the last written char */These pointers wrap around using a mask based on CONFIG_LOG_BUF_SHIFT, ensuring circular behavior without overrunning the buffer.
User‑Space Access
Two primary interfaces let user space read or modify kernel logs:
The klogctl wrapper around the syslog system call (exposed via glibc).
The /proc/kmsg procfs file, which ultimately invokes do_syslog() in kernel/printk.c.
Reading /proc/kmsg consumes the data (advances log_start), whereas syslog returns the data while preserving it for other readers. The dmesg command uses the latter to display the buffer without discarding entries.
Example program using klogctl to set the console log level:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(int argc, char **argv) {
int level;
if (argc == 2) {
level = atoi(argv[1]);
} else {
fprintf(stderr, "%s: need a single arg
", argv[0]);
exit(1);
}
if (klogctl(8, NULL, level) < 0) {
fprintf(stderr, "%s: syslog(setlevel): %s
", argv[0], strerror(errno));
exit(1);
}
exit(0);
}Practical Recommendations
Because printk copies characters one byte at a time and may trigger console interrupts, it can become a performance bottleneck. Developers should limit printk output to essential error messages in production code and use the rate‑limiting facilities for noisy debug paths.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
