我试图了解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 syscall。所以用
gdb catch syscall brk
gdb catch syscall write

显示在我的情况下,当前中断等于0x555555756000,但随后将其设置为0x555555777000。当write出现时,格式化的字符串
x/s $rsi
0x555555756260: "Test rr\n"

驻留在“旧”和"new"之间。写发生后,程序退出。

问题:为什么我们分配了这么多页面,为什么在发生写入系统调用后中断不返回上一页?是否有任何理由使用brk而不是mmap进行此类格式化?

最佳答案

brk()(和它的伴随sbrk())是一种专用于操纵堆大小的mmap()。出于历史原因,它在那里,libc也可以直接使用mmap()mremap()

堆会随着分配的额外内存而扩展,例如使用malloc(),它在libc内部发生,例如有足够的空间从格式字符串和参数或许多其他内部对象(即输出缓冲区)创建实际的字符串当将带缓冲的io与f *函数系列一起使用时)。

如果不再使用堆的某些部分,则通常由于以下两个主要原因而不会自动将其自动释放:堆可能会碎片化,和/或未使用的堆不会低于证明该操作合理的某个阈值,因为它可能是很快需要。

附带说明:格式字符串本身当然不会从ro节复制到堆中,这将是完全无用的。但是结果字符串(通常)是建立在堆上的。

关于c - 了解C语言中的printf,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54090408/

10-16 19:05