2013-09-20 2 views
3

Я написал простое приложение для тестирования потребления памяти. В этом тестовом приложении я создал четыре процесса для непрерывного потребления памяти, эти процессы не освободят память, если процесс не завершится.Почему это приложение не потребляет столько памяти, сколько ожидалось

Я ожидал, что это тестовое приложение будет потреблять большую часть памяти ОЗУ и привести к замедлению или сбою другого приложения. Но результат не такой, как ожидалось. Ниже приведен код:

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

using namespace std; 
unsigned short calcrc(unsigned char *ptr, int count) 
{ 
    unsigned short crc; 
    unsigned char i; 

    //high cpu-consumption code 
    //implements the CRC algorithm 
    //CRC is Cyclic Redundancy Code 
} 


void* ForkChild(void* param){ 
    vector<unsigned char*> MemoryVector; 
    pid_t PID = fork(); 
    if (PID > 0){ 
     const int TEN_MEGA = 10 * 10 * 1024 * 1024; 
     unsigned char* buffer = NULL; 
     while(1){ 
      buffer = NULL; 
      buffer = new unsigned char [TEN_MEGA]; 
      if (buffer){ 
       try{ 
        calcrc(buffer, TEN_MEGA); 
        MemoryVector.push_back(buffer); 
       } catch(...){ 
        printf("An error was throwed, but caught by our app!\n"); 
        delete [] buffer; 
        buffer = NULL; 
       } 
      } 
      else{ 
       printf("no memory to allocate!\n"); 
       try{ 
        if (MemoryVector.size()){ 
         buffer = MemoryVector[0]; 
         calcrc(buffer, TEN_MEGA); 
         buffer = NULL; 
        } else { 
         printf("no memory ever allocated for this Process!\n"); 
         continue; 
        } 
       } catch(...){ 
        printf("An error was throwed -- branch 2," 
          "but caught by our app!\n"); 
        buffer = NULL; 
       } 
      } 
     } //while(1) 
    } else if (PID == 0){ 
    } else { 
     perror("fork error"); 
    } 

    return NULL; 
} 


int main(){ 
int children = 4; 
    while(--children >= 0){ 
    ForkChild(NULL); 
    }; 

    while(1) sleep(1); 
    printf("exiting main process\n"); 
    return 0; 
} 
  1. ТОП команда

    PID USER  PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND   
    2775 steve  20 0 1503m 508 312 R 99.5 0.0 1:00.46 test    
    2777 steve  20 0 1503m 508 312 R 96.9 0.0 1:00.54 test    
    2774 steve  20 0 1503m 904 708 R 96.6 0.0 0:59.92 test    
    2776 steve  20 0 1503m 508 312 R 96.2 0.0 1:00.57 test 
    

    Хотя процессор высок, но память процент остается 0.0. Как это возможно?

  2. Свободная команда

        free shared buffers  cached   
    Mem:   3083796  0  55996  428296 
    

    свободной памяти больше, чем 3G 4G из оперативной памяти.

Кто-нибудь знает, почему это тестовое приложение работает не так, как ожидалось?

+7

Linux фактически не будет выделять страницы памяти, если вы * запись * им. Я не вижу, чтобы вы писались в своем буфере где угодно. –

+0

@ 한국 매미 'new []' default-инициализирует все элементы. –

+2

@CoryNelson Да, и инициализация по умолчанию 'unsigned char' является no-op: запись не требуется. –

ответ

6

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

Если вы хотите заставить систему выделить (зафиксировать) физическую страницу, то вам нужно будет написать.

Следующая строка не выдает никакого записи, как это по умолчанию инициализация unsigned char, которая является не-оп:

buffer = new unsigned char [TEN_MEGA]; 

Если вы хотите, чтобы заставить совершить, использовать ноль -initialization:

buffer = new unsigned char [TEN_MEGA](); 
+1

-1 для сброса вашего ответа в Lounge и raw 'new []'. – Puppy

+5

-1 для использования 'new' ?? Вопрос не требовал лучших практик, он спрашивал об использовании памяти. Ответ решает это кратко - введение чего-либо еще только свернет. –

-1

Если вы хотите, чтобы сожрать много памяти:

int mb = 0; 
char* buffer; 
while (1) { 
    buffer = malloc(1024*1024); 
    memset(buffer, 0, 1024*1024); 
    mb++; 
} 

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

Как уже упоминалось, ваш код никогда не записывается в буфер после его выделения. Здесь memset используется для записи в буфер.

+0

Зачем выделять один мегабайт за раз? Почему бы не выделить сразу несколько сотен МБ или ГБ? – Cramer

+0

Уверенный, просто умножьте на другой 1024. – Ryan

+0

-1, потому что это фактически не отвечает на вопрос. – Puppy

3

Чтобы сделать комментарии в ответ:

  • Linux не будет выделять страницы памяти для процесса до тех пор, пока пишет к ним (копирования при записи).
  • Кроме того, вы ничего не пишете в своем буфере, поскольку конструктор по умолчанию для unsigned chardoes not perform any initializations и new[] default-initialized все элементы.
+0

очень короткий и полезный! Если ваши комментарии пришли раньше, я мог бы принять другое решение: _) – Steve

1

fork() возвращает PID в родителе и 0 в дочернем. Ваш ForkChild, как написано, выполнит всю работу в родительском, а не в дочернем.

И стандартный оператор new никогда не вернет нуль; он будет бросать, если ему не удастся выделить память (но из-за overcommit на самом деле это не произойдет ни в Linux). Это означает, что ваш тест buffer после выделения не имеет смысла: он всегда будет либо первым, либо никогда не достигнет теста. Если вы хотите получить нулевой доход, вам нужно написать new (std::nothrow) .... Включите <new> для этого.

+0

Да, вы правы, я сделал ошибку, судя, какой из них является дочерним процессом. Более того, я также очень помог вам в комментариях к новому оператору. Большое спасибо за указание на это! – Steve

0

Но ваша программа действительно делает то, что вы ожидали от нее. Как указал ответ (@ Michael Foukarakis's answer), память не используется. В вашем выводе программы top я заметил, что столбец virt имел большой объем памяти для каждого процесса, запускающего вашу программу. Чуть позже прибегая к помощи, я увидел, что это было:

VIRT - Virtual Memory Size (KiB). The total amount of virtual memory used by the task.Она включает в себя весь код, данные и общие библиотеки плюс страницы, которые были выгружены и страницы, которые были распознаны, но не используется.

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

отрывок из этого wiki page

страницы, страницы памяти или виртуальной страницы - фиксированной длины непрерывного блока виртуальной памяти, и это наименьшая единица данных для:

  • распределение памяти, выполняемое операционной системой для программы; и
  • Передача между основной памятью и любым другим вспомогательным хранилищем, например жестким диском.

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

Источники:

+0

О, я смущен агиан. Если программа фактически зарезервировала память, почему я получил «бесплатный 3083796» из результата запуска команды «без оболочки» (оперативная память моего компьютера равна 4 Гбайтам)? – Steve

+0

Если вы все еще смущены, я обновил свой ответ, и я нашел [этот вопрос] (http://unix.stackexchange.com/questions/34422/what-is-the-buffers-column-in-the-output- от-бесплатно) на веб-сайте обмена Unix и Linux, который, я думаю, люди там лучше объяснят вам это. – smac89

Смежные вопросы