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

カーネルからユーザー空間までのログの流れ

カーネルの開発では、私たちは深く考えずに printk を使用してロギングを行っていますが、カーネル・ロギングのプロセスと、その基礎となる実装について考えたことはありますか?この記事ではカーネル・ロギングについて、printk でログを作成してユーザー空間のログ・ファイルに挿入するまでのプロセス全体を探ります。

M. Tim Jones, Independent author

M. Tim JonesM. Tim Jones は組み込みソフトウェアのエンジニアであり、『Artificial Intelligence: A Systems Approach』、『GNU/Linux Application Programming』現在、第 2 版です) や『AI Application Programming』(こちらも現在、第 2 版です)、それに『BSD Sockets Programming from a Multilanguage Perspective』などの著者でもあります。技術的な経歴は静止軌道衛星用のカーネル開発から、組み込みシステム・アーキテクチャーやネットワーク・プロトコル開発まで、広範にわたっています。また、コロラド州ロングモン所在のEmulex Corp. の顧問エンジニアでもあります。



2010年 9月 30日

Tim とつながるには

Tim は developerWorks で人気の高いお馴染みの著者の 1 人です。Tim が書いたすべての developerWorks 記事を閲覧してみてください。また、My developerWorks では、Tim のプロフィールを調べることや、彼やその他の著者、そして他の読者とつながることができます。

ログは、コンピューターが誕生して以来、デバッグのために使用されてきました。ログはシステムの内部動作を理解する上で役立つだけでなく、タイムスタンプ付きログに含まれる時間順のメッセージからは、システム内部で行われているアクティビティーのタイミングと相互関係を知ることもできます。

この記事ではカーネル内でのロギングについて調べるために、まず始めにログ情報の構成および収集に使用されるアプリケーション・プログラミング・インターフェース (API) について見ていきます (フレームワークおよびコンポーネントの全体像については、図 1 を参照してください)。次に、カーネルからユーザー空間へのログ・データの流れを追った後、カーネル・ベースのログ・データの宛先となる、ユーザー空間での (rsyslog を使用した) ログ管理フレームワークについて探ります。

図 1. カーネル・ロギング・エコシステムおよび主要なコンポーネント
カーネル・ロギング・エコシステムおよび主要なコンポーネント

カーネル API

カーネル内でのロギングは、printk 関数を使用して行います。この関数は、ユーザー空間での printf 関数 (print formatted) と似ています。printf コマンドは数々の言語で長い間使用されてきました。最近では C 言語で使われていますが、その歴史は 1950年代および 1960年代の Fortran (PRINT および FORMAT 文)、BCPL (writf 関数。BCPL は C の前身となった言語です)、ALGOL 68 (printfputf) にまで遡ります。

カーネル内では printk 関数 (print kernel) を使用して、printf 関数とほぼ同一のフォーマットでメッセージをバッファーに書き込みます。printk のフォーマットは ./linux/include/linux/kernel.h に定義されています (実装は ./linux/kernel/printk.c に含まれています)。

int printk( const char * fmt, ... );

上記のフォーマットは、(printf と同じく) 文字列によってテキストとフォーマットを定義し、文字列には可変の引数一式 (省略記号 [...] で示されています) が伴うことを意味します。

カーネルの構成とエラー

printk によるロギングを有効にするには、カーネル構成で CONFIG_PRINTK カーネル構成オプションを使用します。通常、CONFIG_PRINTK は有効に設定されていますが、このオプションが有効にされていないカーネルに対してシステム・コールを行うと、ENOSYS エラーが返されます。

