我试图了解 printf
在 C 中如何在一个简单的情况下工作。我编写了以下程序:
#include "stdio.h"
int main(int argc, char const *argv[])
{
printf("Test %s\n", argv[1]);
return 0;
}
在二进制文件上运行 objdump
我注意到 Test %s\n
位于 .rodata
objdump -sj .rodata bin
bin: file format elf64-x86-64
Contents of section .rodata:
08e0 01000200 54657374 2025730a 00 ....Test %s..
因此格式化打印似乎执行从 rodata
到其他地方的额外模式复制。
在使用stare ./bin rr
编译并运行它之后,我注意到在实际写入之前有一个brk
系统调用。所以用
gdb catch syscall brk
gdb catch syscall write
显示在我的例子中,当前中断等于 0x555555756000
,但随后设置为 0x555555777000
。当 write
出现时格式化字符串
x/s $rsi
0x555555756260: "Test rr\n"
位于“旧”和"new"中断之间。写入发生后,程序退出。
问题: 为什么我们分配了这么多页,为什么在 write 系统调用发生后 break 没有返回到前一个?是否有任何理由使用 brk
而不是 mmap
来进行这种格式化?
最佳答案
brk()
(及其配套的 sbrk()
)是某种专门用于操纵堆大小的 mmap()
。由于历史原因,libc 也可以直接使用 mmap()
或 mremap()
。
随着分配额外的内存,堆会扩展,例如使用 malloc()
,这在 libc 内部发生,例如有足够的空间从格式字符串创建实际的字符串和参数或许多其他内部事物(即,将缓冲 io 与 f* 函数系列一起使用时的输出缓冲区)。
如果堆的某些部分不再使用,它通常不会自动释放,主要有两个原因:堆可能是碎片化的,和/或未使用的堆没有低于某个阈值,这证明操作是合理的,因为可能很快会再次需要它。
附带说明:格式字符串本身肯定不会从 ro-section 复制到堆中,这将完全没有用。但是结果字符串(通常)是建立在堆上的。
关于c - 理解 C 中的 printf,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54090408/