Linux Kernel: prink(print kernel)によるメッセージ出力
printk()とは
printk(print kernel)は、ユーザ空間のprintf( print formatted )に相当します。注意すべき点として、printf()と以下の点が異なります。本記事では、この差異を説明します。
- フォーマット
- ログレベルの存在
- printk()のラッパーマクロの存在
- ログの出力先がメッセージ用リングバッファ
- メッセージ表示に使用するコマンド(dmesg等)の存在
printk()フォーマット
pritnk()フォーマットは、printfとほぼ同様です。差異は、ログレベル(0〜7, d, c)が指定できる点、実数型(double/float)をサポートしない点です。ログレベルは後ほど解説します。
1 |
printk( ログレベル 書式文字列, 可変個引数 ); |
文字列の書式は、「printf(3)を参考にする事(“$ man 3 printf”)」とソースコード中に記載されている。下表に、代表的な指定文字を示す。型に応じて、どの変換指定文字を使用すべきか迷った場合は、公式ドキュメントに逆引きが存在します。
変換指定文字 | 意味 |
---|---|
%c | 1文字として出力 |
%d | 10進数で出力 |
%x | 16進数で出力 |
%o | 8進数で出力 |
%f | 使用不可。浮動小数点(実数)をKernelはサポートしない |
%d | 使用不可。浮動小数点(実数)をKernelはサポートしない |
%s | 文字列として出力 |
%p | ポインタ情報を出力(ポインタ関連は変換指定文字が多数存在) |
なお、ログレベルと書式文字列の間には、”,”がありません。
1 |
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に関するラッパーマクロが存在します。マクロは、”<Linux>/include/linux/printk.h“に定義されています。
良く使用するマクロは、ログレベルを省略できるマクロです。マクロは、以下のように定義されています。全てのログレベルに対して、マクロが用意されています(下表)。
1 2 |
/include/linux/printk.h">#define pr_warning(fmt, ...) \ printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__) |
1 2 3 |
/* 以下の二つの書き方では、同じ結果が得られます。*/ 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を表示しないマクロや遅延表示するマクロが存在します。
printk()出力先のメッセージリングバッファ
printk()は、ログレベルに関わらず、Kernel内部ログバッファにメッセージを保存しています。このログバッファは循環式であり、バッファ上限値を超えた場合は古いメッセージをに上書きする形で新しいメッセージを保存します。なお、ログバッファにメッセージを書き込む前に、タイムスタンプが付与されます。
ログバッファのサイズ変更は、Linux Kernelビルド前設定(“make menuconfig”)で行います。変更対象は”CONFIG_LOG_BUF_SHIFT”で、Bitシフトでしか設定できません(下図)。
メッセージ表示に利用するdmesgコマンド
printk()で用いるログバッファは、dmesgコマンド で内容を表示できます。dmesgは、klogctl(システムコール)を使用して、ログバッファを読み取り、標準出力 に表示します(以下の例を参考)。出力行数が多いため、head/tail/less/grepなどのコマンドで、メッセージを取捨選択したほうが良いです。
1 2 3 4 5 6 7 8 9 10 11 |
$ 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ファイルに記載された設定値を示し、その設定値の意味を下表に示します。
1 2 3 4 5 |
$ 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”は書き込み可能なファイルであるため、以下のように設定値を変更できます。ただし、管理者権限が必要です。
1 2 3 4 5 |
# 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 に書き出しています。こちらに関しては、インフラ系の勉強時に記事を作成します。
参考
ロシア人と国際結婚した地方エンジニア。
小学〜大学院、就職の全てが新潟。
大学の専攻は福祉工学だったのに、エンジニアとして就職。新卒入社した会社ではOS開発や半導体露光装置ソフトを開発。現在はサーバーサイドエンジニアとして修行中。HR/HM(メタル)とロシア妻が好き。サイトに関するお問い合わせやTwitterフォローは、お気軽にどうぞ。