printk が printf と異なる点としてまず始めに気付くことは、printk は関数に関するものというよりは、プロトコルに関するものであるということです。この特徴は C 言語が持つ曖昧な側面を利用して、メッセージ・レベルまたは優先度の仕様を単純化します。カーネルではメッセージをログ・レベル (特定のメッセージの重大度を定義する 8 つのレベルのうちの 1 つ) で分類し、これらのレベルによって、システムが使用不可になったか (緊急メッセージ)、危険な状態が発生したか (クリティカル・メッセージ)、あるいは単なる情報通知のメッセージであるかを示すことができます。以下のクリティカル・メッセージの例を見るとわかるように、カーネル・コードでは、このログ・レベルを単にメッセージの最初の引数として定義します。

printk( KERN_CRIT "Error code %08x.\n", val );

上記の最初の引数は、カンマ (,) でレベル (KERN_CRIT) と体裁を指定した文字列とを区切っていないので、実際の引数ではないこと注意してください。KERN_CRIT は文字列そのものでしかありません (実際にこの文字列が表すのは文字列 "<2>" です。すべてのログ・レベルを記載した表 1 を参照してください)。プリプロセッサーの一部として、C 言語では「文字列リテラルの連結」と呼ばれる機能で自動的にこの 2 つの文字列を結合します。その結果、ログ・レベルとユーザーが指定したフォーマットの文字列は 1 つの文字列として統合されます。注意する点として、呼び出し側が printk にログ・レベルを指定しなければ、自動的にデフォルトである KERN_WARNING が使用されます (このデフォルトは、KERN_WARNING 以上の優先度を持つログ・メッセージだけがログに記録されることを意味します)。

表 1. ログ・レベル、シンボル、および用途
シンボル文字列用途
KERN_EMERG<0>緊急メッセージ (システムが停止する前に出力)
KERN_ALERT<1>早急な対応が必要なエラー
KERN_CRIT<2>クリティカル・エラー (ハードウェアまたはソフトウェア)
KERN_ERR<3>エラー状態 (ドライバー共通)
KERN_WARNING<4>警告状態 (エラーに至る可能性有り)
KERN_NOTICE<5>エラーではないが、十分な注意が必要な状態
KERN_INFO<6>情報通知メッセージ
KERN_DEBUG<7>デバッグ・メッセージ専用
KERN_DEFAULT<d>デフォルトのカーネル・ロギング・レベル
KERN_CONT<c>ログの行の続き (新しいタイムスタンプが付与されることを回避)

printk は、カーネル内のあらゆるコンテキストから呼び出すことができます。printk を呼び出すと、./linux/kernel/printk.c の printk 関数が実行され、va_start によって可変長引数が解決された後、(同じソース・ファイルにある) vprintk が呼び出されます。

ロギングのヘルパー関数

ロギングを簡単に使用できるように、カーネルはヘルパー関数も提供しています。各ログ・レベルには、printk 関数のマクロとして展開する独自の関数があり、例えば KERN_EMERG ログ・レベルで printk を使用するときには、代わりに pr_emerg を使用することができます。ログ・レベルそれぞれのマクロは、./linux/include/linux/kernel.h に記載されています。

vprintk 関数は管理レベルのいくつかのチェック (反復の検索) を行ってから、ログ・バッファー (__log_buf) のロックを取得します。次に、受け取った文字列でログ・レベルを表す文字列の有無がチェックされ、見つかった場合には、その文字列に応じたログ・レベルが設定されます。最後に vprintk は現在時刻を取得し (cpu_clock 関数を使用)、sprintf (標準ライブラリーのバージョンではなく、./linux/lib/vsprintf.c に実装された内部カーネルのバージョン) を使って現在時刻を文字列に変換します。この文字列が printk に渡されてカーネル・ログ・バッファーにコピーされます。コピーの際に使用されるのは、リングの境界を管理する特殊な関数です (emit_log_char)。この関数の最後でコンソール・セマフォーの取得と解放が行われて、次のログ・メッセージがコンソールに出力されます (この処理は、release_console_sem 内で行われます)。カーネル・リング・バッファーのサイズは当初 4KB でしたが、最近のカーネルでは 16KB に設定されています (アーキテクチャーによっては最大 1MB に設定)。

