Ami*_*ram 2 c heap fortran fortran77 fortran-common-block
我正在用C开发一个应该使用C,C++或Fortran代码的库.它使用的一种机制是捕获堆栈,堆或数据/ bss段中的页面的写入.在这种情况下,"堆"是库从映射文件创建的特殊堆.我发现我的库未能在Fortran应用程序中捕获对变量的写入.变量声明为
double precision u(5,I,J,K)
Run Code Online (Sandbox Code Playgroud)
其中I,J和K是整数参数(即常数).然后代码将u包含在称为"字段"的公共块中.
在GDB下调试时,我发现u的地址不属于三个数据段中的任何一个.(因此库无法捕获写入!)然后我查看/ proc // maps伪文件,发现u的地址落入系统注释为"堆"的范围内.但你是怎么进入这个"堆"的?在这种情况下,Fortran 77代码不使用非标准的"allocate"关键字在堆上进行分配.有人可以向我解释Fortran 77(在Ubuntu Linux x86-64下)在"堆"上分配的变量,以及首先如何创建这个"堆"?
我在公共区块玩了一个数组.看起来是Linux中的.bss段真正与堆合并(或者至少使用相同的brk(2)机制分配空间).
这是相关的Fortran代码:
double precision u(5,20,20,20)
common /a/ u
Run Code Online (Sandbox Code Playgroud)
生成的GNU程序集指令gfortran是:
.comm a_,320000,32
Run Code Online (Sandbox Code Playgroud)
这声明了一个名为a_320000字节的公共符号,应该在32字节边界上对齐.当链接器看到这个声明时,没有其它的定义a_可以在.bss中为它保留空间,因为在objdump生成的二进制文件上运行时可以清楚地看到它:
Sections:
Idx Name Size VMA LMA File off Algn
...
22 .data 00000010 0000000000600b40 0000000000600b40 00000b40 2**3
CONTENTS, ALLOC, LOAD, DATA
23 .bss 0004e220 0000000000600b60 0000000000600b60 00000b50 2**5
ALLOC
...
Run Code Online (Sandbox Code Playgroud)
这里.bss是320000(0x4e200)字节加上一些32字节的附加数据.它仅被标记为可分配,仅此而已 - 没有数据从文件中预先填充.您还可以推断,a_自从uVMA 0x600b80开始以来,已经放置了32个字节的附加数据:
(gdb) info address u
Symbol "u" is static storage at address 0x600b80.
(gdb) info symbol &u
a_ in section .bss of /path/to/a.out
Run Code Online (Sandbox Code Playgroud)
u实际上它只是Fortran主函数中局部变量的符号,同时a_也是全局可见的存储.这就是为什么你可以在不同的子程序/函数中以不同的方式命名数组,但如果将它们放在适当的公共块中,仍然可以访问相同的内存.
看起来.bss的尴尬VMA是ELF文件中.data段偏移的结果,因为.bss紧跟在内存中的.data段之后.在Linux中加载.data段的方式是mmap(2)从文件中获取-ed,MAP_PRIVATE从而为映射提供写时复制语义:
00400000-00401000 r-xp 00000000 00:1d 25681168 /path/to/a.out
00600000-00601000 rw-p 00000000 00:1d 25681168 /path/to/a.out <-- .data
00601000-00670000 rw-p 00000000 00:00 0 [heap]
Run Code Online (Sandbox Code Playgroud)
.bss在与.data映射相同的页面中启动,这是有意义的,因为它们都保存读/写数据并且期望被写入,并且通过不在单独的页面上启动.bss来节省一点VM.
.data段之后的所有内容都不会被文件映射备份,因此会落入可见的动态可调空间[heap]中/proc/pid/maps.这个空间以及堆本身是通过移动数据段,所谓的端部控制的程序中断,与brk(2).内核中的ELF加载器最初将程序中断移动到足以为.bss保留空间,如strace可执行文件所示:
execve("./a.out", ["a.out"], [/* 230 vars */]) = 0
brk(0) = 0x64f000 <-- already moved past the .bss
Run Code Online (Sandbox Code Playgroud)
我们知道.data段从00600000开始.bss从00600B60开始.公共块分配在0x600b80,其大小为0x4e200,因此它以0x64ed80结束,向上舍入到页边界给出0x64f000.如果没有其他动态链接库自己分配空间,那么真正的程序堆就会开始.
由于动态内存分配器malloc(3)使用相同的brk(2)机制(或者mmap(2)对于大型分配是匿名的,或者当数据段大小的限制耗尽时),如果数组是在.bss中或者是在分配的情况下,则无关紧要ALLOCATE().区别在于.bss最初用零填充,而由malloc或分配的内存的内容保持不变ALLOCATE().