8月版 ブート時間の短縮にかけるカーネルアスリートたち


小崎資広
2008/9/1

 OLS(Ottawa Linux Symposium)が終わって気分も一新、Linuxカーネルメーリングリスト(LKML)でのパッチの投稿ペースがまた上がってきています。

 先日、某所で議論したところ、2008年のOLSのハイライトは

  • 基調講演でTOMOYO LinuxがTOMOYA Linuxと呼ばれていた
  • Tシャツの平均サイズが10年間でMからXLに成長。みんな大きくなったね

だとか。

関連記事:
リンク オタワで聞いた“Thanks to Japanese Community”の声(@IT Security&Trust)
http://www.atmarkit.co.jp/fsecurity/special/127ottawa/ottawa01.html

 それでは、2008年8月のLKMLでどんなことが起きたのか見てみましょう。

ブート高速化フレームワークの開発始まる

 OSのブート処理の1つに、デバイスを初期化する処理があります。これによりさまざまなデバイスが使用可能になり、ディスクやネットワークといったリソースが使えるようになるわけです。

 従来、Linuxの起動処理において、デバイスドライバの初期化は1つ1つシーケンシャルに処理されていました。このため複数のCPUを活用することができず、また初期化に時間がかかるドライバがあると全体の起動時間までが延びてしまうという問題がありました。また、例えばUSBコントローラはハードウェア的な制限のため、ポート数に比例して所要時間が延びてしまうという問題があり、そのようなデバイスの場合、ドライバを個別に最適化させる努力だけでは初期化時間の短縮は難しいという状況でした。

 これに対し、Irqbalance、PowerTOPなどの開発で有名なArjan van de Venが、ブート時間を高速化させるパッチシリーズの開発に着手し、「fastbootパッチ」シリーズとして、精力的にパッチを投稿しています。

 基本的なアイデアはシンプルです。Linuxのデバイスドライバはそれぞれ、モジュール初期化関数と呼ばれる関数を持っています。ドライバが定められた作法に従って記述するだけで、その初期化関数が、カーネルが持つ初期化関数テーブルに登録されます。そしてカーネルは、起動時にその初期化関数を順次呼び出すという処理を行っています。

 そこでこの部分に「非同期初期化関数用」のテーブルを新設し、ドライバを1行ほど変更し、そちらのテーブルにもモジュール初期化関数を登録できるようにします。そしてカーネルは、ブート時にこちらの非同期初期化関数を呼び出します。Work Queue(注1)を使って呼び出した処理の完了を待たずに以降の処理を次々と呼び出し、それらは非同期的に実行されます。

 このような仕組みを取ることにより、もし初期化に時間がかかるデバイスがあったとしても、ほかのデバイスの処理には影響が及びません。結果として、ブート時間が大幅に短縮します。Arjanによれば、USBコントローラとACPIのいくつかの初期化を非同期動作させるだけで、カーネルの初期化時間は1.9秒から1.5秒へと、0.4秒短縮できたそうです。

 もちろん、中にはRAIDドライバのように、別のドライバに依存しているドライバもあります。このように、従来の処理順序に暗黙のうちに依存してきたドライバには修正が必要になるため、すぐさま全ドライバを非同期処理に変更することはできません。

 しかしながら、そのような暗黙の依存関係さえなければ、ドライバ自体の変更内容は、module_init()という行をmodule_init_async()に変更するだけという、極めてシンプルなものです。今後、それほど時間がかからずにドライバ側の対応が進むのではないかと期待されています。

注1:Linuxが持っている汎用的な非同期処理の仕組み

ファイルディスクリプタリークを防げ!

 glibcのメンテナであるUlrich Drepperによる「close-exec拡張パッチ」が、2.6.7-rc1においてマージされました。

 Ulrichはこのパッチは、「従来の手法が持っていたセキュリティリスクを解決するパッチだ」と主張しています。それでは、従来の方法の欠点とは何だったのでしょうか。

 Ulrichによると、そもそも、open()システムコールとfcntl(FD_CLOEXEC)とが、2つの手順に分離されていることが問題だといいます。もし「open」と「fcntl」という2つの処理のすき間で、別スレッド、またはシグナルハンドラからforkが呼ばれると、ファイルディスクリプタが閉じられずにリークしてしまいます。もし、execしたプログラムに脆弱(ぜいじゃく)性が含まれていた場合、親プロセスがオープンしていたファイルまでもが危険にさらされることになります。

 ここで、fork()が「signal-safe」なシステムコール(シグナルハンドラから呼ばれ得るという意味です)であることが、話をさらにややこしくしています。

 シグナルハンドラでは、pthred_mutex_lock()などのロック関数が使えないので、セキュアなコードを書くのは一般的に非常に困難です。仕方がないので、open-fcntl間にシグナルが割り込まないよう、openのたびにシグナルマスクを操作するという手法が取られてきましたが、これは、サーバ分野において受け入れ難い性能低下を引き起こしていました。

 では、forkした側がexecする前に全ファイルディスクリプタをcloseすればいいかというと、そうもいきません(注2)。ブラウザやWebサーバのように、アプリケーションがプラグイン型のアーキテクチャになっている場合、forkを呼び出すのはプラグインかもしれません。そのような場合、全プラグインに対して統制を取るのはなかなか難しいという問題がありました。

 そのため、openシステムコールに「O_CLOEXECフラグ」を追加し、このフラグを指定すると、オープンと同時にclose-execをONにできるようにするという解決方法が選択されました。

 またLinuxにおいては、open以外にも新規ファイルディスクリプタを作成して返却するシステムコールは大量にあります。そのすべてにおいて、フラグ引数に「XX_CLOEXECフラグ」を追加する、またはフラグ引数を追加した新システムコールを新設するという修正が行われました。

 ついでにというわけでもないのでしょうが、今回追加されたフラグ引数はすべて、XX_CLOEXECだけではなくXX_NONBLOCKフラグも取れるようになっており、利便性が少しだけ向上しています(close-execと違って、2手順に分かれていてもそんなに問題のない機能ですから、ポータビリティが落ちるのを覚悟で使う価値があるかはちょっと疑問ですが……)。

 この機能、どれだけのアプリケーションが使うのか若干疑問な部分もありますが、glibcが対応を表明しているため、意外とすぐに普及するかもしれません。

