改訂版: LinuxノートPC上でサスペンド/ハイバネーションする方法

昨年6月、私は「LinuxノートPCのサスペンドとハイバネーション」(翻訳記事)という記事を書いた。以来、状況がいくつか(嬉しいことに良い方向で)変わったため、再びこのテーマを取り上げることにした。また、前回の記事の中で書いたサスペンド用のシェルスクリプトについての改善案を何人かの読者からいただいたので、今回の新版でそれらの提案を取り入れた。ただ残念ながらコメントのほとんどが匿名で投稿されたものだったので、書いてくれた人たちのクレジットをきちんと示すことはできなかった。

前回の記事以来に起こった最大の状況の変化は、マルチコアCPU搭載のラップトップが今やデファクトスタンダードになっているということだ。Intel Core DuoプロセッサとCore2 Duoプロセッサはモバイルユーザ向けにSMP(対称型マルチプロセッシング)機能を提供しているだけでなく、それ以前のプロセッサと比べて消費電力も少なく、したがって発熱量も少ない。

これらの新しいマルチコアCPUはLinuxカーネルでもサポートされてはいるものの、SMPモードでそれらのCPUをフル活用するためにはかなり新しいバージョンのカーネルが必要だ。SMPをオンにした状態でのサスペンド(STR)はバージョン2.6.18以降のカーネルで可能だが、100%安定しているというわけではなく、レジューム時に時折クラッシュすることがある。またCore Duoシステムに関して私が遭遇したもう一つの問題として、バッテリー残量の計算違いがある。例えばバッテリーの残量レベルが70%の状態が一分間続き、その直後から40%の状態が一分間続き、またその直後には70%に戻るというようなことが起こると言った具合いだ。とは言えバージョン2.6.20のカーネル(現在リリース候補)では状況が大きく改善されているので、マルチコア搭載のラップトップをお持ちならカーネルをアップグレードすべきだろう。

前回の記事ではシステムのハイバネーションに使うことのできる3つのソリューションを紹介した。すなわちswsusp(カーネルに統合されている)、uswsusp(ユーザ空間モードで動くswsuspを実装したもの)、suspend2の3つだ。この中の内、私はuswsuspを使うことにした。理由はuswsuspが現時点では安定性と信頼性がともに最も高い方法であるためだ。suspend2は非常に優れたソフトウェアではあるものの今現在はまだメインラインのLinuxカーネルには統合されていないので、手動でのパッチ適用とカーネルのコンパイルとが必要となる。

嬉しいことにuswsuspは今では安定していてバージョン2.6.17以降のカーネルではうまく統合されており、また主要なLinuxディストリビューションのほとんどでサポートされている。例えばDebian(EtchとSid)やUbuntu(Edgy EftとFeisty Fawn)のユーザは、(ディストリビューションが提供するカーネルをそのまま実行している場合)uswuspパッケージをインストールし、パッケージマネージャに尋ねられたらスワップのパーティションを入力するだけで良い。そうしておくと、ラップトップをハイバネーションしたくなったらs2diskを実行すればハイバネーションすることができるようになる。

改訂版suspendスクリプトと自動化

前回の記事の中で私が書いたスクリプトについて何人かの読者が、時刻のずれの問題を回避するために、サスペンド時にシステムクロックの時刻をハードウェアクロックに保存し、レジューム時にハードウェアクロックから復帰させるべきだと提案してくれた。実は私のシステムではOpenNTPDを使ってネットワーク経由で時刻を同期しているため、そのような問題が起こったことはない。とは言えこれが良い提案であることに変わりはない。また別の読者はsedについて私よりも詳しく知っていて、ビデオカードのPCI IDを取得するために私がgrepとawkとsedのコマンドの組み合わせを使っていたところをsedのコマンド1つだけでできる方法を教えてくれた。エレガントなシェルスクリプトを書くということは一つのワザであり好ましいことであるので、これもsuspendスクリプトの新版に取り入れた。

さらに、与えられた引数によってサスペンド/ハイバネーションを切り替えることができるようにしたので、サスペンドする場合にはsuspend.sh suspend、ハイバネーションする場合にはsuspend.sh hibernateとして実行することができるようになった。

以下に改良したsuspendスクリプトの新版を示す。このスクリプトを/usr/local/sbin/suspend.shとして保存し、chmod +x /usr/local/sbin/suspend.shとして実行可能にしよう。

#!/bin/sh

# ビデオカードのIDを取得
ID=`lspci | sed -e '/VGA/!d' -e 's/ .*//' -e 's@0000:@@' -e 's@:@/@' -eq`

# 一時ファイルをセキュアに作成
TMP_FILE=`mktemp /var/tmp/video_state.XXXXXX`
trap 'rm -f $TMP_FILE' 0 1 15

# Xでのグラフィックの崩壊を避けるために仮想端末1に切り替え
chvt 1

# システムクロックとハードウェアクロックを同期
hwclock --directisa --localtime --systohc

