龙空技术网

理解Go程序进程的内存空间布局

架构小白 838

前言:

现在同学们对“linux系统多大空间”大概比较关心,各位老铁们都需要了解一些“linux系统多大空间”的相关文章。那么小编在网络上网罗了一些对于“linux系统多大空间””的相关资讯,希望朋友们能喜欢,咱们快快来学习一下吧!

Linux进程内存分布简介

在Linux系统中,一个程序进程在内存布局上遵循一定规律,进程的内存空间布局由高地址到低地址大致可分为以下几段:

栈(stack): 用户态的栈,栈的大小是固定的,其大小可以使用ulimit -s查看和调整,一般默认为8Mb,栈从高地址向低地址增长(函数调用)堆(heap): 动态分配的内存空间,程序在运行时动态分配和释放,堆内存的分配不是连续的,整体上是从低地址向高地址增长bss(未初始化数据区): 未初始化数据区bss, 存放全局的未初始化赋值的变量data(初始化数据区): 存放已经初始化的全局变量数据text: 存放程序代码

ELF文件格式介绍

ELF全称“Executable and Linkable Format”,即可执行可链接文件格式,是Linux上的可执行文件就是采用的这个格式。 我们以Go程序代码在Linux下编译的可执行文件为例进行分析,实例的hello.go代码如下:

package mainimport "fmt"var (   fooVar = "hello"   barVar string)func main() {  barVar = "hellob"  fmt.Println(fooVar, barVar)}

编译hello.go源码文件得到二进制文件hello:

go build -gcflags "-N -l" hello.go

使用-gcflags "-N -l"禁用了编译优化和内联

使用readelf -e命令分析二进制文件hello这个ELF文件的组成:

