Может Linux “исчерпывать RAM”?

Я подозреваю то, что Вы имеете, встроенное микропрограммное обеспечение, а не драйвер. Попытайтесь поместить его в /usr/lib/firmware. Я полагаю, что драйвер для того устройства rt2800usb, таким образом после того, как встроенное микропрограммное обеспечение существует, выполненное modprobe rt2800usb. Если modprobe жалуется на не нахождение драйвера, необходимо будет скомпилировать его, и это заслуживает отдельного вопроса.

20
26.09.2013, 21:44
6 ответов

Что могло бы происходить, если процесс "уничтожается из-за низкой RAM"?

Иногда говорится, что Linux по умолчанию никогда не отклоняет просьбы о большей памяти из кода приложения - например. malloc().1 Это не на самом деле верно; значение по умолчанию использует эвристику посредством чего

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

От [linux_src]/Documentation/vm/overcommit-accounting (все кавычки от 3,11 деревьев). Точно, какое количество как "серьезно дикое выделение" не отмечено явное, таким образом, мы должны были бы пройти источник для определения деталей. Мы могли также использовать экспериментальный метод в сноске 2 (ниже), чтобы попытаться получить некоторое отражение эвристики - на основе этого, мое начальное эмпирическое наблюдение что происходит при идеальных обстоятельствах (==, система неактивна), если у Вас не будет подкачки, то Вам разрешат выделить приблизительно половину Вашей RAM, и если у Вас действительно будет подкачка, то Вы получите приблизительно половину Вашей RAM плюс вся Ваша подкачка. Это более или менее для каждого процесса (но обратите внимание, что этот предел является динамичным и подлежит изменению из-за состояния, посмотрите некоторые наблюдения в сноске 5).

Половина Вашей RAM плюс подкачка является явно значением по умолчанию для поля "CommitLimit" в /proc/meminfo. Вот то, что это означает - и обратите внимание, что это на самом деле не имеет никакого отношения к пределу, просто обсужденному (от [src]/Documentation/filesystems/proc.txt):

CommitLimit: На основе превышать возможности отношения ('vm.overcommit_ratio'), это - общий объем памяти, в настоящее время доступной, чтобы быть выделенным в системе. Этот предел только придерживается к, если строгий принимают на себя непосильные обязательства, учет включен (режим 2 в 'vm.overcommit_memory'). CommitLimit вычисляется со следующей формулой: CommitLimit = ('vm.overcommit_ratio' * Физическая RAM) + Подкачка, Например, в системе с 1G физической RAM и 7G подкачки с 'vm.overcommit_ratio' 30 это привело бы к CommitLimit 7.3G.

Ранее заключенный в кавычки превышенный возможности считавший документ указывает что значение по умолчанию vm.overcommit_ratio 50. Таким образом, если Вы sysctl vm.overcommit_memory=2, можно затем скорректировать vm.covercommit_ratio (с sysctl) и посмотрите последствия 3 режим по умолчанию, когда CommitLimit не осуществляется и только "очевидный принимает на себя непосильные обязательства адресного пространства, отказаны", когда vm.overcommit_memory=0.

В то время как стратегия по умолчанию действительно имеет предельное предотвращение эвристики для каждого процесса "серьезно дикого выделения", это действительно оставляет систему в целом свободной стать серьезно диким, выделение, мудрое 4, что Это означает в какой-то момент, что это может исчерпать память и иметь для объявления банкротства к некоторому процессу (процессам) через уничтожителя OOM.

Что уничтожает уничтожитель OOM? Не обязательно процесс, который попросил память, когда не было ни одного, так как это - не обязательно действительно виновный процесс, и что еще более важно, не обязательно тот, который наиболее быстро вытащит систему из проблемы, в которой это находится.

Это цитируется отсюда, который, вероятно, цитирует 2.6.x источник:

