Почему аргумент brk(void *end_data_segment) не округляется до границы следующей страницы?

Из интерфейса программирования Linux:

int brk(void * end_data_segment );

Системный вызов brk() устанавливает прерывание программы в место, указанное end_data_segment. Поскольку виртуальная память выделяется в единицах страниц, end_data_segment эффективно округляется до границы следующей страницы.

Итак, для этой демонстрации:

#include <stdio.h>
#include <unistd.h>


int
main(int argc, char *argv[])
{
    long int page_size = sysconf(_SC_PAGESIZE);
    printf("My page size: %ld\n", page_size);
    void* c1 = sbrk(0);
    printf("program break address: %p\n", c1);
    printf("sizeof char: %lu\n", sizeof(char));
    c1 = (void*) ((char*) c1 + 1);
    printf("c1: %p\n", c1);
    brk(c1);
    void* c2 = sbrk(0);
    printf("program break address: %p\n", c2); 

}

выход:

My page size: 4096
program break address: 0x55b0bc104000
sizeof char: 1
c1: 0x55b0bc104001
program break address: 0x55b0bc104001

Я ожидаю, что новый адрес прерывания программы будет: 0x55b0bc104000 + 0x1000(4096 in HEX) == 0x55b0bc105000

Почему я получил не 0x55b0bc105000, а 0x55b0bc104001?


person Rick    schedule 14.01.2020    source источник
comment
Две (несвязанные) вещи: во-первых, размер char указывается всегда равным 1. Вам действительно не нужно печатать его. Во-вторых, правильный формат для печати значения size_t (как указано, например, sizeof) — %zu. Несоответствие спецификатора формата и типа приводит к неопределенному поведению.   -  person Some programmer dude    schedule 14.01.2020


Ответы (1)


Думайте об этом как о двух возможностях:

  • выровняйте end_data_segment по границе страницы; и убедитесь, что размер базовой области виртуального адресного пространства точно соответствует значению end_data_segment

  • не выравнивайте end_data_segment по границе страницы; и убедитесь, что размер базовой области виртуального адресного пространства выровнен (округлен) с размером страницы

В первом случае портативное программное обеспечение (которое не знает, каким будет размер страницы) может (например) увеличить end_data_segment на 1/8 страницы и сделать это 8 раз, и вместо того, чтобы получить одну дополнительную страницу ( результат, который вы, естественно, ожидаете), он получит 8 дополнительных страниц (на 7 страниц больше, чем нужно, и на 7 страниц больше, чем ожидалось). Худший; программное обеспечение может уменьшить размер end_data_segment менее чем на страницу, и это ничего не сделает (будет округлено до исходного значения); и это также может быть сделано много раз, что приведет к большой области, от которой программное обеспечение пыталось избавиться, но все еще существует. Конечно, их можно комбинировать — программное обеспечение может увеличивать end_data_segment на 1 байт, а затем уменьшать end_data_segment на один байт в середине цикла, вызывая неожиданную утечку памяти (пространства), которая может быстро сожрать все доступное виртуальное адресное пространство, когда люди этого ожидают. ничего не тратить. Конечно, программное обеспечение могло бы явно обойти все проблемы, добавляя (нестандартные/непереносимые) исправления повсюду, но это было бы ужасно уродливо.

Второй вариант (не выравнивать end_data_segment) более интуитивно понятен, удобнее и менее подвержен ошибкам.

person Brendan    schedule 14.01.2020
comment
Значит, книга неверна и brk использует второй метод? - person Rick; 14.01.2020
comment
@Rick: есть тонкая разница между словами end_data_segment эффективно округляется до границы следующей страницы, а end_data_segment округляется до границы следующей страницы - фактически подразумевается, что он не округляется, а ведет себя так, как если бы он был (что близко к второй вариант и не ошибочный). - person Brendan; 14.01.2020