2019/09/30

プライド≠pride

日本語では外国由来の単語をカタカナで表記しています。しかしその外来語が具体的な事物であるならいざ知らず、抽象的な概念を表している場合にはカタカナで表記される外来語が元々の国での意味と同じかどうかは分かりません。またその外来語を使って発信している側も、その外来語を受け取る側も、はたして意味を同じように認識しているでしょうか。

例えばカタカナで「プライド」と表記するのは、英単語「pride」のことです。この単語は文中で「誰々はプライドが高いから云々」のように使われます。この外来語を使う側も受け取る側も、なんだか文意がわかったような気になりますが、本当にわかっているでしょうか。

外来語「プライド」ではなく日本語で表現するなら「自尊心」という概念があてはまります。ここで「自尊心」という抽象概念が何を指しているのかという問題が派生しますが、それは別途考えることにして、ここでは踏み込まないことにします。

外来語「プライド」の日本語表現はそれだけではありません。英単語「pride」を日本語に直接的に訳語にあてはめただけでなく、外来語「プライド」として意味が派生したものとして「選民意識」という概念もあるのではないかと考えています。

「自尊心」と「選民意識」とでは、受け取る側からすれば相当ニュアンスが異なります。しかし発信側が外来語「プライド」として表現しただけでは、どのような意味で語っているか、実際のところわかりません。

普段の会話では、ここまで厳格に考える必要はないでしょう。でも曖昧さを残す余地がある表現を(もしかすると意図的に)使うのは、コミュニケーションを壊す要因になると思います。

2019/09/29

第243回TOEIC公開テストを受験しました

今日が受験日でした。何かの直接的な目的があるという訳ではなく、英語能力を確認するために10年以上前から毎年この時期にTOEICを受験することにしています。

 TOEICに対する批判があることは承知しています。TOEICで得点がとれても英語ができる訳ではないとか、TOEICは受験テクニックで点数がとれてしまうなどの主張があるようです。それは一理あるのかもしれませんし、そういう側面もあるでしょう。しかし英語能力が無いのに、TOEICテクニックだけで高得点を得るというのは「おとぎ話」だと思います。どんな資格試験でも、同じことでしょう。

TOEICを受験すると、いつも思うことですが、時間が圧倒的に足りません。いつも最後の20問ほどは解答する時間が無くて「塗り絵」になってしまいます。TOEICで高得点を得ることも(ある意味で)目標ではありますが、少なくとも試験時間内に全問を解答できるようになることの方が、切実な目標です。

2019/09/24

.set NHRDRV,0x475 # Number of hard drives

FreeBSDのブートプロセスをみる』(白崎博生、UNIX MAGAZINE COLLECTION、2006年)を追体験しながら、カーネルを勉強しています。カーネルデバッグができる環境をVirtualBoxを使って構築したので、第1回「Boot Manager」から順番に読んでいきます。

大雑把に言うとboot0は、BIOSによってディスクから読み込まれ、boot1を読んで制御を渡すのが、主な役割です。その他にも処理がありますが、歴史的な事情によりINT 13Hの呼び出し方法が違うなどであり(それだけではありませんが)、ざっと眺めておくだけにします。

しかしながら、それほど重要というほどではありませんが、気になったのは、BIOSのデータエリア0x475番地を参照している箇所です。

boot0.Sでは「.set NHRDRV,0x475 # Number of hard drives」のようにシンボルが定義されており、以下のようなコメントも入っています。
    121  * NHRDRV is the address in segment 0 where the BIOS writes the
    122  *      total number of hard disks in the system.

このような情報はWebを検索すると見つかったりします。しかし得てして何かの情報の孫引きだったりするので、最もオリジナルの一次情報を見つけたいところですが、それはなかなか見つかりません。そもそもBIOSにおける非公開(に準じる)情報だったりすると、オリジナルの情報にはアクセスできないことになります。せめて次善の策として、BIOSの内部構造を詳述した定評のある書籍などの記述を参照しておきたいところです。

2019/09/23

