大学生ぐらいの時から不思議だったのですが、飲み過ぎた日、もしくは次の日に「音が高く」聞こえる人居ませんか?
普段から聞き慣れた曲って頭の中で再生できるじゃないですか。あれをやりながら、実際にその曲を聴くと、普段は合っているのに、酒飲みすぎた日は実際の曲の方が音が高いんです。
頭の中で再生できるほど聞き込んでなくても、曲の始まりくらいは覚えてますよね。さあ、この音が鳴るぞ〜と思っている音と、実際に聞こえてくる音がズレていて、高く感じるんです。
そういえば、環境音とか雑音も普段より高く聞こえているはずですが、気になりませんね。私が音を覚えられないためか、高いのか合っているのか良くわからないからでしょうか……?
聞こえる音が高くなると書きましたが「聞こえる音が高くなる」ではなく「記憶していた音が低くなる」可能性もあります。
確かめるには、酔って音が変に聞こえている時に何かの音を覚えて、素面の時に聞いてみる方法があります。音が高く聞こえたら酔うと耳がおかしくなって記憶は正しく、同じ音に聞こえたら耳は正しくて酔うと記憶がおかしくなる、というように判断できるはずです。
しかし、酔ってるときにそんなことしたら絶対気持ち悪くなるので、確かめたくありません……。あと両方おかしくなる場合は判別できません。
飲み会で思い出す度に色んな人に聞いてますが、今のところ同意してくれる人が居らず。一体、何なんでしょう?何かの病気?
先日(2017年6月30日の日記参照)の続きです。Cocos2d-xがビルドできたら、開発環境のセットアップをします。といってもsetup.pyを実行して、質問に答えるだけです。
$ cd cocos2d-x $ ./setup.py Setting up cocos2d-x... ->Check environment variable COCOS_CONSOLE_ROOT ->Search for environment variable COCOS_CONSOLE_ROOT... ->COCOS_CONSOLE_ROOT not found -> Add COCOS_CONSOLE_ROOT environment variable... ->Added COCOS_CONSOLE_ROOT=/home/katsuhiro/usr/src/cocos2d-x/tools/cocos2d-console/bin ... ->Check environment variable NDK_ROOT ->Search for environment variable NDK_ROOT... ->NDK_ROOT not found ->Search for command ndk-build in system... ->Command ndk-build not found ->Please enter the path of NDK_ROOT (or press Enter to skip): ★★Enterを押す★★ ... ->Check environment variable ANDROID_SDK_ROOT ->Search for environment variable ANDROID_SDK_ROOT... ->ANDROID_SDK_ROOT not found ->Search for command android in system... ->Command android not found ->Please enter the path of ANDROID_SDK_ROOT (or press Enter to skip): ★★Enterを押す★★ ... Please execute command: "source /home/katsuhiro/.bashrc" to make added system variables take effect
最初にAndroid NDKの場所を聞かれ、次にAndroid SDKの場所を聞かれます。もしAndroid向けにビルドしないのであれば、Enterを連打して無視しても構いません。私はスマホでもゲームを動かしてみたかったのでインストールしました。
Android SDKのコマンドラインツールは Android Studioのダウンロードサイトの一番下にある "Get just the command line tools" にあります。Android NDKは Android NDKのダウンロードサイトにあります。
SDKとNDKはどこに配置しても良いみたいです。私は~/usr/lib/ の下に放り込んでおきました。
$ cd ~/usr/lib $ unzip android-ndk-r15b-linux-x86_64.zip ...省略... $ ln -s android-ndk-r15b android-ndk $ cd ~/usr/lib $ mkdir android-sdk $ cd android-sdk $ unzip sdk-tools-linux-3859397.zip ...省略... $ cd ~/usr/lib $ tree -L 2 . ├──android-ndk -> android-ndk-r15b ├──android-ndk-r15b │ ├──CHANGELOG.md │ ├──README.md │ ├──build │ ├──meta │ ├──ndk-build │ ├──ndk-depends │ ├──ndk-gdb │ ├──ndk-stack │ ├──ndk-which │ ├──platforms │ ├──prebuilt │ ├──python-packages │ ├──shader-tools │ ├──simpleperf │ ├──source.properties │ ├──sources └──android-sdk └──tools
それとSDKやNDKのバージョン名が変わるたびにセットアップし直すのは面倒だったので、NDKはシンボリックリンクを張りました。手抜きです……。
$ cd cocos2d-x $ ./setup.py Setting up cocos2d-x... ->Check environment variable COCOS_CONSOLE_ROOT ->Search for environment variable COCOS_CONSOLE_ROOT... ->COCOS_CONSOLE_ROOT is found : /home/katsuhiro/usr/src/cocos2d-x/tools/cocos2d-console/bin ... ->Check environment variable NDK_ROOT ->Search for environment variable NDK_ROOT... ->NDK_ROOT not found ->Search for command ndk-build in system... ->Command ndk-build not found ->Please enter the path of NDK_ROOT (or press Enter to skip): /home/katsuhiro/usr/lib/android-ndk -> Add NDK_ROOT environment variable... ->Added NDK_ROOT=/home/katsuhiro/usr/lib/android-ndk ->Check environment variable ANDROID_SDK_ROOT ->Search for environment variable ANDROID_SDK_ROOT... ->ANDROID_SDK_ROOT not found ->Search for command android in system... ->Command android not found ->Please enter the path of ANDROID_SDK_ROOT (or press Enter to skip): /home/katsuhiro/usr/lib/android-sdk -> Add ANDROID_SDK_ROOT environment variable... ->Added ANDROID_SDK_ROOT=/home/katsuhiro/usr/lib/android-sdk ... Please execute command: "source /home/katsuhiro/.bashrc" to make added system variables take effect
環境設定はうまく行ったようです。最後に出ているメッセージの言う通りsource ~/.bashrcを実行すれば、cocosというコマンドが実行できるようになっているはずです。
Cocos2d-xの開発ではcocosコマンドを使うそうです。まずはcocos newでゲームのプロジェクト(テンプレート?)を生成します。
プロジェクト名はmygame1で、パッケージ名はnet.katsuster.mygame1(-p net.katsuster.mygame1)、C++ で開発します(-l cpp)。プロジェクトをディレクトリmygameに置いてもらいます(-d mygame)。何も指定しないとMyCppGameというディレクトリ名になりますが、リネームしても別に問題はありません。
$ mkdir ~/cocos $ cd ~/cocos $ cocos new mygame1 -p net.katsuster.mygame1 -l cpp -d mygame > Copy template into /home/katsuhiro/cocos/mygame/mygame1 > Copying Cocos2d-x files... > Rename project name from 'HelloCpp' to 'mygame1' > Replace the project name from 'HelloCpp' to 'mygame1' > Replace the project package name from 'org.cocos2dx.hellocpp' to 'net.katsuster.mygame1' > Replace the Mac bundle id from 'org.cocos2dx.hellocpp' to 'net.katsuster.mygame1' > Replace the iOS bundle id from 'org.cocos2dx.hellocpp' to 'net.katsuster.mygame1' $ ls mygame mygame1 $ ls mygame/mygame1/ CMakeLists.txt cocos2d proj.ios_mac proj.win10 Classes proj.android proj.linux proj.win32 Resources proj.android-studio proj.tizen
ディレクトリを覗くと、訳のわからないファイルが大量に生成されていて面食らいますが、とりあえず何も気にせず、ビルド&実行してみます。
ビルド対象は先程生成したmygame1(-s mygame/mygame1)で、ターゲットはLinux向け(-p linux)です。初回のビルドはかなり時間がかかりますが、二回目以降は変更した部分だけビルドするため、非常に早いです。
$ cocos compile -s mygame/mygame1/ -p linux Building mode: debug running: 'cmake -DCMAKE_BUILD_TYPE=Debug -DDEBUG_MODE=ON ..' -- The C compiler identification is GNU 6.3.0 ...(初回だけかなり時間掛かります)... [100%] Building CXX object CMakeFiles/MyGame.dir/Classes/HelloWorldScene.cpp.o [100%] Building CXX object CMakeFiles/MyGame.dir/Classes/AppDelegate.cpp.o [100%] Linking CXX executable bin/MyGame [100%] Built target MyGame Build succeed. $ cocos run -s mygame/mygame1/ -p linux Building mode: debug running: 'cmake -DCMAKE_BUILD_TYPE=Debug -DDEBUG_MODE=ON ..' ... [100%] Built target MyGame Build succeed. Deploying mode: debug Starting application. running: '/home/katsuhiro/cocos/mygame/mygame1/bin/debug/linux/MyGame' /home/katsuhiro/cocos/mygame/mygame1/bin/debug/linux/MyGame: error while loading shared libraries: libfmod.so.6: cannot open shared object file: No such file or directory Error running command, return code: 127.
上記ではビルドだけするため、あえてcocos compileを使いましたが、実はcocos runがビルドと実行を同時にやってくれるので、cocos compileは使う必要がありません。
それは良いとしてcocos runが失敗してしまいました。libfmod.so.6が無いとか言っています。このライブラリはcocos2d-x/external/linux-specific/fmod/prebuilt/64-bit/libfmod.soに置いてあるようなのですが、名前がちょっと違っているので、libfmod.so.6という名前のシンボリックリンクを作ってあげて、ライブラリのパスも通してあげると動きます。
$ cd ~/usr/src/cocos2d-x $ cd external/linux-specific/fmod/prebuilt/64-bit $ ln -s libfmod.so libfmod.so.6 $ echo 'export LD_LIBRARY_PATH=$COCOS_X_ROOT/cocos2d-x/external/linux-specific/fmod/prebuilt/64-bit' >> ~/.bashrc $ source ~/.bashrc $ cd ~/cocos $ cocos run -s mygame/mygame1/ -p linux ... Build succeed. Deploying mode: debug Starting application. running: '/home/katsuhiro/cocos/mygame/mygame1/bin/debug/linux/MyGame' Ready for GLSL Ready for OpenGL 2.0 gl.supports_OES_packed_depth_stencil: false gl.supports_OES_depth24: false gl.supports_OES_map_buffer: false gl.supports_vertex_array_object: true gl.renderer: Gallium 0.4 on llvmpipe (LLVM 3.9, 128 bits) gl.version: 3.0 Mesa 13.0.6 cocos2d.x.compiled_with_gl_state_cache: true cocos2d.x.version: cocos2d-x-3.15.1 gl.supports_S3TC: true cocos2d.x.build_type: DEBUG cocos2d.x.compiled_with_profiler: false gl.vendor: VMware, Inc. gl.max_texture_units: 96 gl.max_texture_size: 8192 gl.supports_ATITC: false gl.supports_ETC1: false gl.supports_PVRTC: false gl.supports_NPOT: true gl.supports_BGRA8888: false gl.supports_discard_framebuffer: false libpng warning: iCCP: known incorrect sRGB profile cocos2d: QuadCommand: resizing index size from [-1] to [2560]
下記のように宇宙人のような顔が描いてある画面が出れば成功です。
そういえば何も考えずTigerVNC上で実行していますが、難無く60fps出て驚きました。画面サイズが小さくて、動きがほとんど無いから軽いだけかも知れませんけど、便利だし問題起きるまでVNC越しで開発してみようかなあ。
ひとまずLinux向けには作り始めることができそうです。今回はここまで。
目次: Linux
ちょっと用事があって、ユーザプロセスの仮想アドレスから、物理アドレスへの変換を行うプログラムを作りました。名前はv2p(virt2phys)です。何の捻りもありません。ソースコードは GitHubに置いています。
やっていることは/proc/pid/pagemapを読んで、わかりやすい形式で表示しているだけです。言葉で説明するより動いている姿の方がわかりやすいと思います。
実験に使った環境はDebian GNU/Linux Testing amd64版です。別にDebianじゃなくても最近のLinuxなら動くと思います。
このツールの実行には、プロセスIDと仮想アドレスが必要です。まず解析に使うプロセスを作って、プロセスIDを調べます。
$ cat [Press Ctrl+Z] [1]+ Stopped cat $ ps PID TTY TIME CMD 2148 pts/4 00:00:00 bash 14010 pts/4 00:00:00 cat ★★catのPIDを覚えておく★★ 14015 pts/4 00:00:00 ps
プロセスIDがわかったら、物理アドレスに変換したい仮想アドレスを決めます。ここでは例としてヒープ領域の先頭アドレスを変換しましょう。
$ cat /proc/14010/maps 55ff09a4f000-55ff09a57000 r-xp 00000000 08:11 5505114 /bin/cat 55ff09c56000-55ff09c57000 r--p 00007000 08:11 5505114 /bin/cat 55ff09c57000-55ff09c58000 rw-p 00008000 08:11 5505114 /bin/cat 55ff0b2cb000-55ff0b2ec000 rw-p 00000000 00:00 0 [heap] ★★これを使う★★ 7fdbeb6a3000-7fdbeb838000 r-xp 00000000 08:11 4980859 /lib/x86_64-linux-gnu/libc-2.24.so 7fdbeb838000-7fdbeba38000 ---p 00195000 08:11 4980859 /lib/x86_64-linux-gnu/libc-2.24.so ...(snip)... 7ffdde79c000-7ffdde7be000 rw-p 00000000 00:00 0 [stack] 7ffdde7fb000-7ffdde7fd000 r--p 00000000 00:00 0 [vvar] 7ffdde7fd000-7ffdde7ff000 r-xp 00000000 00:00 0 [vdso]
プロセスIDと仮想アドレスがわかったので、物理アドレスに変換します。
$ sudo ./src/v2p 14010 0x55ff0b2cb000 0x4000 pid: 14010: virt:0x55ff0b2cb000, phys:0x73a42b000 virt:0x55ff0b2cc000, phys:(not present) virt:0x55ff0b2cd000, phys:(not present) virt:0x55ff0b2ce000, phys:(not present)
変換できました。特権のない状態で実行すると全て0x00000000になってしまいますので、お気をつけください。
表示の意味は特に何の捻りもなく、物理アドレスをそのまま示しています。ちなみに (not present) と表示される場合は、まだカーネルからページが割り当てられておらず、仮想アドレスに対応する物理アドレスが存在しないことを意味しています。
これだけだとGitHubに書いたReadmeそのものだし、へぇー…って感じがするだけで面白くないので、catにgdbでアタッチしてヒープ領域を触ったらどうなるか見てみます。
$ sudo ./src/v2p 14010 0x55ff0b2cb000 0x6000 pid: 14010: virt:0x55ff0b2cb000, phys:0x73a42b000 virt:0x55ff0b2cc000, phys:(not present) virt:0x55ff0b2cd000, phys:(not present) ★これを読む★ virt:0x55ff0b2ce000, phys:(not present) virt:0x55ff0b2cf000, phys:(not present) ★これを読む★ virt:0x55ff0b2d0000, phys:(not present) $ gdb -p 14010 GNU gdb (Debian 7.12-6) 7.12.0.20161007-git Copyright (C) 2016 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> ... 0x00007fdbeb77e690 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84 84 ../sysdeps/unix/syscall-template.S: そのようなファイルやディレクトリはありません. (gdb) x/4x 0x55ff0b2cd000 0x55ff0b2cd000: 0x00000000 0x00000000 0x00000000 0x00000000 (gdb) x/4x 0x55ff0b2cf000 0x55ff0b2cf000: 0x00000000 0x00000000 0x00000000 0x00000000 $ sudo ./src/v2p 14010 0x55ff0b2cb000 0x4000 pid: 14010: virt:0x55ff0b2cb000, phys:0x73a42b000 virt:0x55ff0b2cc000, phys:(not present) virt:0x55ff0b2cd000, phys:0x1cb695000★ゼロページが割り当たる★ virt:0x55ff0b2ce000, phys:(not present) virt:0x55ff0b2cf000, phys:0x1cb695000★ゼロページが割り当たる★ virt:0x55ff0b2d0000, phys:(not present)
試しに3番目と5番目の領域からデータを読み出してみました、どちらも0データが見えます。読み出したあとは0x1cb695000(数字は環境によって違います)という同じアドレスが割り当たりました。このアドレスは全て0で埋まっている特殊なページを指していて、読み出したときにとにかく0データを返せば良い領域に使われるらしいです。
じゃあ今度は書いてみたらどうなるでしょう?
$ sudo ./src/v2p 14010 0x55ff0b2cb000 0x6000 pid: 14010: virt:0x55ff0b2cb000, phys:0x73a42b000 virt:0x55ff0b2cc000, phys:(not present) ★これに書く★ virt:0x55ff0b2cd000, phys:0x1cb695000 ★これに書く★ virt:0x55ff0b2ce000, phys:(not present) virt:0x55ff0b2cf000, phys:0x1cb695000 virt:0x55ff0b2d0000, phys:(not present) (gdb) set *((long *)(0x55ff0b2cc000))=0x89abcdef (gdb) x/4x 0x55ff0b2cc000 0x55ff0b2cc000: 0x89abcdef 0x00000000 0x00000000 0x00000000 (gdb) set *((long *)(0x55ff0b2cd000))=0x12345678 (gdb) x/4x 0x55ff0b2cd000 0x55ff0b2cd000: 0x12345678 0x00000000 0x00000000 0x00000000 $ sudo ./src/v2p 14010 0x55ff0b2cb000 0x4000 pid: 14010: virt:0x55ff0b2cb000, phys:0x73a42b000 virt:0x55ff0b2cc000, phys:0x73c76a000★ページが割り当たる★ virt:0x55ff0b2cd000, phys:0x72edd0000★ゼロページが外され、別のページが割り当たる★ virt:0x55ff0b2ce000, phys:(not present) virt:0x55ff0b2cf000, phys:0x1cb695000 virt:0x55ff0b2d0000, phys:(not present)
2番目の (not present) な領域にも書けますし、3番目のゼロページが割り当たっている領域にも書けます。読み出してみても、ちゃんと書けていることがわかります。
書き込んだあとの仮想アドレスと物理アドレスの対応を見ると、2番目はreadした時とは違ってゼロページではなさそうなアドレスが割当たります。3番目はゼロページが外され、別のページが割り当てられます。
不思議に見えるかも知れませんが、ゼロページは0データを返す専門なので、書き込みができません、というかカーネルが書き込みを許しません。ゼロページに書き込もうとした場合はゼロページを外して別のページを割り当て、以降はそのページを使って下さい、という仕組みになっています。