gpt4 book ai didi

C - 进程内存中的常量在哪里?

转载 作者:太空狗 更新时间:2023-10-29 12:40:33 24 4
gpt4 key购买 nike

来自类似问题here我看到 constant 变量必须在程序的进程内存 Text 段 中,如果我理解的一切正确 - 它确实是:

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

0x4bc38b9881 在 dec 中是 3254032528650x4bc38b9000 - 0x4bc38bc000325403250688-325403262976325403252865 在这些边界之间,一切看起来都是正确的。

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 (以及很多类似的)主题说 - constantInitialized Data Segment 而不是在 Code segment 中:

Initialized data stores all global, static, constant,

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

也许 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 而不是 608第一个结果),maps 也表示相同:

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

map :

$ 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

0xea335af040ea335af000-ea335b0000 中带有 rw-p,也就是 data...

真的很困惑...

$ gcc --version
gcc (GCC) 7.1.1 20170630

所以问题是:常量存储在哪里 - 在初始化数据中,还是在文本段中?还是取决于编译器/操作系统?

最佳答案

由于空间和格式的限制,我更喜欢在这里写一个扩展评论而不是评论。

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

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

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

下一个重要的区别是通用的 Unix 行为与 Linux 的细节之间的区别,至少在概念上,在廉​​价硬件上的性能比优雅和坚持干净的分离更重要。因此在引用的链接中:

For that reason, the .rodata section, which contains read-only initialized data, is packed into the same segment that contains the .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

因此,您在代码中指向的任何内容都不会在该区域显示。

关于C - 进程内存中的常量在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46014769/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com