Не может исчерпать физическую память в 32-битном Linux - PullRequest
0 голосов
/ 02 мая 2018

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

У меня есть программа, специально созданная для того, чтобы вызвать экстремальную утечку памяти (например, что происходит, когда вы не освобождаете память после ее выделения). В 64-битных операционных системах (Windows, Linux и т. Д.) Он делает то, что должен делать. Он заполняет физическую память, а затем заполняет пространство подкачки ОС. В Linux процесс завершается ОС. В Windows, однако, это не так, и он продолжает работать. Возможный результат - системный сбой.

Вот код:

#include <stdlib.h>
#include <stdio.h>

void main()
{
    while(1)
    {
        int *a;
        a = (int*)calloc(65536, 4);
    }
}

Однако, если вы скомпилируете и запустите этот код в 32-битном дистрибутиве Linux, это никак не повлияет на использование физической памяти. Он использует примерно 1% моих 4 ГБ выделенной оперативной памяти, и после этого он никогда не увеличивается. У меня нет действительной копии 32-битной Windows для тестирования, поэтому я не могу быть уверен, что это происходит и в 32-битной Windows.

Может кто-нибудь объяснить, почему использование calloc заполнит физическую память 64-битной ОС Linux, но не 32-битной ОС Linux?

1 Ответ

0 голосов
/ 02 мая 2018

Функции malloc и calloc технически не выделяют память, несмотря на их имя. Они фактически выделяют части адресного пространства вашей программы с правами чтения / записи на уровне ОС. Это небольшая разница, которая не актуальна большую часть времени.

Эта программа, как написано, использует только адресное пространство. В конце концов, calloc начнет возвращать NULL, но программа продолжит работу.

#include <stdlib.h>
// Note main should be int.
int main() {
    while (1) {
        // Note calloc should not be cast.
        int *a = calloc(65536, sizeof(int));
    }
}

Если вы пишете по адресам, возвращенным из calloc, это заставит ядро ​​выделить память для поддержки этих адресов.

#include <stdlib.h>
#include <string.h>
int main() {
    size_t size = 65536 * 4;
    while (1) {
        // Allocates address space.
        void *p = calloc(size, 1);
        // Forces the address space to have allocated memory behind it.
        memset(p, 0, size);
    }
}

Недостаточно записи в одно место в блоке, возвращаемом из calloc, потому что степень детализации для выделения фактической памяти составляет 4 КиБ (размер страницы ... 4 КиБ является наиболее распространенным). Так что вы можете просто написать на каждой странице.

А как насчет 64-битного случая?

Существуют некоторые накладные расходы на распределение адресного пространства. В 64-битной системе вы получите что-то вроде 40 или 48 бит адресного пространства, из которых примерно половина может быть выделена программе, что составляет не менее 8 ТиБ. В 32-разрядной системе это составляет около 2 ГБ или около того (в зависимости от конфигурации ядра).

Таким образом, в 64-битной системе вы можете выделить ~ 8 ТиБ, а в 32-битной системе вы можете выделить ~ 2 ГиБ, и из-за накладных расходов возникают проблемы. Обычно на каждый звонок накладывается небольшая сумма на malloc или calloc.

См. Также Почему malloc + memset медленнее, чем calloc?

...