从类似问题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
确认:
dec中{p>$ cat /proc/633/maps 4bc38b9000-4bc38bc000 r-xp 00000000 fe:01 19664256
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
而非608
,maps
也说同样的话:
... 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?
答案 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
。
因此,您的代码中没有任何内容可以显示在该区域中。