LINUX.ORG.RU

Как узнать, сколько памяти доступно для выделения?


0

0

Не понимаю. Есть прога, активно резервирующая и коммитающая память с
помощью mmap. Нужно заранее определить, не вызывая mmap, что памяти
маловато. Но как?! Вызов sysconf(_SC_AVPHYS_PAGES), похоже, никакого
отношения к mmap не имеет. Вот моя программулина.

Когда она падает, sysconf утвержает, что доступно 104055 страниц. Тем
не менее, reserve зарезервировать память не может...

#include <sys/mman.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

const size_t PAGE_SIZE = 0x1000;
const size_t RESERVE_AMOUNT = 64*1024*1024;
const size_t COMMIT_AMOUNT  = 256*1024;

typedef struct Region {
   size_t uncommited;
   void *reserved_base;
   void *uncommited_base;
} Region;

void cat(const char *filename) {
   char buf[1024];
   FILE *f;
   size_t rd;

   printf("----- %s -----\n", filename);

   f = fopen(filename, "rb");
   assert(f != NULL);

   while((rd = fread(buf, 1, sizeof(buf), f)) > 0)
      write(1, buf, rd);

   fclose(f);
}

void Exit(int code) {
   cat("/proc/self/maps");
   cat("/proc/meminfo");
   exit(code);
}

void *Malloc(size_t size) {
   void *mem = malloc(size);
        
   if(mem == NULL) {
      perror("malloc");
      Exit(1);
   }

   return mem;
}

void *reserve(size_t size) {
   void *mem;

   assert(size % PAGE_SIZE == 0);

   printf("Reserving %d KB (%ld pages free)... ", size / 1024, sysconf(_SC_AVPHYS_PAGES));
   fflush(stdout);

   mem = mmap(NULL,
              size,
              PROT_READ | PROT_WRITE | PROT_EXEC,
              MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS,
              -1, 
              0);

   assert(mem != NULL);

   if(mem != MAP_FAILED)
      printf("OK, base address is %p\n", mem);
   else
      printf("FAILED\n");

   fflush(stdout);

   return mem;
}

int commit(void *adr, size_t size) {
   void *a;

   assert(size % PAGE_SIZE == 0);

   printf("Commiting %d KB at %p (%ld pages free)... ", size / 1024, adr, sysconf(_SC_AVPHYS_PAGES));
   fflush(stdout);
    
   a = mmap (adr,
             size,
             PROT_READ | PROT_WRITE | PROT_EXEC,
             MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS,
             -1,
             0);

   assert ((a == MAP_FAILED) || (a == adr));

   if(a != MAP_FAILED)
      printf("OK\n");
   else
      printf("FAILED\n");

   return a != MAP_FAILED;
}

int main() {
   Region *map = NULL;
   int pass_count = 0;
   
   for(;;) {
      printf("----- Pass %d -----\n", pass_count++);
      fflush(stdout);
         
      map = Malloc(sizeof(Region));

      if((map->reserved_base = reserve(RESERVE_AMOUNT)) == MAP_FAILED) {
         perror("mmap");
         Exit(1);
      }

      map->uncommited_base = map->reserved_base;
      map->uncommited = RESERVE_AMOUNT;

      while(map->uncommited > 0) {
         if(!commit(map->uncommited_base, COMMIT_AMOUNT)) {
            perror("mmap");
            Exit(1);
         }

         map->uncommited -= COMMIT_AMOUNT;
         ((char*)map->uncommited_base) += COMMIT_AMOUNT;
      }
   }

   return 0;
}
anonymous

>Как узнать, сколько памяти доступно для выделения?

если точно то никак. если приблизительно то MIN(ram + swap - kernel_mem - already_allocated,3GB)

а ещё ммеп выделяет не память а адресное пространство а память выделяется ядром автоматически по мере необходимости

cvv ★★★★★
()
Ответ на: комментарий от anonymous

>ram + swap - kernel_mem - already_allocated

приближённый предел физической памяти

>3GB

ограничение адресного пространства.

