この記事の内容
システムコールとは
システムコールとは、特権モードで OS の機能を呼ぶことです。 アプリケーションでは通常、ユーザーモードであり CPU の利用が制限されています。 そのため、システムコールによって、特権モードでのみ許可されている機能をユーザーモードのアプリケーションから利用できるようにしています。 メモリの割当やファイル入出力、通信などは特権モードでしか行えません。
システムコールがないとどうなる
計算を行う命令はユーザーモードでも可能です。
しかし
- 特権モードでないとプロセス間通信ができないので、計算結果を画面に出力できない
- 特権モードでないとファイル入出力ができないので、計算結果をファイル出力することができない
- 特権モードでないと共有メモリを作成できないので、計算結果を他のプログラムから参照することができない
- 特権モードでないと外部通信できないので、計算結果を外部ウェブサービスなどに送信できない
システムコールがなければなにもできません。
また、システムコールには抽象化の側面もあります。 OS が提供するシステムコールによって、プログラマはハードウェアを直接制御するプログラムを記述する必要がありません。 さらに、高水準言語によってハードウェアの種類の違いを意識する必要もなくなります。
また、POSIX(Portable Operating System Interface)は IEEE 規格であり、OS 間でシステムコールを呼び出すための共通のインターフェースを決めています。
Go とシステムコール
試しに、ハローワールドの実装を Goland で追いました。 VS Code のデバッガモードでステップインをすることで追えます。 以下より、標準出力するだけでも確かにシステムコールが呼ばれていることが分かります。
// go version go1.14.4 darwin/amd64 fmt.Println("Hello World!") // ↓call n, err = w.Write(p.buf) // print.go // ↓call n, e := f.write(b) // file.go // ↓call n, err = f.pfd.Write(b) // file_unix.go // ↓call n, err := syscall.Write(fd.Sysfd, p[nn:max]) // fd_unix.go // ↓call n, err = write(fd, p) // syscall_unix.go // ↓call r0, _, e1 := syscall(funcPC(libc_write_trampoline), uintptr(fd), uintptr(_p0), uintptr(len(p))) // zsyscall_darwin_amd64.go
ここで libc を使われています。 C 言語のライブラリで printf とか fopen とかが入っています。 libc を介してシステムコールが呼ばれています。
$ otool -L main main: /usr/lib/libSystem.B.dylib (compatibility version 0.0.0, current version 0.0.0)
コンパイルしたバイナリを調べると、/usr/lib/libSystem.B.dylib
が使われています。(Mac)
これがMacでのlibcのようです。
DockerでLinux環境を作り同様に検証してみました。
$ ldd main /lib/ld-musl-x86_64.so.1: main: Not a valid dynamic program
静的リンクなバイナリになりました。 MacとLinuxで異なった結果になりました。 今回は、ハローワールドな簡単なバイナリで調べてみましたが複雑なコードだとまた違った結果がでるかもしれません。