# (念のため)まだ書き込まれていないデータをすべて書き込み
sync

# ビデオカードの現在のデータを一時ファイルへダンプ
cat /proc/bus/pci/$ID > $TMP_FILE

# サスペンド、あるいは、ハイバネーション
case "$1" in
  suspend)   echo -n mem > /sys/power/state ;;
  hibernate) s2disk  ;;
esac

# レジューム時にビデオカードのデータを一時ファイルから復帰
cat $TMP_FILE > /proc/bus/pci/$ID

# ハードウェアクロックとシステムクロックを同期
hwclock --directisa --localtime --hctosys

# (Xが実行中の)仮想端末7に切り替え
chvt 7

# 一時ファイルを消去
rm -f $TMP_FILE

スクリプトのテストはDebianのSidとUbuntuのEdgy Eft上で行なった。uswsuspにはサスペンド(STR)用のs2ramもあるが、私がテストしたところ正しく動かなかったので(信頼性を重視して)以前と同様の方法のままにしておいた。

あなたのラップトップがGeForceビデオカードを搭載していてプロプライエタリのNvidiaドライバを使っている場合には、ビデオカードのデータを一時ファイルに保存してレジューム時に復帰させる必要はないので、関連する行を上のスクリプトから削除しよう。ただしその場合には追加のステップが必要になる。まず必ずNvidiaドライバの最新バージョンである9xxxシリーズをインストールし(8xxxドライバは正しくサスペンドしない)、NVreg_Mobile=1オプション付きでnvidiaカーネルモジュールがロードされるようにする。そして/etc/X11/xorg.confのデバイスセクションに「Option "NvAGP" "1"」という行を付け加えよう。

ところでここで私はATI Radeon搭載ラップトップのユーザには謝らなければならない。私はATI Radeon搭載ラップトップを持っていないので、上記のような点に関して、特に何か役に立つようなことを言うことができない。

ではacpidを使って、ラップトップのフタを閉じる際にACアダプタが付いているかどうかによってサスペンドかハイバネーションのどちらかを自動的に行なうようにしよう。つまりACアダプタが接続されている場合にはサスペンドを行ない、ACアダプタが接続されていない場合にはハイバネーションを行なうようにするということだ。そこでまず前回の記事にある説明に従って/etc/acpi/events/lidファイルを作成し、以下のスクリプトを/etc/acpi/actions/lid.shとして保存し、ファイルのパーミッションを実行可能に変更しよう。

#!/bin/sh
if grep -q off-line /proc/acpi/ac_adapter/AC/state; then
  /usr/local/sbin/suspend.sh hibernate
else
  /usr/local/sbin/suspend.sh suspend
fi

ただし/procファイルシステムの中であなたのラップトップのACアダプタは別の名前になっているかもしれないので、その場合にはそれに応じてスクリプトを適宜変更しよう。

次に、バッテリーの残量レベルが4%に達したら自動的にラップトップをハイバネーションするようにacpidを使ってさらなる自動化をしてみよう。そのためにはバッテリーのイベントとそれに対応するアクションを取り扱う以下の各ファイルを作成する。

/etc/acpi/events/battery

event=battery.*
action=/etc/acpi/actions/battery.sh

/etc/acpi/actions/battery.sh

#!/bin/sh
if grep -q on-line /proc/acpi/ac_adapter/AC/state; then
  exit 0
fi

BAT_DIR=/proc/acpi/battery/BAT0
FULL_BAT=`grep 'last full capacity' ${BAT_DIR}/info | awk '{ print $4 }'`
CUR_BAT=`grep 'remaining capacity' ${BAT_DIR}/state | awk '{ print $3 }'`
AVG=`expr $(expr ${CUR_BAT} \* 100) / ${FULL_BAT}`

if [ "$AVG" -le "4" ]; then
  /usr/local/sbin/suspend.sh hibernate
fi

ここでもやはりあなたの設定に合うようBAT_DIR変数の値を調整しよう。そしてchmod +x /etc/acpi/actions/battery.shとして2つめのファイルを実行可能にする。最後にacpidデーモンを起動すれば完了だ。

まとめ

自分のお気に入りのディストリビューションの最新版ならラップトップ上で上記のようなことをすべて自動的にやってくれるのだから、このような記事は無駄だと言う読者もいるかもしれない。確かにその通りで、状況は改善してきている。しかしそれでもやはり完璧ではない。例えばUbuntuのEdgy Eftではまだそのままでは私のIBM ThinkPad R50eとSony VAIO VGN-FE21Mではレジュームすることができない。と言ってもそれはベンダがACPIの仕様を多分に誤用しているためであって、ディストリビューションやカーネルの開発者が責められるべき類いのものではない。そういうわけでこの記事とともに、TuxMobilLinux on Laptopsといったサイトがあなたのラップトップ上でLinuxをうまく走らせるために役立つかもしれない。

NewsForge.com 原文