readelf -e helloELF Header:  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00  Class:                             ELF64  Data:                              2's complement, little endian  Version:                           1 (current)  OS/ABI:                            UNIX - System V  ABI Version:                       0  Type:                              EXEC (Executable file)  Machine:                           Advanced Micro Devices X86-64  Version:                           0x1  Entry point address:               0x45c1a0  Start of program headers:          64 (bytes into file)  Start of section headers:          456 (bytes into file)  Flags:                             0x0  Size of this header:               64 (bytes)  Size of program headers:           56 (bytes)  Number of program headers:         7  Size of section headers:           64 (bytes)  Number of section headers:         23  Section header string table index: 3Section Headers:  [Nr] Name              Type             Address           Offset       Size              EntSize          Flags  Link  Info  Align  [ 0]                   NULL             0000000000000000  00000000       0000000000000000  0000000000000000           0     0     0  [ 1] .text             PROGBITS         0000000000401000  00001000       000000000007d39e  0000000000000000  AX       0     0     32  [ 2] .rodata           PROGBITS         000000000047f000  0007f000       0000000000034eac  0000000000000000   A       0     0     32  [ 3] .shstrtab         STRTAB           0000000000000000  000b3ec0       000000000000017a  0000000000000000           0     0     1  [ 4] .typelink         PROGBITS         00000000004b4040  000b4040       00000000000004d8  0000000000000000   A       0     0     32  [ 5] .itablink         PROGBITS         00000000004b4520  000b4520       0000000000000058  0000000000000000   A       0     0     32  [ 6] .gosymtab         PROGBITS         00000000004b4578  000b4578       0000000000000000  0000000000000000   A       0     0     1  [ 7] .gopclntab        PROGBITS         00000000004b4580  000b4580       0000000000058710  0000000000000000   A       0     0     32  [ 8] .go.buildinfo     PROGBITS         000000000050d000  0010d000       0000000000000020  0000000000000000  WA       0     0     16  [ 9] .noptrdata        PROGBITS         000000000050d020  0010d020       00000000000105c0  0000000000000000  WA       0     0     32  [10] .data             PROGBITS         000000000051d5e0  0011d5e0       0000000000007810  0000000000000000  WA       0     0     32  [11] .bss              NOBITS           0000000000524e00  00124e00       000000000002ef28  0000000000000000  WA       0     0     32  [12] .noptrbss         NOBITS           0000000000553d40  00153d40       0000000000005360  0000000000000000  WA       0     0     32  [13] .zdebug_abbrev    PROGBITS         000000000055a000  00125000       0000000000000119  0000000000000000           0     0     1  [14] .zdebug_line      PROGBITS         000000000055a119  00125119       000000000001ae04  0000000000000000           0     0     1  [15] .zdebug_frame     PROGBITS         0000000000574f1d  0013ff1d       0000000000005421  0000000000000000           0     0     1  [16] .debug_gdb_s[...] PROGBITS         000000000057a33e  0014533e       0000000000000028  0000000000000000           0     0     1  [17] .zdebug_info      PROGBITS         000000000057a366  00145366       0000000000031048  0000000000000000           0     0     1  [18] .zdebug_loc       PROGBITS         00000000005ab3ae  001763ae       000000000001906f  0000000000000000           0     0     1  [19] .zdebug_ranges    PROGBITS         00000000005c441d  0018f41d       0000000000008b8b  0000000000000000           0     0     1  [20] .note.go.buildid  NOTE             0000000000400f9c  00000f9c       0000000000000064  0000000000000000   A       0     0     4  [21] .symtab           SYMTAB           0000000000000000  00197fa8       000000000000c378  0000000000000018          22   120     8  [22] .strtab           STRTAB           0000000000000000  001a4320       000000000000b002  0000000000000000           0     0     1Key to Flags:  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),  L (link order), O (extra OS processing required), G (group), T (TLS),  C (compressed), x (unknown), o (OS specific), E (exclude),  l (large), p (processor specific)Program Headers:  Type           Offset             VirtAddr           PhysAddr                 FileSiz            MemSiz              Flags  Align  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040                 0x0000000000000188 0x0000000000000188  R      0x1000  NOTE           0x0000000000000f9c 0x0000000000400f9c 0x0000000000400f9c                 0x0000000000000064 0x0000000000000064  R      0x4  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000                 0x000000000007e39e 0x000000000007e39e  R E    0x1000  LOAD           0x000000000007f000 0x000000000047f000 0x000000000047f000                 0x000000000008dc90 0x000000000008dc90  R      0x1000  LOAD           0x000000000010d000 0x000000000050d000 0x000000000050d000                 0x0000000000017e00 0x000000000004c0a0  RW     0x1000  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000                 0x0000000000000000 0x0000000000000000  RW     0x8  LOOS+0x5041580 0x0000000000000000 0x0000000000000000 0x0000000000000000                 0x0000000000000000 0x0000000000000000         0x8 Section to Segment mapping:  Segment Sections...   00   01     .note.go.buildid   02     .text .note.go.buildid   03     .rodata .typelink .itablink .gosymtab .gopclntab   04     .go.buildinfo .noptrdata .data .bss .noptrbss   05   06

经过简化后Go二进制文件ELF格式大致如下图所示:

Go二进制文件ELF由:

EFL Header: ELF Header中包含架构、ABI版本等基础信息,需要注意Entry point address就是Go程序的入口地址Section HeadersProgram headersSections

主要有以下几个Section:

.text存放的是程序代码.rodata存放的是常量数据,例如程序中的字面量编译时会被放到这个Section中.data.noptrdata存放的是在编译时已经初始化赋值的全局变量,在Go中应该就是已经初始化的各个包级别的变量.bss.noptrbss存放的是未初始化的全局变量,在Go中应该就是未初始化的各个包级别的变量

Go是有运行时的垃圾回收语言,.data分为有指针.data section和无指针.noptrdatasection,.bss同样也分为有指针和无指针两个section。

ELF文件的Section Headers中通过Flags字段给出了各个Section的权限:

section

权限

.text

AX 可读可执行

.rodata

A 可读

.data

WA 读写

.noptrdata

WA 读写

.bss

WA 读写

.noptrbss

