Понимание printf в C - PullRequest
       5

Понимание printf в C

0 голосов
/ 08 января 2019

Я пытаюсь понять, как 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"

Находится между «старым» и «новым» перерывом. После завершения записи программы завершают работу.

ВОПРОС: Почему мы выделяем столько страниц и почему разрыв не возвращается к предыдущей после записи системного вызова? Есть ли причина использовать brk вместо mmap для такого форматирования?

Ответы [ 2 ]

0 голосов
/ 08 января 2019

Почему мы выделяем так много страниц?

Использование системного вызова является дорогостоящим, поэтому библиотека запрашивает больше, чем вы хотели бы в данный момент, потому что очень вероятно, что вы захотите большего в ближайшее время. Управление памятью в пользовательском режиме обходится дешевле. Это вопрос гранулярности.

и почему перерыв не вернулся в предыдущий после записи системный вызов происходит?

Опять же, почему бесплатно, если высока вероятность того, что вы попросите о более скором?

Есть ли причина использовать brk вместо mmap для такого форматирования?

Это вопрос выбора, это зависит от реализации.

В сторону: Ваш вопрос больше касается «политики выделения памяти», чем «понимания printf» (это контекст).

0 голосов
/ 08 января 2019

brk() (и его спутник sbrk()) - это своего рода mmap(), специализирующийся на манипулировании размером кучи. Именно по историческим причинам библиотека libc может также использовать mmap() или mremap() напрямую.

Куча расширяется при выделении дополнительной памяти, например, с malloc(), что происходит внутри libc, например, чтобы иметь достаточно места для создания фактической строки из строки формата и параметров или многих других внутренних вещей (т. е. выходные буферы при использовании буферизованного ввода-вывода с семейством функций f *).

Если некоторые части кучи больше не используются, она часто не освобождается автоматически по двум основным причинам: куча может быть фрагментирована, и / или неиспользуемая куча не падает ниже определенного порога, который оправдывает операцию, потому что это может понадобиться снова скоро.

В качестве примечания: сама строка формата, безусловно, не копируется из ro-секции в кучу, это было бы совершенно бесполезно. Но строка результата (обычно) строится в куче.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...