FreeBSDのブートプロセスをみる』(白崎博生、UNIX MAGAZINE COLLECTION、2006年)を追体験しながら、カーネルを勉強することにしました。まず最初にカーネルのリモートデバッグができる環境をVirtualBoxを使って準備しました。書籍ではFreeBSD 5.1が使われていますが、今更古いバージョンを用意するのもなんなので、最新のFreeBSD 12.0を使うつもりです。記事の記述とは合わないところが出てくるかもしれませんが、それも含めて勉強です。

まずは第1回「Boot Manager」から始めます。記事ではboot0のソースがboot/i386/boot0/boot0.sと書かれていますが、stand/i386/boot0/boot0.Sに変わっていました。コメントも記事とは変わっているようですが、ロジックは変化していません。

まず最初に「自分自身を00600Hへ移動する」処理があります。レジスタを適切に設定してrepプレフィックスをつけたmovswで転送する訳ですが、ここで疑問が生じました。該当箇所は次のようになっています。
   181  start:          cld                             # String ops inc
   182                  xorw %ax,%ax                    # Zero
   183                  movw %ax,%es                    # Address
   184                  movw %ax,%ds                    #  data
   185                  movw %ax,%ss                    # Set up
   186                  movw $LOAD,%sp                  #  stack
   187 
   188          /*
   189           * Copy this code to the address it was linked for, 0x600 by default.
   190           */
   191                  movw %sp,%si                    # Source
   192                  movw $start,%di                 # Destination

ここでSIレジスタには転送元、DIレジスタには転送先が入っています。$LOADというのは、上述したソースコードのちょっと前で「.set LOAD,0x7c00」のように定義されています。しかし$startというのはラベルとして定義されているだけです。しかもコードの最初にあるので0x0000ではないのでしょうか。どうして0x0600として扱われるのでしょうか。すくなくとも、このファイルには(コメントに0x600と書かれているだけで)なんの定義もありません。これが疑問でした。

Webを検索しても、boot0を解説している情報は幾つか見つかりますが、この疑問に答えてくれる情報がなかなか見つかりませんでした。

Webで検索を続けていると『FreeBSD Architecture Handbook』に次のような記述があることを知りました。
It is worth looking at the Makefile for boot0 (sys/boot/i386/boot0/Makefile ), as it defines some of the runtime behavior of boot0. For instance, if a terminal connected to the serial port (COM1) is used for I/O, the macro SIO must be defined (-DSIO). -DPXE enables boot through PXE by pressing F6. Additionally, the program defines a set of flags that allow further modification of its behavior. All of this is illustrated in the Makefile. For example, look at the linker directives which command the linker to start the text section at address 0x600, and to build the output file “as is” (strip out any file formatting):
そしてMakefileでは以下のように記述されていると書かれています。
BOOT_BOOT0_ORG?=0x600
LDFLAGS=-e start -Ttext ${BOOT_BOOT0_ORG} \
-Wl,-N,-S,--oformat,binary
そういうことなのか、と思いましたが、Makefileには次のような記述があるだけでした。
BOOT_BOOT0_ORG?=        0x600
ORG=${BOOT_BOOT0_ORG}
考え方は変わっていないのだと思いますが、実現方法が変わったのでしょう。今回は、そこまでは追いかけませんでした。しかしともすれば見過ごしてしまう点を追及できました。

2019/09/22

FreeBSDのカーネルのリモートデバッグをおこなう

FreeBSDのブートプロセスをみる』(白崎博生、UNIX MAGAZINE COLLECTION、2006年)を追体験しながら、カーネルを勉強しようと考えています。連載当時はFreeBSD 5.1でしたが、今はFreeBSD 12.0です。細かいところで記述内容と合わない点があります。まずは連載最後の17回を参考に、カーネルデバッグの環境を整えておこうと思います。

カーネルデバッガDDBが起動できるようなカーネルに入れ換える作業は、特に問題ありませんでした。

つぎはリモートデバッグの環境を準備しようと思います。書籍の記述ではVMwareを利用していますが、僕はVirtualBoxを使おうと思います。VMwareだろうが、VirtualBoxだろうが、もしくは仮想環境ではなく実機であろうが、考え方は変わらない筈です。その筈なのですが、当初うまくいかず苦労してしまいました。しかし最終的にリモートデバッグ環境ができたので、結果オーライという感じです。

