まずは挨拶
Qiita に初投稿です。よろしくお願いいたします。
サスペンドが出来ない
ある時 Ubuntu をアップデートしました。アップデート後の環境は
$ lsb_release -d -r
Description: Ubuntu 14.04.5 LTS
Release: 14.04
です。
さて、寝ようと PC をサスペンドをしたのですがすぐに起き上がります。何かキーボートを押したかな? と、またサスペンドをしたのですがすぐに起き上がります。寝れません。(うそ。寝ました) 調べます。いろいろ検索に掛かりますが、だいたいが ArchWiki に書いてある解決策になるようです。曰く、 /proc/acpi/wakeup の
LID0
以外を disabled
にしなさい。以下の様に。
# echo XHC1 > /proc/acpi/wakeup
、、、えっ? 何でリダイレクトなの? ま、まあ 素直に従います。
$ cat /proc/acpi/wakeup
Device S-state Status Sysfs node
UAR1 S4 *disabled pnp:00:0b
USB1 S3 *disabled
RP01 S4 *disabled pci:0000:00:1c.0
RP03 S4 *disabled pci:0000:00:1c.2
BR40 S4 *disabled pci:0000:02:00.0
RP04 S4 *disabled pci:0000:00:1c.3
GLAN S4 *disabled
EHC1 S4 *enabled pci:0000:00:1d.0
EHC2 S4 *enabled pci:0000:00:1a.0
XHC S4 *enabled pci:0000:00:14.0
HDEF S4 *disabled pci:0000:00:1b.0
PEG0 S4 *disabled
PEGP S4 *disabled
PEG1 S4 *disabled
PEG2 S4 *disabled
(上記の wakeup の内容はサスペンドが効かない状況とは若干違うかもしれません。解決してからまたアップデートしたので。大筋ということで)
自分の環境だと EHC1
と EHC2
と XHC
を disabled
にすればいいのだな。
$ sudo sh -c "echo EHC1 > /proc/acpi/wakeup"
$ sudo sh -c "echo EHC2 > /proc/acpi/wakeup"
$ sudo sh -c "echo XHC > /proc/acpi/wakeup"
$ cat /proc/acpi/wakeup
Device S-state Status Sysfs node
UAR1 S4 *disabled pnp:00:0b
USB1 S3 *disabled
RP01 S4 *disabled pci:0000:00:1c.0
RP03 S4 *disabled pci:0000:00:1c.2
BR40 S4 *disabled pci:0000:02:00.0
RP04 S4 *disabled pci:0000:00:1c.3
GLAN S4 *disabled
EHC1 S4 *disabled pci:0000:00:1d.0
EHC2 S4 *disabled pci:0000:00:1a.0
XHC S4 *disabled pci:0000:00:14.0
HDEF S4 *disabled pci:0000:00:1b.0
PEG0 S4 *disabled
PEGP S4 *disabled
PEG1 S4 *disabled
PEG2 S4 *disabled
サスペンドが出来るようになりました。めでたしめでたし。が、この記事の主題はそこではありません。主題は
なぜ リダイレクトによる /proc/acpi/wakeup への書き込みで結果が内容と違うのか
という事です。
/proc/acpi/wakeup に書き込むとはどういうことか?
上述の出力を見れば分かるように /proc/acpi/wakeup は複数行です。普通は echo EHC1 > /proc/acpi/wakeup
は 1行になると考えられます。
- /proc下は 仮想ファイルシステム(procfs)で、プロセス、カーネルにファイル形式でアクセスするインターフェースの役割を持つ。逐次作成している。
しかしこれは 前述の例で 1行にならない説明になっていません。
- 想像だが、wakeup を扱っているモジュールが ファイルディスクリプタの操作で文字列を受け内容を解釈し、wakeupファイルを逐次作成している。
これまた ぼんやりしています。内容を解釈って具体的にはなんだろう?
よく分からないので具体的に何をやっているのか見てみましょう。
strace で確認
strace
コマンドを使います。 strace
はシステムコールやシグナルを監視するツールです。
まずは EHC1
を enabled
にして
$ sudo sh -c "echo EHC1=enabled > /proc/acpi/wakeup"
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1 S4 *enabled pci:0000:00:1d.0
、、、実はこの実行の認識が間違っている事を後で気付くのですが。
そして strace
。(なのだが、ちょっと長い上に getcwd("/home/xxx" … はまずいので)
$ sudo -p "sudo> " test
sudo>
$ sudo strace sh -c "echo EHC1 > /proc/acpi/wakeup" 2> strace_write_ehc1_to_wakeup.txt
## strace の出力行数
$ wc -l strace_write_ehc1_to_wakeup.txt
50 strace_write_ehc1_to_wakeup.txt
## head -10
$ head -10 strace_write_ehc1_to_wakeup.txt
execve("/bin/sh", ["sh", "-c", "echo EHC1 > /proc/acpi/wakeup"], [/* 18 vars */]) = 0
brk(0) = 0x7f03e785b000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f03e6ed7000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=106703, ...}) = 0
mmap(NULL, 106703, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f03e6ebc000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
## tail -10
$ tail -10 strace_write_ehc1_to_wakeup.txt
close(1) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "EHC1\n", 5) = 4
write(1, "\n", 1) = 1
dup2(10, 1) = 1
close(10) = 0
exit_group(0) = ?
+++ exited with 0 +++
## システムコールの履歴
$ cat strace_write_ehc1_to_wakeup.txt | cut -s -d \( -f 1 |paste -d, - - - - -
execve,brk,access,mmap,access
open,fstat,mmap,close,access
open,read,fstat,mmap,mprotect
mmap,mmap,close,mmap,mmap
arch_prctl,mprotect,mprotect,mprotect,munmap
getpid,rt_sigaction,geteuid,brk,brk
getppid,getcwd,rt_sigaction,rt_sigaction,rt_sigaction
rt_sigaction,rt_sigaction,rt_sigaction,open,fcntl
close,fcntl,dup2,close,write
write,dup2,close,exit_group,
おそらく tail の出力に現れている writeシステムコール が /proc/acpi/wakeupへの書き込み処理の実態でしょう。しかしこれだけではよく分かりません。
そこで /proc/acpi/wakeup への書き込みと /dev/null への書き込みとの strace
の
出力の比較をしてみます。と、その前に strace
の出力で 0x7f03e785b000
などの0x(16進数12桁) の文字列があります。これはアドレスなので 0xzzzz
に置換して比較します。
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1 S4 *enabled pci:0000:00:1d.0
$ diff -U0 \
<(sudo strace sh -c "echo EHC1 > /dev/null" 2>&1 |sed -E 's/0x[a-fA-F0-9]{12}/0xzzzz/g') \
<(sudo strace sh -c "echo EHC1 > /proc/acpi/wakeup" 2>&1 |sed -E 's/0x[a-fA-F0-9]{12}/0xzzzz/g')
--- /proc/self/fd/11 2016-09-28 18:44:26.882716060 +0900
+++ /proc/self/fd/13 2016-09-28 18:44:26.883716065 +0900
@@ -1 +1 @@
-execve("/bin/sh", ["sh", "-c", "echo EHC1 > /dev/null"], [/* 18 vars */]) = 0
+execve("/bin/sh", ["sh", "-c", "echo EHC1 > /proc/acpi/wakeup"], [/* 18 vars */]) = 0
@@ -26 +26 @@
-getpid() = 30419
+getpid() = 30421
@@ -31 +31 @@
-getppid() = 30414
+getppid() = 30415
@@ -39 +39 @@
-open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
+open("/proc/acpi/wakeup", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
@@ -45 +45,2 @@
-write(1, "EHC1\n", 5) = 5
+write(1, "EHC1\n", 5) = 4
+write(1, "\n", 1) = 1
上記の結果から 文字列 "EHC1\n" の書き込み処理は、対象のファイルによってwriteシステムコール を
- /dev/null(通常ファイルもか): 5バイトを一気に処理
- /proc/acpi/wakeup: 4バイト処理(返り値1) した後、残りの 1バイトを処理
という様に呼び出している事が分かります。
さて、こうなると長い文字列を書き込んだ時どうなるのかが気になります。たぶん /proc/acpi/wakeup には何を書き込んでもいいような気がします。そこで 文字列 euu45678901234567890
を書き込んでみます。
$ diff -U0 \
<(sudo strace sh -c "echo euu45678901234567890 > /dev/null" 2>&1 |sed -E 's/0x[a-fA-F0-9]{12}/0xzzzz/g') \
<(sudo strace sh -c "echo euu45678901234567890 > /proc/acpi/wakeup" 2>&1 |sed -E 's/0x[a-fA-F0-9]{12}/0xzzzz/g')
--- /proc/self/fd/11 2016-09-28 20:57:35.643468246 +0900
+++ /proc/self/fd/13 2016-09-28 20:57:35.643468246 +0900
@@ -1 +1 @@
-execve("/bin/sh", ["sh", "-c", "echo euu45678901234567890 > /dev"...], [/* 18 vars */]) = 0
+execve("/bin/sh", ["sh", "-c", "echo euu45678901234567890 > /pro"...], [/* 18 vars */]) = 0
@@ -26 +26 @@
-getpid() = 31593
+getpid() = 31590
@@ -31 +31 @@
-getppid() = 31586
+getppid() = 31587
@@ -39 +39 @@
-open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
+open("/proc/acpi/wakeup", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
@@ -45 +45,6 @@
-write(1, "euu45678901234567890\n", 21) = 21
+write(1, "euu45678901234567890\n", 21) = 4
+write(1, "5678901234567890\n", 17) = 4
+write(1, "901234567890\n", 13) = 4
+write(1, "34567890\n", 9) = 4
+write(1, "7890\n", 5) = 4
+write(1, "\n", 1) = 1
上記の結果から 文字列の書き込み処理は、対象のファイルによって writeシステムコール を
- /dev/null(通常ファイルもか): 文字列を一気に(リミットあり1)処理
- /proc/acpi/wakeup: 4バイトずつ処理
という様に呼び出している事が分かります。
/proc/acpi/wakeup への書き込みの実際
先程、実行の認識が間違っていた、と書きましたが、実際に /proc/acpi/wakeup への書き込みの例を示します。
echo EHC1 をリダイレクト
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1 S4 *enabled pci:0000:00:1d.0
$ sudo sh -c "echo EHC1 > /proc/acpi/wakeup"
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1 S4 *disabled pci:0000:00:1d.0
EHC1 の状態: enabled -> disabled
echo EHC1=disabled をリダイレクト
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1 S4 *disabled pci:0000:00:1d.0
$ sudo sh -c "echo EHC1=disabled > /proc/acpi/wakeup"
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1 S4 *enabled pci:0000:00:1d.0
EHC1 の状態: disabled -> enabled
よって XXX=disabled 形式の設定は無効
echo EHC1=1 をリダイレクト
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1 S4 *enabled pci:0000:00:1d.0
$ sudo sh -c "echo EHC1=1 > /proc/acpi/wakeup"
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1 S4 *disabled pci:0000:00:1d.0
EHC1 の状態: enabled -> disabled
よって XXX=1 形式の設定は無効
echo EHC1 を連続でリダイレクト
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1 S4 *disabled pci:0000:00:1d.0
$ sudo sh -c "echo EHC1 > /proc/acpi/wakeup"
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1 S4 *enabled pci:0000:00:1d.0
$ sudo sh -c "echo EHC1 > /proc/acpi/wakeup"
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1 S4 *disabled pci:0000:00:1d.0
EHC1 の状態: disabled -> enabled -> disabled
トグル処理 になる
echo EHC123 をリダイレクト
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1 S4 *disabled pci:0000:00:1d.0
$ sudo sh -c "echo EHC123 > /proc/acpi/wakeup"
$ cat /proc/acpi/wakeup |grep -E "^EHC"
EHC1 S4 *enabled pci:0000:00:1d.0
EHC2 S4 *disabled pci:0000:00:1a.0
EHC1 の状態: disabled -> enabled
変数 EHC1 への書き込みと解釈される。EHC123 という変数が出来るわけではない
echo XHC を連続でリダイレクト
$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC S4 *disabled pci:0000:00:14.0
$ sudo sh -c "echo XHC > /proc/acpi/wakeup"
$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC S4 *enabled pci:0000:00:14.0
$ sudo sh -c "echo XHC > /proc/acpi/wakeup"
$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC S4 *disabled pci:0000:00:14.0
XHC の状態: disabled -> enabled -> disabled
トグル処理 になる
echo XHC5, echo XHC56 をリダイレクト
$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC S4 *disabled pci:0000:00:14.0
$ sudo sh -c "echo XHC5 > /proc/acpi/wakeup"
$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC S4 *disabled pci:0000:00:14.0
$ sudo sh -c "echo XHC56 > /proc/acpi/wakeup"
$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC S4 *disabled pci:0000:00:14.0
XHC の状態: disabled -> disabled -> disabled
XHC5, XHC56 の様に 最初の 3バイトが XHC でも 変数 XHC と解釈しない
echo XHC 0 をリダイレクト
$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC S4 *disabled pci:0000:00:14.0
$ sudo sh -c "echo XHC 0 > /proc/acpi/wakeup"
$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC S4 *enabled pci:0000:00:14.0
XHC の状態: disabled -> enabled
最初の 4バイト が "XHC " (この場合最後の1バイトはスペース) の時 変数として解釈するのだろう
/proc/acpi/wakeup に書き込むとは
以上のリダイレクト時のシステムコールや書き込み例から推察すると、 /proc/acpi/wakeup への書き込みは、 EHC1=enabled
といった様に値を指定出来るわけではなく、単に
リダイレクトで送る文字列の最初の4バイトが /proc/acpi/wakeup 内の変数であった時、その変数のトグル処理が行われる
という事です。
/proc 下のファイルに echo 1
, echo 0
とかをリダイクレトするのはよくやりますが、この wakeupファイル の様な、最初の4バイトだけを受け付けるファイルもある ってのは常識なのだろうか?
wakeup の他の設定方法
こちらの記事 の様な手順で設定することも出来ます。まず
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1 S4 *enabled pci:0000:00:1d.0
と pci:0000:00:1d.0
なので /sys/bus/pci/devices/0000:00:1d.0/power/wakeup
を読みます。
$ cat /sys/bus/pci/devices/0000:00:1d.0/power/wakeup
enabled
/proc/acpi/wakeup の EHC1
と同じく enabled
です。そこで disabled
を書き込んでみます。
$ sudo sh -c "echo disabled > /sys/bus/pci/devices/0000:00:1d.0/power/wakeup"
$ cat /sys/bus/pci/devices/0000:00:1d.0/power/wakeup
disabled
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1 S4 *disabled pci:0000:00:1d.0
/proc , /sys どちらにも反映されました。
ただし
$ sudo sh -c "echo enab > /sys/bus/pci/devices/0000:00:1d.0/power/wakeup"
sh: echo: I/O error
$ sudo sh -c "echo 1 > /sys/bus/pci/devices/0000:00:1d.0/power/wakeup"
sh: echo: I/O error
$ cat /sys/bus/pci/devices/0000:00:1d.0/power/wakeup
disabled
誤った文字列を書き込もうとすると I/O error
(I/Oエラーなんだ) を吐きます。正確な情報を設定しましょう。
$ sudo sh -c "echo enabled > /sys/bus/pci/devices/0000:00:1d.0/power/wakeup"
$ cat /sys/bus/pci/devices/0000:00:1d.0/power/wakeup
enabled
そう考えると、wakeup に関しては procfs
へのアクセスの方が sysfs
へのアクセスよりも安全な気がします。(まあ、この辺を変更する人間が安全に配慮をしないわけがないのですが)
あらためて
今後ともよろしくお願いいたします。
https://linuxjm.osdn.jp/html/LDP_man-pages/man2/write.2.html
単に $ man 2 write
だけど