C-其中常数位于过程中';什么是记忆?
从类似的问题中,我看到常量变量必须位于程序的进程内存文本段,如果我理解的一切都是正确的,那么它实际上是:C-其中常数位于过程中';什么是记忆?,c,linux,memory-management,C,Linux,Memory Management,从类似的问题中,我看到常量变量必须位于程序的进程内存文本段,如果我理解的一切都是正确的,那么它实际上是: 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
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;
}
这给了我一个结果:
运行后-/proc/maps
确认:
0x4bc38b9881
在十二月是325403252865,0x4bc38b9000-0x4bc38bc000
是325403250688-325403262976,而325403252865在这些边界之间,看起来都是正确的
大小
也表明它在文本中
:
但是(以及许多类似的)主题说-常量在初始化数据段中,而不是在代码段上:
初始化数据存储所有全局、静态、常量
那么,真相在哪里?或者我只是误会了什么
可能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
),map
也说了同样的话:
和地图
:
0xea335af040位于ea335af000-ea335b0000中,带有rw-p
,即数据
这里真的很困惑
所以问题是:常量存储在哪里——在初始化数据中,还是在文本段中?或者它取决于编译器/OS?由于空间和格式的限制,我更喜欢在这里写扩展注释,而不是注释 首先,at中的内存布局链接没有错误;它只是没有提到它正试图对所有操作系统进行全面的讨论。四个通用的布局概念是:1)代码,2)构建时分配的数据(这会变得混乱),以及两种类型的动态数据:3)堆栈和4)堆。如图所示。它忽略了动态链接环境所需的许多实际机制,例如全局偏移表(.got) 我认为OP的一个重要区别是可执行文件布局与运行进程内存布局之间的模糊性。可执行文件布局(如ELF)是磁盘上包含进程蓝图的潜在位。这就是讨论所有特定部分以及应用
objdump
工具的地方objdump-hfoo实际上将显示30个部分
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
Linux内核中的可执行加载程序将这些多个部分映射到几个内存区域,以最小化管理内存的复杂性和开销
下一个重要的区别是通用Unix行为与Linux的具体特性之间的区别,至少在概念上,Linux在廉价硬件上的性能比优雅和坚持干净的分离要高。因此,在参考链接中:
因此,.rodata部分包含只读
初始化的数据打包到包含
.文本部分
这是不幸的,因为只读初始化数据附加了完全不必要的执行权限。几乎可以肯定,Linux的强化变体不会以牺牲少量内存或CPU浪费为代价来实现这一点
下面是我的代码(我注意到您使用的是基于1的数组——oops,这不是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;
}
/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\u-frame\u-hdr
,.eh\u-frame
,.init\u-array
,.fini\u-array
,.jcr
,.dynamic
和.got
因此,您可以在代码中指出的任何内容都不会在该区域显示。实际问题是什么?常量将位于链接器放置它们的位置。@tilz0R常量存储在初始化数据或文本段中的位置?基本上取决于编译器。我不再太注意这些东西了--这是编译器的工作,为什么要担心呢?--但是我相信现在在文本段中放置const
字符串(可能还包括字符串文本)和在初始化数据段中放置可写字符串是很常见的。这是两种不同的东西的混合。常量数据位于节.rodata
(只读数据)中,与节.text
不同。.text
和.rodata
节都打包到同一段中@setevoy:大多数优秀的作者都希望被告知他们所犯的任何错误,这样他们就可以在下一版中更正错误。如果该网站有电子邮件联系人/地址,也许你可以给他们发送一封友好、礼貌但坚定的电子邮件,告诉作者这个问题?当然,包括页面URL。(我还将包括我的证明,即上面的程序,因此作者可以轻松地验证错误。)顺便说一下,同样的方法也适用于软件项目。:)
$ 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
$ gcc --version
gcc (GCC) 7.1.1 20170630
#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;
}
Address of main: 0x4005cd
My process ID : 5788
small_ro_global: 0x400700 big_ro_global: 0x400720 small_rw_global: 0x603060 big_rw_global: 0x603080
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
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