内容
open O_CLOEXECを追加
fcntl ディスクリプタの複製を行うF_DUPFDコマンドの変種としてF_DUPFD_CLOEXECコマンドを新設
recvmsg Unix Domain Socketでの暗黙のディスクリプタオープンに対するclose-execを行うMSG_CMSG_CLOEXECフラグを追加
dup3 dup2にフラグ引数を追加した新システムコール
pipe2 pipeにフラグ引数を追加した新システムコール
socket typeパラメータにフラグ引数の意味をオーバーロードさせてSOCK_CLOEXECフラグを追加(注3
socketpair typeパラメータにフラグ引数の意味をオーバーロードさせてSOCK_CLOEXECフラグを追加(注3
paccept selectに対するpselectのように、シグナルマスク引数を取ることが可能になり、かつ、フラグパラメータも追加された新システムコール
eventfd 新フラグEFD_CLOEXECの追加
signalfd 新フラグSFD_CLOEXECの追加
timerfd 新フラグTFD_CLOEXECの追加
epoll_create1 epoll_createにフラグ引数を追加した新システムコール
inotify_init1 inotify_initにフラグ引数を追加した新システムコール
変更されたシステムコール一覧

注2:これは以下のようなコードで行います。

    int open_max = sysconf (_SC_OPEN_MAX);
    for (int i = 3; i < open_max; i++)
    close(i);

注3:つまり、socket(PF_INET, SOCK_STREAM|SOCK_CLOEXEC, ...)のような形で使えということです。最初はフラグ引数を追加した新システムコールを新設する方向で提案されていたのですが、Alan Coxがむやみにシステムコールを増やすなといい出してこの形に。どう見ても汚いと思うんだけど……。

7月版へ
1/2

Index
Linux Kernel Watch 8月版
 ブート時間の短縮にかけるカーネルアスリートたち
Page 1
 ブート高速化フレームワークの開発始まる
 ファイルディスクリプタリークを防げ!
  Page 2
  SSDドライブの耐用年数を増やすセクタ破棄リクエスト
 -stableの進ちょく

連載 Linux Kernel Watch


 Linux Squareフォーラム Linuxカーネル関連記事
連載:Linux Kernel Watch(連載中)
Linuxカーネル開発の現場ではさまざまな提案や議論が交わされています。その中からいくつかのトピックをピックアップしてお伝えします
連載:Linuxファイルシステム技術解説
ファイルシステムにはそれぞれ特性がある。本連載では、基礎技術から各ファイルシステムの特徴、パフォーマンスを検証する
特集:全貌を現したLinuxカーネル2.6[第1章]
エンタープライズ向けに刷新されたカーネル・コア
ついに全貌が明らかになったカーネル2.6。6月に正式リリースされる予定の次期安定版カーネルの改良点や新機能を詳しく解説する
特集:/procによるLinuxチューニング[前編]
/procで理解するOSの状態

Linuxの状態確認や挙動の変更で重要なのが/procファイルシステムである。/procの概念や/procを利用したOSの状態確認方法を解説する
特集:仮想OS「User Mode Linux」活用法
Linux上で仮想的なLinuxを動かすUMLの仕組みからインストール/管理方法やIPv6などに対応させるカーネル構築までを徹底解説
Linuxのカーネルメンテナは柔軟なシステム
カーネルメンテナが語るコミュニティとIA-64 Linux
IA-64 LinuxのカーネルメンテナであるBjorn Helgaas氏。同氏にLinuxカーネルの開発体制などについて伺った

MONOist組み込み開発フォーラムの中から、Linux関連記事を紹介します


Linux & OSS フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Linux & OSS 記事ランキング

本日 月間