Fortran 77如何分配公共块变量?

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下)在"堆"上分配的变量,以及首先如何创建这个"堆"?

Hri*_*iev 7

我在公共区块玩了一个数组.看起来是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().