Open2

「Linuxのしくみ」

masafumi330masafumi330

仮装記憶とは

仮想記憶がない場合の課題

  • 空きメモリ空間がバラバラに分散している場合、プロセスはまとまったメモリ確保ができなくなる。
  • メモリの直接アクセスが可能なために、意図しないデータの破壊や漏洩を引き起こす。

仮想記憶の構成要素

  • 仮想メモリ空間

  • 物理メモリ空間

  • ページテーブル

  • プロセスは仮想メモリ空間にアクセスする。この時、CPUがページテーブルを参照することで、仮想メモリ空間から実際の物理メモリ空間へ変換を行い、プロセスは間接的に物理メモリ空間にアクセスする。

  • ページテーブルはカーネルによって作られる。ページテーブルはカーネルのメモリ内に保持される。

  • 仮想メモリ空間はプロセスごとに作られる。これによって、物理メモリ空間としてはバラバラでも、仮想メモリ空間上ではまとまったメモリ空間を確保することができる。

仮想記憶による課題の解決

  • メモリの断片化の防止
    • 物理メモリ空間としてはバラバラでも、プロセスの仮想メモリ空間としてはまとまった領域を確保できる。
  • 不正なメモリへのアクセス
    • プロセスからは、物理メモリ空間がわからないので、直接メモリアクセスのしようがない。
masafumi330masafumi330

ページフォールトとは

  • 仮想メモリアドレスに対応する物理メモリアドレスが存在しない状態で、プロセスがその仮想メモリアドレスにアクセスした時、対応する物理メモリアドレスが存在しないので、メモリアクセスに失敗すること。
  • CPUが例外「ページフォールト」を返し、カーネルがその例外をページフォールトハンドラによって受け取る。その後、プロセスにシグナル SIGSEGV を送信する。シグナル SIGSEGV を受け取ったプロセスは通常、強制終了する。

ページフォールトが発生する様子

package main

import (
	"fmt"
)

func main() {
	// nil は必ずアクセスに失敗してページフォールトが発生する特殊なメモリアクセス
	var p *int = nil
	fmt.Println("不正メモリアクセス前")
	*p = 0
	fmt.Println("不正メモリアクセス後")
}

実行すると、

$ go build main.go
$ ./main
不正メモリアクセス前
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x100a12e48]

goroutine 1 [running]:
main.main()
        /Users/m.masafumi/work/sandbox/go-test-with-db-ci/app/cmd/template/main.go:11 +0x58

シグナル SIGSEGV が送られているのがエラーメッセージから分かる。
原因は、メモリ確保を行なっていないから = 物理メモリに対応する仮想メモリが確保されていない。

修正後

package main

import (
	"fmt"
)

func main() {
	// nil は必ずアクセスに失敗してページフォールトが発生する特殊なメモリアクセス
	// var p *int = nil
	var p *int = new(int) // 物理メモリを確保
	fmt.Println("不正メモリアクセス前")
	*p = 0
	fmt.Println("不正メモリアクセス後")
}
$ ./main
不正メモリアクセス前
不正メモリアクセス後

Go言語のメモリ確保 new()make() については、 Effective Go に書かれている。
https://go.dev/doc/effective_go#allocation_new