$30 off During Our Annual Pro Sale. View Details »

Goならわかる Linuxのメモリ管理

Goならわかる Linuxのメモリ管理

Jumpei Takiyasu

August 22, 2018
Tweet

More Decks by Jumpei Takiyasu

Other Decks in Technology

Transcript

  1. Goならわかる
    Linuxのメモリ管理
    Jumpei Takiyasu @juntaki
    M3, Inc.

    View Slide

  2. = &Me{
    Name: "Jumpei Takiyasu",
    Company: "M3, Inc.",
    Web: "https://juntaki.com",
    }
    About me

    View Slide

  3. x86_64のLinuxで実行すると、何が表示される?
    実行環境: go 1.10 / Linux version 4.18.0-rc8 / Intel(R) Core(TM) i7-8650U、デバッガはmain.mainでbreak
    1. hoge
    2. 0x0 - 0x528000 のどこか
    3. 0x528000 - 0x547000 [heap] のどこか
    4. 0x7ffffffde000 - 0x7ffffffff000 [stack] のどこか
    5. 2,3,4以外の領域のどこか
    6. それ以外
    func main() {
    a := "hoge"
    fmt.Println(&a)
    }
    (gdb) i proc mappings
    process 26627
    Mapped address spaces:
    Start Addr End Addr Size Offset objfile
    0x400000 0x483000 0x83000 0x0 /home/juntaki/memory/quiz/main
    0x483000 0x514000 0x91000 0x83000 /home/juntaki/memory/quiz/main
    0x514000 0x528000 0x14000 0x114000 /home/juntaki/memory/quiz/main
    0x528000 0x547000 0x1f000 0x0 [heap]
    0xc000000000 0xc000001000 0x1000 0x0
    0xc41fff8000 0xc420100000 0x108000 0x0
    0x7ffff7f5a000 0x7ffff7ffa000 0xa0000 0x0
    0x7ffff7ffa000 0x7ffff7ffd000 0x3000 0x0 [vvar]
    0x7ffff7ffd000 0x7ffff7fff000 0x2000 0x0 [vdso]
    0x7ffffffde000 0x7ffffffff000 0x21000 0x0 [stack]
    0xffffffffff600000 0xffffffffff601000 0x1000 0x0 [vsyscall]

    View Slide

  4. なあハム太郎!!!!お前もそう思うだろ!!???!!そうなのだ!!!!!まったくもってその通りなのだ!!!!!!!
    プログラムとは
    メモリ操作とIO

    View Slide

  5. ユーザ空間での
    メモリ割り当て

    View Slide

  6. エスケープ解析でヒープorスタックが決まる
    ヒープになっちゃうのはfmt.Println()にポインタを渡してるためです
    $ go build -gcflags '-m -N -l' alloc.go
    # command-line-arguments
    ./alloc.go:9:13: a escapes to heap
    ./alloc.go:8:10: new(int) escapes to heap
    ./alloc.go:9:13: main ... argument does not escape
    ./alloc.go:11:10: main new(int) does not escape
    func main() {
    a := new(int) // ヒープに取れる
    fmt.Println(a)
    b := new(int) // スタックに取れる
    *b = 0xdeadbeef
    }

    View Slide

  7. ふつうのプロセスにおけるヒープとスタック
    64bitの場合、0x00007fffffffffffがユーザスペースの上限。Intelの仕様のためこれ以上とっても意味ない
    ヒープ
    アロケーションが遅い
    GCが必要(free)
    スタック
    アロケーションがはやい
    関数のreturnと同時に消滅
    Program text
    Stack
    Heap
    0x00007fffffffffff
    0x0000000000000000

    View Slide

  8. 呼び出し規約とスタックの仕組み
    やきにく たべたい
    package main
    func main() {
    yakiniku()
    }
    //go:noinline
    func yakiniku() {
    tabetai(0xdeadbeef)
    }
    //go:noinline
    func tabetai(a int) {
    a++
    }

    View Slide

  9. 関数を呼び出すだけの関数をみる
    要するに$spが指していて書き込み可能なら別に決まった場所じゃなくてもよい
    mov %fs:0xfffffffffffffff8,%rcx
    cmp 0x10(%rcx),%rsp
    jbe 44c1b5
    sub $0x10,%rsp
    mov %rbp,0x8(%rsp)
    lea 0x8(%rsp),%rbp
    mov $0xdeadbeef,%eax
    mov %rax,(%rsp)
    callq 44c1c0
    mov 0x8(%rsp),%rbp
    add $0x10,%rsp
    retq
    callq 444570
    jmp 44c180
    スタックが足りなくなる場合は
    runtime.morestackで拡張
    スタックに%rbpと、引数を積む
    callで、戻り番地が積まれる
    スタックを元に戻す
    caller’s rbp
    引数
    rbp
    戻り番地
    rsp

    View Slide

  10. goroutineのスタックはヒープから取る
    main関数もgoroutineです。ユーザ空間でスケジューリングするので、スイッチングのコストが低い
    関数呼び出し時に足りない場合は、コピーして拡張する
    [Five things that make Go fast | Dave Cheney](https://dave.cheney.net/2014/06/07/five-things-that-make-go-fast)

    View Slide

  11. ヒープはTCMallocで割り当てる
    この辺の仕掛けはglibc mallocも似たような感じだが、main arena相当もなく、コードはTCMallocのほうが簡潔
    取得/解放コストを下げたいが、メモリは使いたくない
    ● OSからのメモリ確保はプールして回避
    ● ロック競合と探索を避けて再利用する
    [Post-Mortem Heap Analysis: TCMalloc | Backtrace.io](https://backtrace.io/blog/backtrace/memory-allocator-tcmalloc/)

    View Slide

  12. カーネルからの
    メモリ取得

    View Slide

  13. mmap
    「Goならわかる」なのでカーネルはさっぱりと
    vmaを追加

    View Slide

  14. メモリアドレッシング
    メモリは本当に必要になるまで、割り当ててもらえないです
    ページフォルトのハンドラで割り当てる
    リニアアドレス 物理アドレス
    MMU
    ページ
    ページ
    page fault

    View Slide

  15. ページフォルト時の動作
    Buddyアロケータから、ページ(4KB)単位でメモリをとれる
    要求されたアドレスが、vmaの範囲内か(など)をチェック
    cr3レジスタから連なるマッピングテーブルを作る

    View Slide

  16. まとめ
    自分が何を作ったのか、
    もうちょっと知っておきませんか

    View Slide

  17. (社内向け)Goならわかるシステムプログラミング輪読会
    やろう
    第1回は8/27(月)
    第1章 Go言語で覗くシステムプログラミングの世界
    第2章 低レベルアクセスへの入口 1:io.Writer
    参加者募集!!!
    https://meetup.sugooi.net/meetup/bdt0rjlhqd8g00ja7v6g

    View Slide

  18. 参考資料
    malloc動画はオススメ
    malloc動画
    [The 67th Yokohama kernel reading party - YouTube]
    (https://www.youtube.com/watch?v=0-vWT-t0UHg)
    Goのメモリ管理(特にスタックのはなし)
    [Five things that make Go fast | Dave Cheney]
    (https://dave.cheney.net/2014/06/07/five-things-that-make-go-fast)
    [Why is a Goroutine’s stack infinite ? | Dave Cheney]
    (https://dave.cheney.net/2013/06/02/why-is-a-goroutines-stack-infinite)
    [Contiguous stacks]
    (https://docs.google.com/document/d/1wAaf1rYoM4S4gtnPh0zOlGzWtrZFQ5suE8qr2sD8uWQ/pub)
    main.mainより前には何が起きるか
    [Golang Internals, Part 6: Bootstrapping and Memory Allocator Initialization | Altoros]
    (https://blog.altoros.com/golang-internals-part-6-bootstrapping-and-memory-allocator-initialization.html)

    View Slide