ログ・メッセージをカーネル・リング・バッファーに挿入するために使用される API についての説明は以上です。次は、カーネルからホストにデータを移動させるためのメソッドに目を向けます。


カーネル・ロギングとインターフェース

ログ・バッファーには、多用途の syslog システム・コールによってコア・レベルでアクセスすることができます。このシステム・コールはさまざまなアクションを実行しますが、すべてユーザー空間から実行することができます。ただし、非 root ユーザーが実行できるアクションはそのうちの 1 つだけです。syslog システム・コールのプロトタイプは ./linux/include/linux/syslog.h に定義されており、実装は ./linux/kernel/printk.c に含まれています。

syslog(2) と syslog(3) の違い

ここに定義されている syslogsyslog(2) であり、メッセージをシステム・ロガーに送信するための API である syslog(3) とは異なることに注意してください。syslog(3) は、ログ・ファイルに対して openclosewrite を実行する関数を使用して (ある特定の優先度を指定し)、メッセージを syslog に送信することができます。

syslog システム・コールは、カーネルのログ・メッセージのリング・バッファーに対する入出力 (I/O) および制御インターフェースとして機能します。syslog システム・コールにより、アプリケーションはログ・メッセージ (メッセージの一部、全体、または新規メッセージのみ) を読み取ることができるだけでなく、リング・バッファーの振る舞いを制御することもできます (バッファーの中身のクリア、ログに記録するメッセージ・レベルの設定、コンソールの有効化/無効化など)。図 2 の、ロギング・スタックを示す図に、ここで説明する主要なコンポーネントの一部を示します。

図 2. 主要なコンポーネントを示すカーネル・ロギング・スタック
主要なコンポーネントを示すカーネル・ロギング・スタック

syslog システム・コール (./linux/kernel/printk.c のカーネル内では do_syslog と呼ばれます) は、カーネル・リング・バッファーの読み取りおよび制御機能を提供する比較的小さな関数です。glibc 2.0 では、この関数は klogctl と呼ばれていることに注意してください。このように呼ばれている理由は、syslog という用語は、あまりにも多様な呼び出しとアプリケーションを指すために使われているからです。syslog および klogctl の (ユーザー空間での) プロトタイプ関数は以下のように定義されています。

int syslog( int type, char *bufp, int len );
int klogctl( int type, char *bufp, int len );

type 引数は実行するコマンドを示しており、このコマンドにはオプション・バッファーとその長さが関連付けられます。bufp 引数と len 引数は、一部のコマンド (リング・バッファーのクリアなど) では無視されます。以下の表 2 に示すコマンドのうち、最初の 2 つのコマンドはカーネル内ではアクションを行いませんが、残りのコマンドはログ・メッセージの読み取りや、ロギングの側面を制御するために使用されます。ログ・メッセージの読み取りに使用するコマンドは、3 つあります。そのうち、SYSLOG_ACTION_READ コマンドは、ログ・メッセージが読み取り可能になるまでブロックし、読み取り可能になった時点でログ・メッセージを指定のバッファーに返します。このコマンドは読み取ったメッセージを消去するので、このコマンドに対する以降の呼び出しでは、古いメッセージが読み取られることはありません。SYSLOG_ACTION_READ_ALL コマンドはログから最後の n 文字を読み取ります (n は、klogct に渡される len パラメーターとして定義されています)。SYSLOG_ACTION_READ_CLEAR コマンドは、SYSLOG_ACTION_READ_ALL アクションに続いて SYSLOG_ACTION_CLEAR コマンド (リング・バッファーのクリア) を実行します。SYSLOG_ACTION_CONSOLE_ONSYSLOG_ACTION_CONSOLE_OFF はログ・レベルを操作し、コンソールに対してログ・メッセージをそれぞれ表示させたり、非表示にしたりします。一方、SYSLOG_CONSOLE_LEVEL を使用すると、呼び出し側がコンソールに受け入れさせるログ・メッセージのレベルを定義することができます。さらに、カーネル・リング・バッファーのサイズを返す SYSLOG_ACTION_SIZE_BUFFER コマンド、そしてカーネル・リング・バッファー内で現在読み取り可能な文字数を返すSYSLOG_ACTION_SIZE_UNREAD コマンドもあります。表 2 に、全 SYSLOG コマンドの一覧を記載します。