/*
 * oom_badness - calculate a numeric value for how bad this task has been
 *
 * The formula used is relatively simple and documented inline in the
 * function. The main rationale is that we want to select a good task
 * to kill when we run out of memory.
 *
 * Good in this context means that:
 * 1) we lose the minimum amount of work done
 * 2) we recover a large amount of memory
 * 3) we don't kill anything innocent of eating tons of memory
 * 4) we want to kill the minimum amount of processes (one)
 * 5) we try to kill the process the user expects us to kill, this
 *    algorithm has been meticulously tuned to meet the principle
 *    of least surprise ... (be careful when you change it)
 */

Который походит на достойное объяснение. Однако не становясь судебным, № 5 (который избыточен из № 1) кажется, что жесткое продает мудрую реализацию, и № 3 избыточен из № 2. Таким образом, могло бы иметь смысл считать срезанным на # 2/3 и № 4.

Я держал через недавний источник (3.11) и заметил, что этот комментарий изменился тем временем:

/**
 * oom_badness - heuristic function to determine which candidate task to kill
 *
 * The heuristic for determining which task to kill is made to be as simple and
 * predictable as possible.  The goal is to return the highest value for the
 * task consuming the most memory to avoid subsequent oom failures.
 */

Это немного более явно о № 2: "Цель состоит в том, чтобы [уничтожить] задачу, использующую большую часть памяти для предотвращения последующих oom отказов" и косвенно № 4 ("мы хотим уничтожить минимальное количество процессов (один)).

Если Вы хотите видеть уничтожителя OOM в действии, см. сноску 5.


1 Заблуждение, от которого Gilles к счастью избавил меня, видят комментарии.


2 Вот простой бит C, который просит все больше большие блоки памяти определять, когда запрос на больше перестанет работать:

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

#define MB 1 << 20

int main (void) {
    uint64_t bytes = MB;
    void *p = malloc(bytes);
    while (p) {
        fprintf (stderr,
            "%lu kB allocated.\n",
            bytes / 1024
        );
        free(p);
        bytes += MB;
        p = malloc(bytes);
    }
    fprintf (stderr,
        "Failed at %lu kB.\n",
        bytes / 1024
    );
    return 0;
}            

Если Вы не знаете C, можно скомпилировать это gcc virtlimitcheck.c -o virtlimitcheck, затем выполненный ./virtlimitcheck. Это абсолютно безопасно, поскольку процесс не использует ни одного пространства, это просит - т.е. это никогда действительно использует любую RAM.

В 3,11 x86_64 системах с системой на 4 ГБ и 6 ГБ подкачки, я перестал работать на уровне ~7400000 КБ; число колеблется, поэтому, возможно, заявите, фактор. Это по совпадению близко к CommitLimit в /proc/meminfo, но изменение этого через vm.overcommit_ratio не имеет никакого значения. В 3.6.11 32-разрядных системах ARM 448 МБ с 64 МБ подкачки, однако, я перестал работать на уровне ~230 МБ. Это интересно с тех пор в первом случае, сумма почти удваивает сумму RAM, тогда как во втором это о 1/4, что - сильно допущение объема подкачки является фактором. Это было подтверждено путем выключения подкачки в первой системе, когда пороговое значение отказа снизилось до ~1.95 ГБ, очень похожее отношение к небольшому полю ARM.

Но это действительно для каждого процесса? Это, кажется. Короткая программа ниже просит определяемый пользователем блок памяти, и если это успешно выполняется, ожидает Вас для удара возврата - этот способ, которым можно попробовать несколько одновременных экземпляров:

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

#define MB 1 << 20

int main (int argc, const char *argv[]) {
    unsigned long int megabytes = strtoul(argv[1], NULL, 10);
    void *p = malloc(megabytes * MB);
    fprintf(stderr,"Allocating %lu MB...", megabytes);
    if (!p) fprintf(stderr,"fail.");
    else {
        fprintf(stderr,"success.");
        getchar();
        free(p);
    }
    return 0;
}

Остерегайтесь, однако, это это не строго о сумме RAM, и подкачка независимо от использования - см. сноску 5 для наблюдений об эффектах состояния системы.


3 CommitLimit относится на сумму адресного пространства, допускал систему когда vm.overcommit_memory = 2. По-видимому, затем сумма, которую можно выделить, должна быть то, что минус то, что уже фиксируется, который является, по-видимому, Committed_AS поле.

Потенциально интересный эксперимент, демонстрирующий это, должен добавить #include <unistd.h> к вершине virtlimitcheck.c (см. сноску 2), и a fork() прямо перед while() цикл. Это, как гарантируют, не будет работать, как описано здесь без некоторой утомительной синхронизации, но существует достойный шанс, это будет, YMMV:

> sysctl vm.overcommit_memory=2
vm.overcommit_memory = 2
> cat /proc/meminfo | grep Commit
CommitLimit:     9231660 kB
Committed_AS:    3141440 kB
> ./virtlimitcheck 2&> tmp.txt
> cat tmp.txt | grep Failed
Failed at 3051520 kB.
Failed at 6099968 kB.

Это имеет смысл - рассмотрение tmp.txt подробно, Вы видите, что процессы чередуют свои большие и большие выделения (это легче при броске pid в вывод), пока один, очевидно, не утверждал достаточно, что другой перестал работать. Победитель затем свободен захватить все до CommitLimit минус Committed_AS.


4 Это стоит упомянуть в этой точке, если Вы уже не понимаете виртуальное обращение и подкачку по обращению, которая, что передает обязательство, возможное во-первых, то, что то, что ядро выделяет процессам пространства пользователя, не является физической памятью вообще - это - виртуальное адресное пространство. Например, если процесс резервирует 10 МБ для чего-то, это размечается как последовательность (виртуальных) адресов, но те адреса еще не соответствуют физической памяти. Когда к такому адресу получают доступ, это приводит к отсутствию страницы, и затем ядро пытается отобразить его на реальную память так, чтобы он мог сохранить действительное значение. Процессы обычно резервируют намного больше виртуального пространства, чем они на самом деле доступ, который позволяет ядру делать наиболее эффективное использование RAM. Однако физическая память является все еще конечным ресурсом и когда все это было отображено на виртуальном адресном пространстве, некоторое виртуальное адресное пространство должно быть устранено к свободному некоторая RAM.


5 Сначала предупреждение: Если Вы пробуете это vm.overcommit_memory=0, удостоверьтесь, что Вы сохраняете свою первую работу и закрываете любые важные приложения, потому что система будет заморожена в течение ~90 секунд, и некоторый процесс умрет!

Идея состоит в том, чтобы выполнить fork-бомбу, которая испытывает таймаут после 90 секунд, с ветвлениями, выделяющими место и некоторые из них пишущий большие объемы данных к RAM, все время сообщая stderr.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>

/* 90 second "Verbose hungry fork bomb".
Verbose -> It jabbers.
Hungry -> It grabs address space, and it tries to eat memory.

BEWARE: ON A SYSTEM WITH 'vm.overcommit_memory=0', THIS WILL FREEZE EVERYTHING
FOR THE DURATION AND CAUSE THE OOM KILLER TO BE INVOKED.  CLOSE THINGS YOU CARE
ABOUT BEFORE RUNNING THIS. */

#define STEP 1 << 30 // 1 GB
#define DURATION 90

time_t now () {
    struct timeval t;
    if (gettimeofday(&t, NULL) == -1) {
        fprintf(stderr,"gettimeofday() fail: %s\n", strerror(errno));
        return 0;
    }
    return t.tv_sec;
}

int main (void) {
    int forks = 0;
    int i;
    unsigned char *p;
    pid_t pid, self;
    time_t check;
    const time_t start = now();
    if (!start) return 1;

    while (1) {
    // Get our pid and check the elapsed time.
        self = getpid();
        check = now();
        if (!check || check - start > DURATION) return 0;
        fprintf(stderr,"%d says %d forks\n", self, forks++);
    // Fork; the child should get its correct pid.
        pid = fork();
        if (!pid) self = getpid();
    // Allocate a big chunk of space.
        p = malloc(STEP);
        if (!p) {
            fprintf(stderr, "%d Allocation failed!\n", self);
            return 0;
        }
        fprintf(stderr,"%d Allocation succeeded.\n", self);
    // The child will attempt to use the allocated space.  Using only
    // the child allows the fork bomb to proceed properly.
        if (!pid) {
            for (i = 0; i < STEP; i++) p[i] = i % 256;
            fprintf(stderr,"%d WROTE 1 GB\n", self);
        }
    }
}                        

Скомпилируйте это gcc forkbomb.c -o forkbomb. Во-первых, попробуйте его sysctl vm.overcommit_memory=2 - Вы, вероятно, получите что-то как:

6520 says 0 forks
6520 Allocation succeeded.
6520 says 1 forks
6520 Allocation succeeded.
6520 says 2 forks
6521 Allocation succeeded.
6520 Allocation succeeded.
6520 says 3 forks
6520 Allocation failed!
6522 Allocation succeeded.

В этой среде этот вид fork-бомбы не становится очень далеким. Обратите внимание, что число в том, "говорит ветвления N", не является общим количеством процессов, это - количество процессов в продвижении цепочки/ответвления до того.

Теперь попробуйте его vm.overcommit_memory=0. При перенаправлении stderr в файл можно сделать некоторый сырой анализ позже, например:

> cat tmp.txt | grep failed
4641 Allocation failed!
4646 Allocation failed!
4642 Allocation failed!
4647 Allocation failed!
4649 Allocation failed!
4644 Allocation failed!
4643 Allocation failed!
4648 Allocation failed!
4669 Allocation failed!
4696 Allocation failed!
4695 Allocation failed!
4716 Allocation failed!
4721 Allocation failed!

Только 15 процессам не удалось выделить 1 ГБ - демонстрирующий, что эвристика для overcommit_memory = 0 затронута состоянием. Сколькими процессы были там? Взгляд в конце tmp.txt, вероятно,> 100,000. Теперь, как может, на самом деле добрался для использования 1 ГБ?

> cat tmp.txt | grep WROTE
4646 WROTE 1 GB
4648 WROTE 1 GB
4671 WROTE 1 GB
4687 WROTE 1 GB
4694 WROTE 1 GB
4696 WROTE 1 GB
4716 WROTE 1 GB
4721 WROTE 1 GB

Восемь - который снова имеет смысл, так как в то время, когда у меня было ~3 ГБ свободной RAM и 6 ГБ подкачки.

Взгляните на свои системные журналы после того, как Вы сделаете это. Необходимо видеть, что уничтожитель OOM сообщает об очках (среди других вещей); по-видимому, это касается oom_badness.

41
27.01.2020, 19:43
  • 1
    не является решением (или даже связанный) к памяти по обязательству. Выделение памяти (например: malloc), о запросе виртуальной памяти быть зарезервированным, не физическая. –  jlliagre 26.09.2013, 23:35
  • 2
    @jillagre: "выгрузка не является решением (или даже связанный) к памяти по обязательству"-> Да, на самом деле это. Нечасто используемые страницы подкачиваются из RAM, оставляя больше RAM доступным для контакта с отсутствиями страницы, вызванными подкачкой по обращению / выделения (который является механизмом, который передает возможное обязательство). Выгруженному страницы, вероятно, также придется быть спрос, разбитый на страницы назад в RAM в какой-то момент. –  goldilocks 27.09.2013, 09:05
  • 3
    "Выделение памяти (например: malloc), о запросе виртуальной памяти быть зарезервированным, не физическая".-> Право, но ядро могло (и дополнительно, быть), говорят "нет", когда нет никаких физических оставленных отображений. Это, конечно, не было бы, потому что процесс исчерпал виртуальное адресное пространство (или по крайней мере не обычно, так как это возможно также, по крайней мере, в 32-разрядных системах). –  goldilocks 27.09.2013, 09:08
  • 4
    не то, что делает память по обязательству возможной. Linux, конечно, по памяти фиксации в системах без области подкачки вообще. Вы могли бы путать память по обязательству и подкачке по обращению. Если в Linux говорится "нет" malloc с процессом на 64 бита, т.е. если бы он не настроен, чтобы всегда принять на себя непосильные обязательства, который был бы или из-за повреждения памяти или потому что сумма всего резервирования памяти (или отображенный или не к RAM или диску) по пороговому порогу в зависимости от конфигурации. Это не имеет никакого отношения к Использованию оперативной памяти, как это могло бы произойти даже, в то время как существует все еще свободная RAM. –  jlliagre 27.09.2013, 10:15
  • 5
    "Подкачка по обращению не то, что делает память по обязательству возможной".->, Возможно, было бы лучше сказать, что это - виртуальное обращение, которое делает и подкачку по обращению и по обязательству возможной. "Linux, конечно, по памяти фиксации в системах без области подкачки вообще".->, Очевидно, так как подкачка по обращению не требует подкачки; подкачка по обращению от подкачки является просто специальным экземпляром подкачки по обращению. Еще раз подкачка является решением по обязательству, не в том смысле, что это решает проблему, а в том смысле, что поможет предотвратить потенциальные события OOM, которые могли бы закончиться из-за обязательства. –  goldilocks 27.09.2013, 16:27

Этого не произойдет с Вами, если Вы только когда-либо будете загружать 1G данных в память. Что, если Вы загружаетесь очень намного больше? Например, я часто работаю с огромными файлами, содержащими миллионы вероятностей, которые должны быть загружены в R. Это берет приблизительно 16 ГБ RAM.

Выполнение вышеупомянутого процесса на моем ноутбуке заставит это начинать подкачивать как сумасшедший, как только мои 8 ГБ RAM были заполнены. Это, в свою очередь, замедлит все, потому что чтение из диска намного медленнее, чем чтение из RAM. Что, если у меня есть ноутбук с 2 ГБ RAM и только 10 ГБ свободного пространства? После того как процесс взял всю RAM, он также заполнит диск, потому что он пишет для свопинга, и меня оставляют без большего количества RAM, и больше никакого пространства для свопинга в (люди склонны ограничивать подкачку специализированным разделом, а не своп-файлом по точно той причине). Это - то, где уничтожитель OOM входит и начинает уничтожать процессы.

Так, система может действительно исчерпать память. Кроме того, в большой степени подкачивающие системы могут стать неприменимыми задолго до того, как это происходит просто из-за медленных операций ввода-вывода из-за свопинга. Каждый обычно не хочет подкачивать как можно больше. Даже на высокопроизводительных серверах с быстрыми SSD в производительности существует четкое уменьшение. На моем ноутбуке, который имеет классический диск на 7200 об/мин, любой значительный свопинг по существу представляет неприменимую систему. Чем больше это подкачивает, тем медленнее это добирается. Если я не уничтожаю незаконный процесс быстро, все зависает, пока уничтожитель OOM не вступает.

16
27.01.2020, 19:43

Процессы не уничтожаются, когда больше нет RAM, они уничтожаются, когда они были обмануты этот путь:

  • Ядро Linux обычно позволяет процессам выделять (т.е. резерв) объем виртуальной памяти, которая больше, чем, что действительно доступно (часть RAM + вся область подкачки)
  • пока процессы только получают доступ к подмножеству страниц, которые они зарезервировали, все хорошо работает.
  • если через какое-то время, процесс пытается получить доступ к странице, он владеет, но больше страниц не свободно, из ситуации с памятью происходит
  • Уничтожитель OOM выбирает один из процессов, не обязательно тот, который запросил новую страницу и просто уничтожает ее для восстановления виртуальной памяти.

Это могло бы произойти даже, в то время как система активно не подкачивает, например, если область подкачки заполнена страницами памяти демонов сна.

Этого никогда не происходит на Ose, которые не превышают возможности памяти. С ними никакой вероятностный процесс не уничтожается, но первый процесс, просящий виртуальную память, в то время как это исчерпывается, имеет malloc (или подобный) возвращающийся по ошибке. Этому таким образом дают шанс правильно обработать ситуацию. Однако на этих Ose, это могло бы также произойти, система исчерпывает виртуальную память, в то время как существует все еще свободная RAM, которая довольно сбивает с толку и обычно является недооцененной.

5
27.01.2020, 19:43

Только добавить в другой точке зрения из других ответов, хост многого VPS несколько виртуальных машин на любом данном сервере. Любой единственный VM будет иметь указанную сумму RAM для их собственного использования. Многие поставщики предлагают "пакетную RAM", в которой они могут использовать RAM вне суммы, что они определяются. Это предназначено, чтобы быть только для краткосрочного использования и тех, кто идет вне этой суммы расширенного количества времени, может быть оштрафован хостом уничтожение процессов для перевода в нерабочее состояние суммы RAM, используемой так, чтобы другие не страдали от перегружаемой хост-машины.

2
27.01.2020, 19:43

Некоторое время Linux занимает внешнее виртуальное место. Это - раздел подкачки. Когда Поршень заполнен, Linux берет эту область подкачки для кончений приоритетного процесса.

-1
27.01.2020, 19:43
  • 1
    Никакой процесс не выполняется от подкачки. Виртуальная память разделена на одинаково размерные отличные единицы, названные страницами. Когда физическая память освобождена, низкоприоритетные страницы выселены из RAM. В то время как страницы в кэше файла имеют поддержку файловой системы, анонимные страницы должны быть сохранены в подкачке. Приоритет страницы непосредственно не связан с приоритетом процесса, это принадлежит, но как часто это используется. Если рабочий процесс пытается получить доступ к странице не в физической памяти, отсутствие страницы сгенерировано, и процесс вытесняется в пользу другого процесса, в то время как необходимая страница (страницы) выбирается от диска. –  Thomas Nyman 27.09.2013, 10:05

Когда доступная RAM исчерпывается, ядро начинает выгружать биты обработки к диску. На самом деле ядро начинает подкачивать, когда RAM является близким исчерпанием: это начинает подкачивать заранее, когда это имеет неактивный момент, чтобы быть более быстро реагирующим, если приложение внезапно требует большей памяти.

Обратите внимание, что RAM не используется только для хранения памяти процессов. В типичной здоровой системе только приблизительно половина RAM используется процессами, и другая половина используется для дискового кэша и буферов. Это обеспечивает хороший баланс между выполнением вводом/выводом файла и процессов.

Область подкачки весьма конечна. В какой-то момент, если процессы будут продолжать выделять все больше памяти, то данные избытка из RAM заполнят подкачку. Когда это происходит, процессы, которые пытаются запросить, больше памяти посмотрите их отклоненную просьбу.

По умолчанию Linux превышает возможности памяти. Это означает, что будет иногда позволять процессу работать с памятью, которую это зарезервировало, но не использовало. Главной причиной для того, чтобы иметь чрезмерные обязательства является способ, которым работает разветвление. Когда процесс запускает подпроцесс, дочерний процесс концептуально работает в копии памяти родителя — два процесса первоначально имеют память с тем же содержанием, но что содержание будет отличаться, поскольку процессы делают изменения каждым в их собственном пространстве. Для реализации этого полностью ядро должно было бы скопировать всю память родителя. Это сделало бы разветвляющимся медленный, таким образом, копия на записи методов ядра: первоначально, ребенок совместно использует всю его память с родителем; каждый раз, когда любой процесс пишет в поделившуюся страницу, ядро создает копию той страницы для повреждения совместного использования.

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

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

Вы могли бы думать в этой точке, что уничтожение процесса запроса является очевидным решением. Но на практике, это не настолько хорошо. Процесс может быть важным, который, оказывается, только должен получить доступ к одной из его страниц теперь, в то время как может быть другой, менее важное выполнение процессов. Таким образом, ядро включает сложную эвристику для выбора который процессы уничтожить — (в) известном уничтожителе OOM.

3
27.01.2020, 19:43

Теги

Похожие вопросы