Выделение огромных объемов памяти
Теперь, когда вы увидели, что ОС Linux преодолевает ограничения модели памяти ОС MS-DOS, давайте усложним ей задачу. Приведенная в упражнении 7.2 программа запрашивает выделение объема памяти, большего, чем физически есть в машине, поэтому можно предположить, что функция malloc начнет давать сбои при приближении к максимальному объему физической памяти, поскольку ядру и всем остальным выполняющимся процессам также нужна память.
Упражнение 7.2. Запрос на всю физическую память
С помощью программы memory2.с мы собираемся запросить больше памяти, чем физически есть в машине. Вам нужно откорректировать определение PHY_MEM_MEGS в соответствии с физическими ресурсами вашего компьютера.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define A_MEGABYTE (1024 * 1024)
#define PHY_MEM_MEGS 1024 /* Откорректируйте это число
должным образом */
int main() {
char *some_memory;
size_t size_to_allocate = A_MEGABYTE;
int megs_obtained = 0;
while (megs_obtained < (PHY_MEM_MEGS * 2)) {
some_memory = (char *)malloc(size_to_allocate);
if (some_memory != NULL) {
megs_obtained++;
sprintf(somememory, "Hello World");
printf("%s — now allocated %d Megabytesn", some_memory, megs_obtained);
} else {
exit(EXIT_FAILURE);
}
}
exit(EXIT_SUCCESS);
}
Далее приведен немного сокращенный вывод:
$ <b>./memory3</b>
Hello World — now allocated 1 Megabytes
Hello World — now allocated 2 Megabytes
...
Hello World — now allocated 2047 Megabytes
Hello World — now allocated 2048 Megabytes
Как это работает
Программа очень похожа на предыдущий пример. Это просто циклы, запрашивающие все больше и больше памяти до тех пор, пока не будет выделено памяти вдвое больше, чем заданный вами с помощью корректировки определения PHY_MEM_MEGS объем памяти, имеющейся у вашего компьютера. Удивительно, что эта программа вообще работает, потому что мы, как оказалось, создали программу, которая использует каждый байт физической памяти на машине одного из авторов. Обратите внимание на то, что в нашем вызове malloc применяется тип size_t.
Другая интересная особенность заключается в том, что, по крайней мере, на данной машине программа выполняется в мгновение ока. Таким образом, мы не только вне сомнения использовали всю память, но и сделали это на самом деле очень быстро.
Продолжим исследование и посмотрим, сколько памяти мы сможем выделить на этой машине с помощью программы memory3.c (упражнение 7.3). Поскольку уже понятно, что система Linux способна очень умно обходиться с запросами памяти, мы каждый раз будем выделять память по 1 Кбайт и записывать данные в каждый полученный нами блок.
Упражнение 7.3. Доступная память
Далее приведена программа memory3.c. По своей истинной природе она крайне недружественная по отношению к пользователю и может очень серьезно повлиять на многопользовательскую машину. Если вас беспокоит подобный риск, лучше совсем не запускать ее; если вы окажитесь от выполнения этой программы, усвоению материала это не повредит.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define ONE_K (1024)
int main() {
char *some_memory;
int size_to_allocate = ONE_K;
int megs_obtained = 0;
int ks_obtained = 0;
while (1) {
for (ks_obtained = 0; ks_obtained < 1024; ks_obtained++) {
some_memory = (char *)malloc(size_to_allocate);
if (some_memory == NULL) exit(EXIT_FAILURE);
sprintf(some_memory, "Hello World");
}
megs_obtained++;
printf("Now allocated %d Megabytesn", megs_obtained);
}
exit(EXIT_SUCCESS);
}
На этот раз вывод, также сокращенный, выглядит следующим образом:
$ <b>./memory3</b>
Now allocated 1 Megabytes
...
Now allocated 1535 Megabytes
Now allocated 1536 Megabytes
Out of Memory: Killed process 2365
Killed
После этого программа завершается. Она выполняется несколько секунд и существенно замедляется при приближении к размеру, равному объему физической памяти на компьютере, а также активно использует жесткий диск. Тем не менее программа выделяла и получала доступ к области памяти, большей по размеру объема физической памяти, которая была установлена на машине одного из авторов во время написания этой главы. В конце концов, система защищает себя от этой довольно агрессивной программы и уничтожает ее. В некоторых системах она может тихо закончить выполнение, когда функция malloc завершается аварийно.
Как это работает
Память, выделяемая приложению, управляется ядром системы Linux. Каждый раз, когда программа запрашивает память, пытается записывать в память или считывать из памяти, которая была выделена, ядро Linux решает, как обрабатывать этот запрос.
Сначала ядро может использовать свободную физическую память для удовлетворения запроса приложения на выделение памяти, но когда физическая память исчерпана, ядро начинает использовать так называемую область свопинга или подкачки. В ОС Linux это отдельная область диска, выделяемая во время инсталляции системы. Если вы знакомы с ОС Windows, функционирование области свопинга в Linux немного напоминает файл подкачки в Windows. Но в отличие от ОС Windows при написании программного кода не нужно беспокоиться ни о локальной, ни о глобальной динамической памяти (heap), ни о выгружаемых сегментах памяти — ядро Linux все организует для вас.