printk()とは

printk(print kernel)は、ユーザ空間のprintf( print formatted )に相当します。注意すべき点として、printf()と以下の点が異なります。本記事では、この差異を説明します。

printf()との差異

  • フォーマット
  • ログレベルの存在
  • printk()のラッパーマクロの存在
  • ログの出力先がメッセージ用リングバッファ
  • メッセージ表示に使用するコマンド(dmesg等)の存在

printk()フォーマット

pritnk()フォーマットは、printfとほぼ同様です。差異は、ログレベル(0〜7, d, c)が指定できる点、実数型(double/float)をサポートしない点です。ログレベルは後ほど解説します。

printk( ログレベル 書式文字列, 可変個引数 );

文字列の書式は、「printf(3)を参考にする事("$ man 3 printf")」とソースコード中に記載されている。下表に、代表的な指定文字を示す。型に応じて、どの変換指定文字を使用すべきか迷った場合は、公式ドキュメントに逆引きが存在します。

変換指定文字意味
%c1文字として出力
%d10進数で出力
%x16進数で出力
%o8進数で出力
%f使用不可。浮動小数点(実数)をKernelはサポートしない
%d使用不可。浮動小数点(実数)をKernelはサポートしない
%s文字列として出力
%pポインタ情報を出力(ポインタ関連は変換指定文字が多数存在

なお、ログレベルと書式文字列の間には、",“がありません。

printk(KERN_WARNING "Test message.\n");

KERN_WARNINGは、defineマクロで定義された"4"を意味します。C言語では自動的にログレベルと書式文字列(“Test〜“の部分)は、 文字列として結合されます。ログレベルを指定しなかった場合は、自動的にデフォルト値(KERN_WARNING)となります。

ログレベル一覧

文字列defineマクロ意味・用途
0KERN_EMERG システム停止前の緊急メッセージ
1KERN_ALERT 早急な対応が必要なエラー
2KERN_CRIT HWもしくはSWの致命的なエラー
3KERN_ERR ドライバ共通のエラー
4KERN_WARNING 警告(エラーとなる可能性がある)
5KERN_NOTICE 注意(エラーではない)
6KERN_INFO 情報通知
7KERN_DEBUG デバッグメッセージ専用
dKERN_DEFAULTデフォルトのログレベル
cKERN_CONTログの継続(タイムスタンプの更新を回避。ブート中専用)

printk()のラッパーマクロ

Kernel開発者は、printk()を高頻度で使用します。利便性を高めるために、printkに関するラッパーマクロが存在します。マクロは、”/include/linux/printk.h“に定義されています。

良く使用するマクロは、ログレベルを省略できるマクロです。マクロは、以下のように定義されています。全てのログレベルに対して、マクロが用意されています(下表)。

#define pr_warning(fmt, ...) \
	printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
/* 以下の二つの書き方では、同じ結果が得られます。*/
printk(KERN_WARNING "Test message.\n");
pr_warn("Test message.\n");
文字列ログレベルラッパーマクロ
0KERN_EMERGpr_emerg
1KERN_ALERTpr_alert
2KERN_CRITpr_crit
3KERN_ERRpr_err
4KERN_WARNINGpr_warningもしくはpr_warn
5KERN_NOTICEpr_notice
6KERN_INFOpr_info
7KERN_DEBUGpr_devel(#ifdefによって、“DEBUG"が0の場合はprintk()しない仕様)
cKERN_CONTpr_cont

上記の他にも、一度しかprintkを表示しないマクロや遅延表示するマクロが存在します。

[the_ad id=“598”]

printk()出力先のメッセージリングバッファ

printk()は、ログレベルに関わらず、Kernel内部ログバッファにメッセージを保存しています。このログバッファは循環式であり、バッファ上限値を超えた場合は古いメッセージをに上書きする形で新しいメッセージを保存します。なお、ログバッファにメッセージを書き込む前に、タイムスタンプが付与されます。

ログバッファのサイズ変更は、Linux Kernelビルド前設定(“make menuconfig”)で行います。変更対象は”**CONFIG_LOG_BUF_SHIFT”**で、Bitシフトでしか設定できません(下図)。

メッセージ表示に利用するdmesgコマンド

printk()で用いるログバッファは、dmesgコマンド で内容を表示できます。dmesgは、klogctl(システムコール)を使用して、ログバッファを読み取り、標準出力 に表示します(以下の例を参考)。出力行数が多いため、head/tail/less/grepなどのコマンドで、メッセージを取捨選択したほうが良いです。

$ sudo dmesg | head -n 10
[    0.000000] Linux version 4.9.0-8-amd64 (debian-kernel@lists.debian.org) (gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1) ) #1 SMP Debian 4.9.130-2 (2018-10-27)
[    0.000000] Command line: BOOT_IMAGE=/boot/vmlinuz-4.9.0-8-amd64 root=UUID=0c52f0f1-f354-4cff-9ed7-e8d7a8bc9814 ro quiet
[    0.000000] x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x004: 'AVX registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x008: 'MPX bounds registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x010: 'MPX CSR'
[    0.000000] x86/fpu: xstate_offset[2]:  576, xstate_sizes[2]:  256
[    0.000000] x86/fpu: xstate_offset[3]:  832, xstate_sizes[3]:   64
[    0.000000] x86/fpu: xstate_offset[4]:  896, xstate_sizes[4]:   64

ログ出力の程度を抑えるために、ユーザ設定のログレベルのメッセージだけを表示する仕組みがあります。その設定値は、"/proc/sys/kernel/printk"に記載されています。以下にprintkファイルに記載された設定値を示し、その設定値の意味を下表に示します。

$ cat /proc/sys/kernel/printk
4	4	1	7

(注釈)数値は左から順に、
console_loglevel、default_message_level、minimum_console_loglevel、 default_console_loglevel
設定意味
console_loglevelログレベルが設定値より小さい場合のみ、コンソール出力可能。
default_message_level明示的にログレベルが設定されていないpritnk()メッセージのログレベル
minimum_console_loglevelconsole_loglevelに設定できる最小値
default_console_loglevelconsole_loglevelのデフォルト値

“/proc/sys/kernel/printk"は書き込み可能なファイルであるため、以下のように設定値を変更できます。ただし、管理者権限が必要です。

# cat /proc/sys/kernel/printk
4	4	1	7
# echo "7 2 1 7" > /proc/sys/kernel/printk
# cat /proc/sys/kernel/printk
7	2	1	7

最後に余談ですが、Linuxは、dmesgとは別の仕組みでログが管理されています。システム内部では、klogd が周期的にログ内容を収集し、 /var/log/syslog に書き出しています。こちらに関しては、インフラ系の勉強時に記事を作成します。

参考

Linux Kernel公式ドキュメント

カーネル・ロギング: API と実装(IBM)

kernel/printk/printk.c