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")」とソースコード中に記載されている。下表に、代表的な指定文字を示す。型に応じて、どの変換指定文字を使用すべきか迷った場合は、公式ドキュメントに逆引きが存在します。
| 変換指定文字 | 意味 |
|---|---|
| %c | 1文字として出力 |
| %d | 10進数で出力 |
| %x | 16進数で出力 |
| %o | 8進数で出力 |
| %f | 使用不可。浮動小数点(実数)をKernelはサポートしない |
| %d | 使用不可。浮動小数点(実数)をKernelはサポートしない |
| %s | 文字列として出力 |
| %p | ポインタ情報を出力(ポインタ関連は変換指定文字が多数存在) |
なお、ログレベルと書式文字列の間には、",“がありません。
printk(KERN_WARNING "Test message.\n");
KERN_WARNINGは、defineマクロで定義された"4"を意味します。C言語では自動的にログレベルと書式文字列(“Test〜“の部分)は、 文字列として結合されます。ログレベルを指定しなかった場合は、自動的にデフォルト値(KERN_WARNING)となります。
ログレベル一覧
| 文字列 | defineマクロ | 意味・用途 |
|---|---|---|
| 0 | KERN_EMERG | システム停止前の緊急メッセージ |
| 1 | KERN_ALERT | 早急な対応が必要なエラー |
| 2 | KERN_CRIT | HWもしくはSWの致命的なエラー |
| 3 | KERN_ERR | ドライバ共通のエラー |
| 4 | KERN_WARNING | 警告(エラーとなる可能性がある) |
| 5 | KERN_NOTICE | 注意(エラーではない) |
| 6 | KERN_INFO | 情報通知 |
| 7 | KERN_DEBUG | デバッグメッセージ専用 |
| d | KERN_DEFAULT | デフォルトのログレベル |
| c | KERN_CONT | ログの継続(タイムスタンプの更新を回避。ブート中専用) |
printk()のラッパーマクロ
Kernel開発者は、printk()を高頻度で使用します。利便性を高めるために、printkに関するラッパーマクロが存在します。マクロは、”
良く使用するマクロは、ログレベルを省略できるマクロです。マクロは、以下のように定義されています。全てのログレベルに対して、マクロが用意されています(下表)。
#define pr_warning(fmt, ...) \
printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
/* 以下の二つの書き方では、同じ結果が得られます。*/
printk(KERN_WARNING "Test message.\n");
pr_warn("Test message.\n");
| 文字列 | ログレベル | ラッパーマクロ |
|---|---|---|
| 0 | KERN_EMERG | pr_emerg |
| 1 | KERN_ALERT | pr_alert |
| 2 | KERN_CRIT | pr_crit |
| 3 | KERN_ERR | pr_err |
| 4 | KERN_WARNING | pr_warningもしくはpr_warn |
| 5 | KERN_NOTICE | pr_notice |
| 6 | KERN_INFO | pr_info |
| 7 | KERN_DEBUG | pr_devel(#ifdefによって、“DEBUG"が0の場合はprintk()しない仕様) |
| c | KERN_CONT | pr_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_loglevel | console_loglevelに設定できる最小値 |
| default_console_loglevel | console_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 に書き出しています。こちらに関しては、インフラ系の勉強時に記事を作成します。
