Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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

De059f612e2f68589831e4bde0f15c83?s=128

Jumpei Takiyasu

August 22, 2018
Tweet

Transcript

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

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

    } About me
  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]
  4. なあハム太郎!!!!お前もそう思うだろ!!???!!そうなのだ!!!!!まったくもってその通りなのだ!!!!!!! プログラムとは メモリ操作とIO

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

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

    Stack Heap 0x00007fffffffffff 0x0000000000000000
  8. 呼び出し規約とスタックの仕組み やきにく たべたい package main func main() { yakiniku() }

    //go:noinline func yakiniku() { tabetai(0xdeadbeef) } //go:noinline func tabetai(a int) { a++ }
  9. 関数を呼び出すだけの関数をみる 要するに$spが指していて書き込み可能なら別に決まった場所じゃなくてもよい mov %fs:0xfffffffffffffff8,%rcx cmp 0x10(%rcx),%rsp jbe 44c1b5 <main.yakiniku+0x35> sub

    $0x10,%rsp mov %rbp,0x8(%rsp) lea 0x8(%rsp),%rbp mov $0xdeadbeef,%eax mov %rax,(%rsp) callq 44c1c0 <main.tabetai> mov 0x8(%rsp),%rbp add $0x10,%rsp retq callq 444570 <runtime.morestack_noctxt> jmp 44c180 <main.yakiniku> スタックが足りなくなる場合は runtime.morestackで拡張 スタックに%rbpと、引数を積む callで、戻り番地が積まれる スタックを元に戻す caller’s rbp 引数 rbp 戻り番地 rsp
  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)
  11. ヒープはTCMallocで割り当てる この辺の仕掛けはglibc mallocも似たような感じだが、main arena相当もなく、コードはTCMallocのほうが簡潔 取得/解放コストを下げたいが、メモリは使いたくない • OSからのメモリ確保はプールして回避 • ロック競合と探索を避けて再利用する [Post-Mortem

    Heap Analysis: TCMalloc | Backtrace.io](https://backtrace.io/blog/backtrace/memory-allocator-tcmalloc/)
  12. カーネルからの メモリ取得

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

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

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

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

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

  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)