もし実機を使ってリモートデバッグ環境を整えるなら、2台のマシン間をRS-232Cのヌルモデムケーブルで繋ぐことになるでしょう。仮想環境であれば、2台のゲスト環境のCOM1ポートを「仮想的」に接続されているように見せかける必要があります。それが連載記事でも紹介されている、名前付きパイプを使用する方法です。記事ではVMwareの設定としてシリアルポートを名前付きパイプとして設定しています。VirtualBoxでもシリアルポートを名前付きパイプとして設定できるので、当初この方法で2台のゲスト環境を接続するつもりでした。ところがうまくいきません。どううまくいかないかというと、リモートデバッグ環境からターゲット側への接続ができないというエラーが出てしまうのです。そもそもVirtualBoxでは無理なのかと思いましたが、Webで「Freebsd kernel remote debugging. Part1」という情報を発見しました。バージョンが書かれていませんが、成功した事例と言えるでしょう。

VirtualBoxではなくQEMUを使った事例として「FreeBSDのオンラインカーネルデバッグ with QEMU」という情報も見つけました。こちらはQEMUにしかない機能を利用しているので、同じことをVirtualBoxで実現することはできませんが、参考になります。

最終的にVirtualBoxの設定を次のようにすることでリモートデバッグ環境が出来上がりました。

  1. 使用したVirtualBoxは「バージョン 6.0.12 r133076 (Qt5.6.2)」です。
  2. FreeBSDのカーネルデバッグされる側を「ターゲット」と呼ぶことにします。
  3. GDBでデバッグコマンドを操作する側を「デバッガ」と呼ぶことにします。
  4. 「ターゲット」ではVirtualBoxでシリアルポートを利用できるように設定しておきます。
    1. 「ポートモード」を「TCP」にします。
    2. 「存在するパイプ/ソケットに接続」のチェックを外します。
    3. 「パス/アドレス」を設定します。他のアプリケーションと衝突しなければなんでも良いでしょう。僕は「35790」にしました。
  5. 「デバッガ」ではシリアルポートを使わないので、有効にする必要はありません。

上述した設定をして「ターゲット」と「デバッガ」のゲスト環境を起動します。「ターゲット」でログインプロンプトが出たら、CtrlとPrintScrキーを同時に押してカーネルデバッガに入ります。そして「gdb」コマンドを入力してリモートからの接続を待機させます。
FreeBSD/amd64 (target) (ttyv0)

login: KDB: enter: manual escape to debugger
[ thread pid 12 tid 100006 ]
Stopped at       0xffffffff80bf955b = kdb_enter+0x3b:   moveq   $0,0xffffffff81f7cbe8 = kdb_why
db> gdb
(ctrl-c will return control to ddb)
Switching to gdb back-end
一方「デバッガ」ではgdbを起動し、「ターゲット」に接続します。ここで接続先の指定はTCP/IPアドレスとポート番号を使用します。注意が必要なのは、TCP/IPアドレスは、「ターゲット」のゲスト環境のFreeBSDに割り当てられたアドレスではなく、ホスト環境であるWindows10のアドレスです。
furusawa@debugger:~ % kgdb /boot/kernel/kernel
GNU gdb (GDB) 8.3 [GDB v8.3 for FreeBSD]
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-portbld-freebsd12.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /boot/kernel/kernel...
Reading symbols from /boot/kernel/kernel.debug...
(kgdb) target remote 192.168.1.14:35790
Remote debugging using 192.168.1.14:35790
warning: remote target does not support file transfer, attempting to access files from local filesystem.
Reading symbols from /boot/kernel/intpm.ko...
warning: the debug information found in "/usr/lib/debug//boot/kernel/intpm.ko.debug" does not match "/boot/kernel/intpm.ko" (CRC mismatch).

(No debugging symbols found in /boot/kernel/intpm.ko)
Reading symbols from /boot/kernel/smbus.ko...
warning: the debug information found in "/usr/lib/debug//boot/kernel/smbus.ko.debug" does not match "/boot/kernel/smbus.ko" (CRC mismatch).