表 2. syslog/klogctl システム・コールに実装されているコマンド
コマンド/オペコード用途
SYSLOG_ACTION_CLOSE (0)ログを閉じる (カーネル内でのアクションは行われない)
SYSLOG_ACTION_OPEN (1)ログを開く (カーネル内でのアクションは行われない)
SYSLOG_ACTION_READ (2)ログから読み取る
SYSLOG_ACTION_READ_ALL (3)ログからすべてのメッセージを読み取る (消去はしない)
SYSLOG_ACTION_READ_CLEAR (4)ログからすべてのメッセージを読み取って消去する
SYSLOG_ACTION_CLEAR (5)リング・バッファーをクリアする
SYSLOG_ACTION_CONSOLE_OFF (6)コンソールに対して printk を無効にする
SYSLOG_ACTION_CONSOLE_ON (7)コンソールに対して printk を有効にする
SYSLOG_ACTION_CONSOLE_LEVEL (8)コンソールに受け入れさせるメッセージのレベルを設定する
SYSLOG_ACTION_SIZE_UNREAD (9)ログ内のまだ読み取られていない文字数を返す
SYSLOG_ACTION_SIZE_BUFFER (10)カーネル・リング・バッファーのサイズを返す

カーネル・バッファーからログ・メッセージを読み取るためのバイナリー・インターフェースを提供する I/O パスは、syslog/klogctl 層の上に実装される kmsg proc ファイルシステムです (./linux/fs/proc/kmsg.c に実装されています)。通常、この読み取りはデーモン (klogd または rsyslogd) によって行われます。このデーモンではメッセージを読み取って、rsyslog にメッセージを渡すことで、(ログ・ファイルの構成に応じて) 適切なファイルにメッセージがルーティングされるようにします。

/proc/kmsg ファイルは、内部 do_syslog 操作に相当するいくつかのファイル操作を実装しています。内部では、open 呼び出しが SYSLOG_ACTION_OPEN に関係し、release 呼び出しが SYSLOG_ACTION_CLOSE に関係します (この 2 つはそれぞれ、NOP (No Operation Performed) として実装されています)。poll 操作では、ファイルでのアクティビティーを待機してから、SYSLOG_ACTION_SIZE_UNREAD を呼び出すことで読み取り可能な文字数を明らかにすることができます。そして SYSLOG_ACTION_READ に対応する read 操作は、読み取り可能なログ・メッセージの読み取りを行います。/proc/kmsg ファイルはユーザーにとってはあまり有用でないことに注意してください。このファイルは、デーモンがログ・メッセージを取得して、/var 空間の適切なログ・ファイルにルーティングするために使用されます。


ユーザー空間でのアプリケーション

ユーザー空間には、カーネル・ロギングを読み取り、操作するための数々のアクセス・ポイントがあります。このセクションでは、まずは下位レベルのインターフェース (/proc ファイルシステムの構成要素など) を取り上げ、それから上位レベルのアプリケーションへと説明を進めます。

/proc ファイルシステムは、ログ・メッセージにアクセスするためのバイナリー・インターフェース (kmsg) をエクスポートするだけではありません。このファイルシステムは、syslog/klogctl で説明した要素に関連するもの、関連しないものも含め、多数の構成要素を提示します。リスト 1 に、これらのパラメーターの詳細を記載します。