если надо большие куски памяти до 3Гб на процесс то увеличиваем сумму ram+swap как минимум до 4Gb.

если надо выделять куски памяти полее 3Гб то настойчиво рекомендуется сменить архитектуру на что-то 64-битное

cvv ★★★★★
()
Ответ на: комментарий от anonymous

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

cvv ★★★★★
()
Ответ на: комментарий от cvv

>mem = mmap(NULL, > size, > PROT_READ | PROT_WRITE | PROT_EXEC, > MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS, > -1, > 0);

почему используеш MAP_NORESERVE????

я сразу не обратил внимания. это приводит к существенному ограничению на максимальный размер блока памяти

cvv ★★★★★
()
Ответ на: комментарий от cvv

Враппер не получится. Ведь и dlopen тоже мапирует файлы в память via mmap.

> у тебя commit ВСЕГДА должен возвращать false
Но он возвращает true. Может быть, у меня система кривая?

>это приводит к существенному ограничению на максимальный размер блока памяти
Гм... Почему же?

anonymous
()
Ответ на: комментарий от cvv

> почему используеш MAP_NORESERVE????
Это для того, чтобы страницы не подключались до того, как реально понадобятся.

anonymous
()
Ответ на: комментарий от anonymous

>> почему используеш MAP_NORESERVE????

>Это для того, чтобы страницы не подключались до того, как реально понадобятся.

ерунда. ты требуеш лочить физическую память вместо виртуальной. а страницы не будут подключатся в любом случае

cvv ★★★★★
()
Ответ на: комментарий от anonymous

>> у тебя commit ВСЕГДА должен возвращать false

>Но он возвращает true. Может быть, у меня система кривая?

если я правильно понял то ты пытаешся дважды сделать mmap по одному адресу без munmap между ними. такого делать нельзя.

cvv ★★★★★
()
Ответ на: комментарий от cvv

>...такого делать нельзя.

Сильно сомневаюсь.

====== man mmap qoute =====================================
The address start must be a multiple of the page size. All
pages containing a part of the indicated range are
unmapped, and subsequent references to these pages will
generate SIGSEGV. It is not an error if the indicated
range does not contain any mapped pages.
===========================================================

anonymous
()
Ответ на: комментарий от cvv

М-м... Да, пожалуй. Но я очень сильно сомневаюсь, что повторный mmap запрещён. С чего ты это взял, где так написано?

anonymous
()
Ответ на: комментарий от anonymous

>Но я очень сильно сомневаюсь, что повторный mmap запрещён. С чего ты это взял, где так написано?

а я почти уверен. узнать можно посмотрев исходники ядра.

хотя можно и проще: сделай два раза подряд mmap() - один раз MAP_ANONYMOUS и сразу второй MAP_FIXED на этот же блок памяти.

PS:а может тебе надо выключить в ядре oom-killer????

cvv ★★★★★
()
Ответ на: комментарий от cvv

>PS:а может тебе надо выключить в ядре oom-killer????

это позволит однозначно увязать доступную память и адресное пространство

cvv ★★★★★
()
Ответ на: комментарий от cvv

Посмотрел в glibc: mremap устроен просто как вызов mmap. Я сто раз делал повторные mmap-ы (например, для рисования стеков тредов, например - расстановки guard pages).

anonymous
()
Ответ на: комментарий от anonymous

крайне странно. Если сюда заглянет idle то он точно полезет смотреть исходники ядра на тему достоверности твоего утверждения.

помоемому именно он говорил/доказывал обратные вещи

cvv ★★★★★
()
Ответ на: комментарий от cvv

честно говоря я так устал что вообще ничего уже не понимаю.

но MAP_NORESERVE - это вот что такое.

если у нас OVERCOMMIT_NEVER - он игнорируется.

в противном случае он означает, что вы готовы к OOM, то 
есть do_mmap_pgoff не будет проверять vm_enough_memory()
на предмет "не надо ли мне вернуть NULL ?".

а страницы в любом случае выделяться не будут, для этого
есть MAP_POPULATE.

mremap и mmap - конечно разные функции.

idle ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.