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

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

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

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

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

cvv ★★★★★ ()

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

>ram + swap - kernel_mem - already_allocated

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

>3GB

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

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

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

cvv ★★★★★ ()

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

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

cvv ★★★★★ ()

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

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

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

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

cvv ★★★★★ ()

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

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

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

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

anonymous ()

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

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

anonymous ()

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

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

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

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

cvv ★★★★★ ()

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

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

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

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

cvv ★★★★★ ()

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

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

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

====== 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 ()

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

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

anonymous ()

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

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

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

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

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

cvv ★★★★★ ()

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

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

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

cvv ★★★★★ ()

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

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

anonymous ()

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

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

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

cvv ★★★★★ ()

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

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

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

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

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

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

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

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