リスト 1. /proc 内の printk 構成パラメーターの詳細
mtj@ubuntu:~$ cat /proc/sys/kernel/printk
4	4	1	7
mtj@ubuntu:~$ cat /proc/sys/kernel/printk_delay
0
mtj@ubuntu:~$ cat /proc/sys/kernel/printk_ratelimit
5
mtj@ubuntu:~$ cat /proc/sys/kernel/printk_ratelimit_burst
10

リスト 1 の最初のエントリーは、printk API で使用されている現在のログ・レベルを定義します。これらのログ・レベルが表しているのは、コンソール・ログ・レベル、デフォルト・メッセージ・ログ・レベル、最小コンソール・ログ・レベル、デフォルト・コンソール・ログ・レベルです。printk_delay の値は、(一部のシナリオでメッセージを読みやすくするために) printk メッセージ間に挿入する遅延時間 (ミリ秒数) を表します。上記ではゼロに設定されていますが、この値を /proc で設定することはできないことに注意してください。printk_ratelimit は、メッセージ間の最小許容時間を定義します (上記では、5 秒ごとにカーネル・メッセージが出力されるものとして定義されています)。1 回ごとに出力されるメッセージの最大数を定義するのは、printk_ratelimit_burst です (上記では 10 に定義)。このパラメーターは、カーネルから多数のメッセージが上がる一方、帯域幅が限られた (シリアル・ポート経由などによる) コンソール・デバイスを使用している場合には特に役立ちます。注意する点として、速度制限はカーネル内では呼び出し側が制御するため、printk 内には実装されません。printk ユーザーが速度を制限する場合には、printk_ratelimit 関数を呼び出します。

カーネル・リング・バッファーを出力および制御するには、dmesg コマンドも使用することができます。このコマンドは、klogctl システム・コールを使用してカーネル・リング・バッファーを読み取り、標準出力 (stdout) に送信します。さらに、カーネル・リング・バッファーのクリア (-c オプションを使用)、コンソールに出力するログ・レベルの設定 (-n オプションを使用)、そしてカーネルのログ・メッセージの読み取りに使用するバッファーのサイズの定義 (-s オプションを使用) にも対応することができます。バッファー・サイズが指定されない場合、dmesg は適切なバッファー・サイズを判別するために、klogctl に対して SYSLOG_ACTION_SIZE_BUFFER 操作を実行することに注意してください。

すべてのロギング・アプリケーションの母体となる syslog は、主要なオペレーティング・システム (Linux® および BSD (Berkeley Software Distribution) も含まれます) に実装された標準ロギング・フレームワークです。syslog はコンポーネントをオリジネーター、リレー、コレクターに分類し、独自のプロトコルを使用してイベント通知メッセージを多種多様なトランスポート・プロトコルに伝搬します。大抵の場合、オリジネーター、リレー、コレクターはすべて 1 つのホストに実装されます。多くの興味深い特徴がある syslog では、ロギング情報を保管する場所とともに、収集およびフィルタリングする方法を指定します。syslog はこれまで数々の変更を経て進化してきました。読者の皆さんも、syslogklog、あるいは sysklogd については聞いたことがあると思います。Ubuntu の最近のディストリビューションでは、rsyslog と呼ばれる新しいバージョンの syslog が (オリジナルの syslog をベースに) 使用されています。この rsyslog という名前は、高い信頼性 (reliable) を備えた拡張 syslogd を意味しています。

rsyslogd デーモンは、/etc/rsyslog.conf にあるその構成ファイルによって /proc ファイルシステムの kmsg インターフェースを理解し、そのインターフェースを使用してカーネルのロギング・メッセージを抽出します。内部では、すべてのログ・レベルが /proc/kmsg によって書き込まれるため、転送するログ・レベルを定義するのはカーネルの仕事ではなく、rsyslog 自体に任されます。カーネルのログ・メッセージは、(他にも構成済みのファイルがあるなかで) /var/log/kern.log に保管されます。/var/log にはこのファイルの他にも多数のログ・ファイルがあります。例えば、一般メッセージおよびシステム関連の呼び出しが含まれるファイル (/var/log/messages)、システム・ブート・ログが含まれるファイル (/var/log/boot.log)、認証ログが含まれるファイル (/var/log/auth.log) などです。

