📄

Goならわかるシステムプログラミング2版5章読書メモ

2023/02/23に公開

Goならわかるシステムプログラミング2版5章読書メモ

Windowsのプロテクトモード

Windows3.0までアプリケーションプロセスが他のプロセスのメモリ空間にも自由にアクセスできました。(P74)

知らなかった。面白そうなので調べてみた。Windowsの歴史が書かれている2001年の@ITの記事を見つけた。

コラム:Windowsの歴史、メモリの歴史(1)

コラム:Windowsの歴史、メモリの歴史(2)

80286は(中略)新たに組み込まれた「プロテクト モード(protect mode)」というメモリ保護メカニズムが特徴的なプロセッサである。

プロテクト モードは、プログラムのコードやデータを他のプロセスから保護するためのメカニズムであり、これによりたとえばあるプロセスが誤って他のプロセスのコードやデータ領域を破壊したり、権限のない一般ユーザーのプログラムが、特権レベルで動作するOSカーネルのデータへアクセスしたりするのを防ぐことができる。80286以前のプロセッサではこのような保護機能はいっさい用意されておらず、信頼性の高いシステムを構築するのは不可能であった。

Windows 2.0は、リアル モードのMS-DOS上において、リアル モードのコードで記述されたWindowsシステムを実行するための環境であるので(当然、アプリケーションもリアル モードで記述されている)、80286や386が備えるプロテクト モードの機能はまったく使っていない。

Windows 3.xでは、80286/386が提供するプロテクト モードに対応し、Windowsのシステム モジュールや、Windowsアプリケーションをプロテクト モード メモリ領域に割り当てるようになった。

かなり面白いw1982年にCPUにはプロテクトモードが実装されていたようだ。しかし、1987年発売のWindows2.0はリアルモード(自由にメモリを読み書きできる)で実装されていて、1990年にWindows3.0でやっとプロテクトモードに対応したみたい。

しかし、Windowsは互換性が大事なので、リアルモードで書かれたアプリケーションが動く仕組みがWindows95でも実装されていたようだ。

リアルモードとかプロテクトモードが、特権モードとユーザーモードの原型なのかな。

システムコールより内側の世界

通常の関数呼び出しでは、呼び出し側と呼ばれる側が、スタックメモリ上で隣接したメモリブロックに、それぞれのスコープに含まれるローカル変数を格納します。関数の引数も、このスタックメモリを使って渡されます。

関数呼び出しのメモリスタックの挙動はnand2tetrisで実装したことがある。IPAのページがいい感じに可視化してあって、見ながら思い出した。

https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/c006.html

SYSCALL命令は、関数呼び出しのように呼び出されますが、呼び出し側と呼ばれる側とで環境がまったく別物です。

asmlinkageフラグを付けることで、引数がすべてCPUのレジスタ経由で渡されるようになるため、スタックを使わずに情報が渡せるようになります。

引数をCPUのレジスタに格納することで、システムコールを呼ぶための情報が全てCPUに渡ったことになる。つまり、システムコールはCPUが呼ぶ。これはたぶん、プロテクトモード(権威モード)がCPUの機能だから、というのが理由だと思う。一度CPUを経由してユーザーモードから権威モードに切り替えてOSに戻ってくるというイメージなんだけど、あっているかな…。

macOSのシステムコールの変更

Go言語の1.7のリリースが予定より遅れたのは、macOSの10.12(Sierra)でシステムコールの引数が変更されたのが原因です。(P89)

調べてみたけど、どのシステムコールの引数が変更されたのか分からなかった…。go1.6とgo.17でFiles changedも3000件以上あってちょっと無理だった。ただ、1.7のリリースノートにこうあった。

Go 1.7 adds support for macOS 10.12 Sierra. Binaries built with versions of Go before 1.7 will not work correctly on Sierra.(https://go.dev/doc/go1.7#introduction)

macOS 10.12よりも前のOSでビルドしたバイナリは10.12では使えないと言っていることからも、OS側に変更があったことが推測できる…かな?

libSystem.dylib

Go1.11からはmacOSとiOSでは直接カーネルを呼ぶのではなく、libSystem.dylibというシステムライブラリを使い、将来のOSの変更の影響を受けにくいように変更されています。

libSystem.dylibはmacOSに含まれているシステムライブラリで、libcなどが含まれている。(参考: The System Library: libSystem - Mac OS X for Unix Geeks)

つまり、macOSとiOSはシステムコールを直接呼ぶのではなく、OSが提供するC言語の関数を使うようになったようだ。1.11のリリースノートにも記載があった。

On macOS and iOS, the runtime now uses libSystem.dylib instead of calling the kernel directly. This should make Go binaries more compatible with future versions of macOS and iOS.(https://go.dev/doc/go1.11#runtime)

GitHubで編集を提案

Discussion