WA 读写

Go程序进程的内存空间布局

在执行程序时,ELF文件被加载到内存中,相同权限的Section会被对应到一个Segment中,大致是下图的内存加载结构:

下面我们使用objdump这个命令对照hello.go这个简单的程序找一下代码区和数据区的各个段内容。

查看.text:

objdump -t -j .text hellohello:     file format elf64-x86-64SYMBOL TABLE:0000000000401000 l     F .text  0000000000000000 runtime.text0000000000401d40 l     F .text  000000000000022d cmpbody0000000000401fa0 l     F .text  000000000000013e memeqbody0000000000402120 l     F .text  0000000000000117 indexbytebody0000000000457be0 l     F .text  0000000000000040 gogo0000000000457c20 l     F .text  0000000000000035 callRet0000000000457c60 l     F .text  000000000000002f gosave_systemstack_switch0000000000457ca0 l     F .text  000000000000000d setg_gcc0000000000457cc0 l     F .text  000000000000055a aeshashbody0000000000458220 l     F .text  000000000000004b debugCall320000000000458280 l     F .text  000000000000004b debugCall6400000000004582e0 l     F .text  000000000000006f debugCall1280000000000458360 l     F .text  0000000000000072 debugCall25600000000004583e0 l     F .text  0000000000000072 debugCall5120000000000458460 l     F .text  0000000000000072 debugCall102400000000004584e0 l     F .text  0000000000000072 debugCall20480000000000458560 l     F .text  0000000000000076 debugCall409600000000004585e0 l     F .text  0000000000000076 debugCall81920000000000458660 l     F .text  0000000000000076 debugCall1638400000000004586e0 l     F .text  0000000000000076 debugCall327680000000000458760 l     F .text  0000000000000076 debugCall65536000000000047e39e l     F .text  0000000000000000 runtime.etext0000000000401000 g     F .text  0000000000000059 internal/cpu.Initialize0000000000401060 g     F .text  0000000000000625 internal/cpu.processOptions00000000004016a0 g     F .text  0000000000000445 internal/cpu.doinit0000000000401b00 g     F .text  000000000000001b internal/cpu.cpuid.abi00000000000401b20 g     F .text  0000000000000011 internal/cpu.xgetbv.abi00000000000401b40 g     F .text  0000000000000087 type..eq.internal/cpu.option0000000000401be0 g     F .text  0000000000000094 type..eq.[15]internal/cpu.option0000000000401c80 g     F .text  000000000000006f runtime/internal/sys.OnesCount640000000000401d00 g     F .text  0000000000000022 internal/bytealg.init.00000000000401f80 g     F .text  000000000000000e runtime.cmpstring00000000004020e0 g     F .text  000000000000001b runtime.memequal0000000000402100 g     F .text  000000000000001c runtime.memequal_varlen0000000000402240 g     F .text  0000000000000018 internal/bytealg.IndexByteString.abi00000000000402260 g     F .text  0000000000000043 type..eq.internal/abi.RegArgs00000000004022c0 g     F .text  0000000000000043 runtime.memhash1280000000000402320 g     F .text  000000000000004a runtime.strhashFallback0000000000402380 g     F .text  00000000000000e5 runtime.f32hash......

查看.rodata,根据hello.go中的字符串字面量"theFoolValue"和"theBarVarValue"应该是在.rodata,下面搜索一下:

objdump -s -j .rodata hello | grep hellohello:     file format elf64-x86-64 494900 616e6863 68616e68 656c6c6f 696e6974  anhchanhelloinit 494aa0 656e6365 6572726e 6f206865 6c6c6f62  enceerrno hellob

包变量fooVar已经初始化位于.data,包变量barVar未初始化位于.bss

objdump -t hello | grep main.fooVar000000000051dda0 g     O .data	0000000000000010 main.fooVarobjdump -t hello | grep main.barVar00000000005250f0 g     O .bss	0000000000000010 main.barVar
参考

标签: #linux系统多大空间