(No debugging symbols found in /boot/kernel/smbus.ko)
kdb_enter (why=0xffffffff812fae28 "break", msg=<optimized out>)
    at /usr/src/sys/kern/subr_kdb.c:479
479                     kdb_why = KDB_WHY_UNSET;
(kgdb)
このように「ターゲット」と「デバッガ」の接続が確立されました。これでリモートデバッグ環境が出来たことになります。

試しにシステムコール「sys_mkdir」にブレークポイントを仕掛けてみます。そして「ターゲット」のコマンドラインでコマンド「mkdir」を実行しようとすると、ブレークポイントで停止してくれました。成功です。
(kgdb) b sys_mkdir
Breakpoint 1 at 0xffffffff80c90614: file /usr/src/sys/kern/vfs_syscalls.c, line 3582.
(kgdb) c
Continuing.
[New Thread 100086]
[New Thread 100063]
[Switching to Thread 100086]

Thread 78 hit Breakpoint 1, sys_mkdir (td=0xfffff80003fd8580,
    uap=0xfffff80003fd8940) at /usr/src/sys/kern/vfs_syscalls.c:3582
3582            return (kern_mkdirat(td, AT_FDCWD, uap->path, UIO_USERSPACE,
(kgdb) list
3577    #endif
3578    int
3579    sys_mkdir(struct thread *td, struct mkdir_args *uap)
3580    {
3581
3582            return (kern_mkdirat(td, AT_FDCWD, uap->path, UIO_USERSPACE,
3583                uap->mode));
3584    }
3585
3586    #ifndef _SYS_SYSPROTO_H_
(kgdb) c
Continuing.
これでFreeBSDのカーネルをリモートデバッグするための環境が手に入りました。この環境を利用して『FreeBSDのブートプロセスをみる』を読んでいこうと思います。

2019/09/20

FreeBSDでカーネルデバッガを使ってみる

カーネルを勉強をしてみたいと以前から考えていましたが、どこから手をつければ良いのか、迷っていました。独習するとしても、何か伴走してくれるような参考書があると助かります。そこで手持ちの書籍から『FreeBSDのブートプロセスをみる』(白崎博生、ASCII、2006年)の辿った道程を追体験していきながら、カーネルになじんでいくことにしました。

この本はUNIX MAGAZINEの連載をまとめたものですが、もう15年ほど前に執筆されたので対象となるFreeBSDはバージョン5.1です。いろいろと変わったところもあるでしょうが、枝葉末節を気にしなければ、大筋は変わっていないかもしれません。それは別にどちらでもよくて、変化していても、変化していなくても、それを踏まえて、カーネルの世界を歩いていく練習をするのが目的です。

学ぶ環境はVirtualBox上に構築することにします。まずは連載最終回の第17回「カーネルデバッガ」の手順を追体験してみます。

カーネルデバッガを使えるようにする基本的は変わっていないようですが、指定できるオプションがFreeBSD 12では違いがありました。またカーネル構築手順も違っています。

カーネル定義ファイルは、記事同様に「UMKERNEL」としておきます(これはUnix MagazineのKernelという意味なのでしょう)。記事では既存のカーネル定義ファイル(GENERICなど)を変更して作成することが想定されているようですが、今回は次のようなファイルを新規に作成しました。これはFreeBSDハンドブックの「8.4. コンフィグレーションファイル」に書いてあることに倣ったものです。
include         GENERIC
ident           UMKERNEL

options         DDB
options         DDB_NUMSYM
options         GDB
カーネルのコンパイル方法も違っています。記事にあるのは伝統的なBSDにおけるカーネルコンパイル方法ですが、「8.5. カスタムカーネルの構築とインストール」に倣って、次のようにしました。コンパイルに1時間半ほどかかりました。
# cd /usr/src
# make buildkernel KERNCONF=UMKERNEL
# make installkernel KERNCONF=UMKERNEL
カーネルを入れ替えたら、再起動します。記事では「DDBの呼び出し方」として5種類あると書かれていますが、このあたりも今は若干違うようです。記事にも書いてある最初の手順「キーボードのCtrlとPrtScキーを同時に押す」を実行したところ、カーネルデバッガに入ることが出来ました。

ひとまず、ここまでは成功です。次はリモートデバッグに挑戦してみようと思います。すでに実験しているところです。概念は難しくないのですが、うまくいきません。記事ではVMwareを利用していますが、僕はVirtualBoxにしようと思っています。それが手強い原因とも思えませんが、なんとか解決したいと思います。

2019/09/18

佐和山城址


OSのカーネルを理解するには?

OSのカーネルを理解したいと昔から考えていました。このように考えている人は世間に多いようで、いろいろな書籍が出ていますし、Webにも情報が溢れています。しかし、まがりなりにも最後まで到達したという情報は少なく、多くは三日坊主です。

またOSの選択も多様です。最も多いのがLinuxのカーネルを素材とする場合です。その他にはBSD系やUNIX V6などもありますが、あまり活発ではありません。

もう20年以上前の1996年10月30日に当時のネットニュースfj.os.bsd.freebsdに西村享さん(奈良先端科学技術大学院大学情報科学センター) が投稿した記事があり、参考になると思っています。その記事によると、FreeBSDのカーネルを読もうとした或る人物の投稿への返信として、次のように語っています。
大学院生のソース読み輪講につきあったことがあるのですが、彼らも同じような非効率なアプローチを取りました。システムコールが呼ばれた時の制御の流れや、コンテキストスイッチとはCPUの何をどうすることかなど、超基本的な知識を咀嚼できてない状態で「生のOS」のソースコードに手を出すのは難がある(ひどく遠回りをするという意味)ように思います。

さらに『4.3BSDの設計と実装』を読もうとしていることに対して、次のように語っています。(原文ママ)
他の方も指摘されていますが、改訂版もでています。私は旧版新版どちらもお奨めしません。なぜなら、よく誤解されているようですが、入門書でないからです。以前comp.os.*なしがしだったかで、これらを評して「too dry」だとの発言を見たことがあります。私もまったくその通りだと思います。これらの本に書いてあることを理解するには、まずソースコードを読んでおくことが先決です。「UNIXカーネルの全体像を得る」 などという目的でこれらの本に取り組むとおそらく自滅するでしょう。それらは前提知識の範疇に含まれるように思います。

閑話休題。カーネルのソースは、LinuxでもBSD系(FreeBSD、NetBSD、OpenBSD)でも容易に入手できます。どれを読むか悩むところですが、個人的にはBSD系を選ぼうと思います。NetBSDの情報が多いような気もしますが、FreeBSDにしようかと考えているところです。

カーネルは大規模ですが、全体を区分すれば、ボリューム的に大部分を占めるのはデバイスドライバではないかと思います。またファイルシステム、ネットワークスタック、ブートストラップ、システムコールなどの構成要素に分割できると思うので、一つずつ取り組んでいけば(大変だとは思いますが)無謀な取り組みでもないと思っています。

TCP/IPの教科書

TCP/IPの挙動について理解したいと思い、パケットの状況をwiresharkで調査してみました。このようなツールを使えば現実の通信状況が見えるので、とても理解がすすむのですが、基礎的な理解ができていないと行き詰まってしまいます。

TCP/IPの基本について学んでおこうと思います。TCP/IPの入門書は多数出版されているものの、その多くは入門的すぎて満足できません。その中でも定評があるのが次のシリーズです。

どちらも3巻組で、第1巻がプロトコル、第2巻が実装、第3巻がアプリケーションになっています。詳しく記述されているのでページ数も多く、値段も高いです。これらのシリーズは以前から知っていたのですが、値段もネックだし、どちらを選ぶかも迷っていて、入手していませんでした。

迷っているだけでは、いつまでたっても進歩しないので、思い切って両シリーズの第1巻を買うことにしました。翻訳書だと、中古本であっても、両方合わせて1万円以上するので、さすがに躊躇します。そこで旧版の原書をabebooks経由で購入することにしました。

例えば「Internetworking with TCP/IP Volume 1 : Principles, Protocols, and Architecture」は新版もありますが、旧版なら$1でした。しかし送料は別に必要ですが、合計しても10ドル以下なので、円換算しても千円以下です。

到着まで時間がかかるのが難点です。しかし急ぐわけでもないので、ゆっくり待とうと思います。