C - 位于进程内存中的常量?

时间:2017-09-02 14:25:49

标签: c linux memory-management

从类似问题here我看到常量变量必须位于程序的进程内存文本段中,如果我&# 39;理解一切正确 - 它的确是:

int main() {

    static const char somedata[8192] = "somedata";

    while (1) {
        printf("\tAddress of main: %p\n", main);
        printf("\tMy process ID : %d\n", getpid());
        printf("\tArray Some first address: %p\n", &somedata[1]);
        sleep(10);
    };

    return 0;
}

这给了我结果:

Address of main: 0x4bc38b971a
My process ID : 633
Array Some first address: 0x4bc38b9881

运行后 - /proc/maps确认:

$ cat /proc/633/maps 
4bc38b9000-4bc38bc000 r-xp 00000000 fe:01 19664256
dec中{p> 0x4bc38b9881 325403252865 0x4bc38b9000 - 0x4bc38bc000 325403250688-325403262976 325403252865 位于这些边界之间,一切看起来都是正确的。

size也告诉它在text

$ size mem_lay_inc_text_print
   text    data     bss     dec     hex filename
  10097     608       8   10713    29d9 mem_lay_inc_text_print

但是this(以及很多类似的)主题说 - 常量位于初始化数据段而不是代码段

  

初始化数据存储所有全局,静态,常量

那么 - 真相在哪里?或者我只是误解了什么?

也许 4bc38b9000-4bc38bc000 包含 Init。数据文字细分?

不,它没有:

...
static int i = 100;

while (1) {
    printf("Address of main: %p\n", main);
    printf("My process ID : %d\n", getpid());
    printf("Array Some first address: %p\n", &somedata[1]);
    printf("Int I address: %p\n", &i);
...

现在从size的结果我看到data从第一个结果变得更大(data 612而非608maps也说同样的话:

...
Int I address: 0xea335af040
...

maps

$ cat /proc/8859/maps
ea333ac000-ea333af000 r-xp 00000000 fe:01 19664256
ea335ae000-ea335af000 r--p 00002000 fe:01 19664256
ea335af000-ea335b0000 rw-p 00003000 fe:01 19664256

0xea335af040 位于带有rw-p ea335af000-ea335b0000 中,data ...

这里真的很困惑......

$ gcc --version
gcc (GCC) 7.1.1 20170630

所以问题是:常量存储在哪里 - 初始化数据或文本段?或者它取决于编译器/ OS?

1 个答案:

答案 0 :(得分:0)

由于空间和格式限制,我更喜欢在此处编写扩展注释而不是注释。

首先,https://developerinsider.co/memory-layout-representation-of-c-program/处的内存布局链接没有错;它没有提到它正在尝试对所有操作系统进行全面的广泛讨论。通用的四个布局概念是通用的:1)代码,2)在构建时分配的数据(这变得混乱),以及两种风格的动态数据:3)堆栈和4)堆。如图所示。它忽略了动态链接环境所需的许多实际机制,例如全局偏移表(.got)。

我认为OP的一个重要区别是可执行文件布局与正在运行的进程内存布局。可执行文件布局(如ELF)是磁盘上的潜在位,包含进程的蓝图。这是谈论所有特定部分以及适用objdump工具的地方。 objdump -h foo实际上会显示30个部分。

Linux内核中的可执行加载程序将这些部分映射到几个内存区域,以最大限度地减少管理内存的复杂性和开销。

下一个重要的区别在于普遍的Unix行为与Linux的细节之间的区别,至少在概念上,廉价硬件上的性能优于优雅和坚持清洁分离。因此在引用的链接中:

  

因此,.rodata部分包含只读   初始化数据被打包到包含该数据的同一段中   .text section。

这很不幸,因为只读初始化数据附加了完全不必要的执行权限。几乎可以肯定,Linux的强化变体不会以微小的内存或CPU浪费为代价。

这是我的代码(我注意到你正在使用基于1的数组 - 哎呀,这不是C的工作方式!) - 它试图显示只读与读取 - 写映射:

#include <stdio.h> /* printf */
#include <sys/types.h> /* getpid */
#include <unistd.h> /* getpid */
#include <sys/select.h>

const char small_ro_global[  16] = "small ro global";
char       small_rw_global[  16] = "small ro global";
const char   big_ro_global[8192] = "  big ro global";
char         big_rw_global[8192] = "  big rw global";

int main(int argc, char *argv[]) {
  /* small_ro_global[1] = 'b'; compile error */
  small_rw_global[1] = 'c'; /* ensure writable */

  printf("Address of main: %p\n", main);
  printf("My process ID : %d\n", getpid());

  printf("small_ro_global: %p big_ro_global: %p small_rw_global: %p big_rw_global: %p\n",
         small_ro_global, big_ro_global, small_rw_global, big_rw_global);

  /* waits forever */
  select(0, NULL, NULL, NULL, NULL);

  return 0;
}

GCC:v.4.8.4 GNU / Linux发行版:Ubuntu 15.10硬件:Intel 64位 输出:

Address of main: 0x4005cd
My process ID : 5788
small_ro_global: 0x400700 big_ro_global: 0x400720 small_rw_global: 0x603060 big_rw_global: 0x603080

/proc/5788/maps摘录。请注意第1部分和第3部分的内容,但第2部分没有任何内容:

00400000-00403000 r-xp 00000000 ca:01 1947                               /root/foo
00602000-00603000 r--p 00002000 ca:01 1947                               /root/foo
00603000-00606000 rw-p 00003000 ca:01 1947                               /root/foo

最后,感谢刚刚了解到readelf -l的链接,其中给出了以下内容。特别是看到包含.text和.rodata部分的段02。

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp 
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 
   03     .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss 
   04     .dynamic 
   05     .note.ABI-tag .note.gnu.build-id 
   06     .eh_frame_hdr 
   07     
   08     .init_array .fini_array .jcr .dynamic .got 

proc(5)上的文档中我们了解到内存段中的第二个 (权限:只读,不可执行),并将其与objdump相关联,用于过程机制:动态链接和异常处理。它包含以下部分:.eh_frame_hdr.eh_frame.init_array.fini_array.jcr.dynamic.got

因此,您的代码中没有任何内容可以显示在该区域中。