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

时间:2012-04-24 04:06:36

标签: c fortran heap fortran77 fortran-common-block

我在C中开发一个应该使用C,C ++或Fortran代码的库。它使用的一种机制是捕获堆栈,堆或数据/ bss段中的页面的写入。在这种情况下,“堆”是库从映射文件创建的特殊堆。我发现我的库未能在Fortran应用程序中捕获对变量的写入。变量声明为

double precision u(5,I,J,K)

其中I,J和K是整数参数(即常数)。然后代码将u包含在一个名为“fields”的公共块中。

在GDB下调试时,我发现u的地址不属于三个数据段中的任何一个。 (因此库无法捕获写入!)然后我查看/ proc // maps伪文件,发现u的地址落入系统注释为“堆”的范围内。但你是怎么进入这个“堆”的?在这种情况下,Fortran 77代码不使用非标准的“allocate”关键字在堆上进行分配。有人可以向我解释Fortran 77(Ubuntu Linux x86-64下)在“堆”上分配的变量,以及首先如何创建这个“堆”?

1 个答案:

答案 0 :(得分:7)

我在公共区块玩了一个数组。它看起来像是Linux中的.bss段真的与堆合并(或者至少使用相同的brk(2)机制分配空间)。

以下是相关的Fortran代码:

double precision u(5,20,20,20)
common /a/ u

gfortran生成的GNU程序集指令是:

.comm a_,320000,32

它声明了一个名为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
 ...

这里.bss是320000(0x4e200)字节加上一些32字节的附加数据。它仅被标记为可分配,仅此而已 - 没有数据从文件中预先填充。您还可以推断,在a_ u开始之前,在{0}} VMA 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

u实际上只是Fortran主函数中局部变量的符号,而a_是全局可见存储。这就是为什么你可以在不同的子程序/函数中以不同的方式命名数组,但如果将它们放在适当的公共块中,它仍然可以访问相同的内存。

看起来.bss的尴尬VMA是ELF文件中.data段偏移的结果,因为.bss紧跟在内存中的.data段之后。在Linux中加载.data段的方式是mmap(2) - 来自带有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]

.bss在与.data映射相同的页面中启动,这是有意义的,因为它们都保存读/写数据并且可以写入,并且通过不在单独的页面上启动.bss来节省一点VM。 / p>

.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

我们知道.data段从00600000开始.bss从00600B60开始。公共块分配在0x600b80,其大小为0x4e200,因此它以0x64ed80结束,向上舍入到页边界给出0x64f000。如果没有其他动态链接库自己分配空间,那么真正的程序堆就会开始。

由于动态内存分配器malloc(3)使用相同的brk(2)机制(或匿名mmap(2)进行大型分配,或者当数据段大小的限制用尽时)它实际上没有如果数组是在.bss中,或者是用ALLOCATE()分配的话。区别在于.bss最初填充为零,而mallocALLOCATE()分配的内存内容保留原样。