c - Fortran 77 如何分配公共(public) block 变量?

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

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

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

其中 I、J 和 K 是整数参数(即常量)。然后,代码将 u 包含在称为“字段”的公共(public) block 中。

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

最佳答案

我在公共(public) block 中使用了一个数组。看起来 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 字节的附加数据。它仅被标记为可分配,仅此而已 - 没有从文件中预填充任何数据。您还可以推断出 32 字节的附加数据位于 a_ 之前,因为 u 从 VMA 0x600b80 开始:

(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_ 是全局可见的存储。这就是为什么您可以在不同的子例程/函数中对数组进行不同的命名,但如果将它们放在适当的公共(public) block 中,仍然可以访问相同的内存。

看起来 .bss 的尴尬 VMA 是 ELF 文件中 .data 段偏移的结果,因为 .bss 紧邻内存中 .data 段之后。 Linux 中加载 .data 段的方式是使用 MAP_PRIVATE 从文件中进行 mmap(2) 编辑,这为映射提供了写时复制语义:

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。

.data 段之后的所有内容都没有由文件映射备份,因此属于可动态调整的空间,该空间在 /proc/pid/maps< 中以 [heap] 形式可见。该空间以及堆本身是通过使用 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开始。公共(public) block 分配在 0x600b80,其大小为 0x4e200,因此它以 0x64ed80 结束,向上舍入到页边界给出 0x64f000。如果没有其他动态链接库自行分配空间,则真正的程序堆将从此处开始。

由于动态内存分配器 malloc(3) 使用相同的 brk(2) 机制(或者对于大型内存分配器使用匿名 mmap(2)分配或当数据段大小的限制耗尽时)数组是否位于 .bss 中或使用 ALLOCATE() 分配实际上并不重要。不同之处在于 .bss 最初用零填充,而由 mallocALLOCATE() 分配的内存内容保持原样。

关于c - Fortran 77 如何分配公共(public) block 变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10291609/

相关文章:

arrays - Fortran 错误 # 6366 : The shapes of the array expressions do not conform

fortran - 错误: Character argument 'a' at (1) must be length 1 because procedure is BIND(C)

C++ 处理堆上的对象

java - 堆空间不足

C 字符串长度 - 这是有效代码吗?

c - Linux 中的 DOS "pause"?

c - 在c中填充斜边表

recursion - 如何防止递归 Fortran 子例程中定义的变量在内部调用时更新?

设置环境变量后 Elasticsearch 报告默认堆内存大小

c - 奇怪的 gcc 错误杂散/缺少终止“C 中的字符