ログは自ら見直すために用意されるものの、自動的な監査および科学捜査のためにログを使用することもできます。パターン認識や相関分析などの手法を使用して (これらの手法はシステムに跨ることさえあります)、トラブルシューティングや、セキュリティー規制への準拠、そして問題の自動的検索を行う、さまざまなログ・ファイル・アナライザーが存在しています。


さらに詳しく調べてください

この記事では、カーネル・ロギングおよびカーネル・ロギング・アプリケーションに簡単に触れました。カーネル内でカーネル・ログ・メッセージが作成されて、カーネルのリング・バッファーに保管され、syslog/klogctl または /proc/kmsg でユーザー空間に転送された後、rsyslog ロギング・フレームワークによってメッセージが最終的に保管される /var/log サブツリーにルーティングされる流れを説明しました。このように、Linux は (カーネル内およびカーネル外部での) ロギングのためのリッチで柔軟なフレームワークを提供しています。

参考文献

学ぶために

  • rsyslogマニュアル・ページWiki サイトで、syslog および klog に代わるこの新しいシステム・ロギング・フレームワークについて読んでください。
  • Ubuntu で管理している、rsyslog を重点としたロギングに関するページは参考になります。このホワイト・ペーパーではロギングと rsyslog について、構成および複雑なマルチホスト・ロギング・ネットワークを含め、詳しく紹介しています。
  • syslog(2)man ページは、syslog(2) およびその各種オプションと構成について知るには絶好の入門書です。
  • printk 関数が依存しているのは、文字列リテラルの連結という C 言語の機能です。C 言語に関する Wikipedia のページに、この手法についての説明が載っています。
  • syslog プロトコルは、Internet Engineering Task Force の RFC (Request for Comments) プロセスで標準化されたプロトコルです。syslog RFC 5424 について読んでください。
  • ログ・ファイル分析は、マシン・ラーニング/モニタリング・ツールで話題を集めています。Wikipedia では一般的なログ分析について詳しく説明しています。アクティブにログ・ファイルをモニタリングするツール、Swatch については SourceForge で学んでください。
  • developerWorks Linux ゾーンで、Linux 開発者および管理者向けのハウツー記事とチュートリアル、そしてダウンロード、ディスカッション、フォーラムなど、豊富に揃った資料を探してください。
  • さまざまな IBM 製品および IT 業界についての話題に絞った developerWorks の Technical events and webcasts で時代の流れをキャッチしてください。
  • 無料の developerWorks Live! briefing に参加して、IBM 製品およびツール、そして IT 業界の傾向を素早く学んでください。
  • developerWorks の on-demand demos で、初心者向けの製品のインストールおよびセットアップから熟練開発者向けの高度な機能に至るまで、さまざまに揃ったデモを見てください。
  • Twitter で developerWorks をフォローするか、developerWorks で Linux に関するツイートのフィードに登録してください。

製品や技術を入手するために

  • ご自分に最適な方法で IBM 製品を評価してください。評価の方法としては、製品の試用版をダウンロードすることも、オンラインで製品を試してみることも、クラウド環境で製品を使用することもできます。また、SOA Sandbox では、数時間でサービス指向アーキテクチャーの実装方法を効率的に学ぶことができます。

議論するために

  • My developerWorks コミュニティーに加わってください。ここでは他の developerWorks ユーザーとのつながりを持てる他、開発者が主導するブログ、フォーラム、グループ、ウィキを調べることができます。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux
ArticleID=617665
ArticleTitle=カーネル・ロギング: API と実装
publish-date=09302010