Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

415
Views
¿Cómo mmap () un archivo grande sin arriesgar el asesino OOM?

Tengo una caja ARM Linux integrada con una cantidad limitada de RAM (512 MB) y sin espacio de intercambio, en la que necesito crear y luego manipular un archivo bastante grande (~ 200 MB). Cargar todo el archivo en la RAM, modificar el contenido en la RAM y luego volver a escribirlo a veces invocaría el OOM-killer, que quiero evitar.

Mi idea para evitar esto fue usar mmap() para mapear este archivo en el espacio de direcciones virtuales de mi proceso; de esa manera, las lecturas y escrituras en el área de memoria mapeada irían al sistema de archivos flash local en su lugar, y se evitaría el asesino de OOM ya que si la memoria se agotara, Linux podría simplemente vaciar parte de la memoria mmap () páginas de vuelta al disco para liberar algo de RAM. (Eso podría hacer que mi programa sea lento, pero lento está bien para este caso de uso)

Sin embargo, incluso con la llamada mmap() , sigo viendo de vez en cuando que el OOM-killer mata procesos mientras realizo la operación anterior.

Mi pregunta es, ¿era demasiado optimista acerca de cómo se comportaría Linux en presencia de un gran mmap() y RAM limitada? (es decir, mmap()-ing un archivo de 200 MB y luego leer/escribir en la memoria de mmap() todavía requiere 200 MB de RAM disponible para lograr de manera confiable?) ¿O mmap() debería ser lo suficientemente inteligente como para paginar páginas mmap'd cuando la memoria es baja, pero estoy haciendo algo mal en la forma en que la uso?

FWIW mi código para hacer el mapeo está aquí:

 void FixedSizeDataBuffer :: TryMapToFile(const std::string & filePath, bool createIfNotPresent, bool autoDelete) { const int fd = open(filePath.c_str(), (createIfNotPresent?(O_CREAT|O_EXCL|O_RDWR):O_RDONLY)|O_CLOEXEC, S_IRUSR|(createIfNotPresent?S_IWUSR:0)); if (fd >= 0) { if ((autoDelete == false)||(unlink(filePath.c_str()) == 0)) // so the file will automatically go away when we're done with it, even if we crash { const int fallocRet = createIfNotPresent ? posix_fallocate(fd, 0, _numBytes) : 0; if (fallocRet == 0) { void * mappedArea = mmap(NULL, _numBytes, PROT_READ|(createIfNotPresent?PROT_WRITE:0), MAP_SHARED, fd, 0); if (mappedArea) { printf("FixedSizeDataBuffer %p: Using backing-store file [%s] for %zu bytes of data\n", this, filePath.c_str(), _numBytes); _buffer = (uint8_t *) mappedArea; _isMappedToFile = true; } else printf("FixedSizeDataBuffer %p: Unable to mmap backing-store file [%s] to %zu bytes (%s)\n", this, filePath.c_str(), _numBytes, strerror(errno)); } else printf("FixedSizeDataBuffer %p: Unable to pad backing-store file [%s] out to %zu bytes (%s)\n", this, filePath.c_str(), _numBytes, strerror(fallocRet)); } else printf("FixedSizeDataBuffer %p: Unable to unlink backing-store file [%s] (%s)\n", this, filePath.c_str(), strerror(errno)); close(fd); // no need to hold this anymore AFAIK, the memory-mapping itself will keep the backing store around } else printf("FixedSizeDataBuffer %p: Unable to create backing-store file [%s] (%s)\n", this, filePath.c_str(), strerror(errno)); }

Puedo reescribir este código para usar simple-viejo-archivo-I/O si es necesario, pero sería bueno si mmap() pudiera hacer el trabajo (o si no, al menos me gustaría entender por qué no ).

over 4 years ago · Santiago Trujillo
1 answers
Answer question

0

Después de mucha más experimentación, determiné que el OOM-killer me estaba visitando no porque el sistema se hubiera quedado sin RAM, sino porque la RAM ocasionalmente se fragmentaba lo suficiente como para que el kernel no pudiera encontrar un conjunto de páginas de RAM físicamente contiguas lo suficientemente grandes. para satisfacer sus necesidades inmediatas. Cuando esto sucedía, el kernel invocaba el OOM-killer para liberar algo de RAM y evitar un pánico en el kernel, lo cual está muy bien para el kernel, pero no es tan bueno cuando elimina un proceso en el que el usuario confiaba para obtener su trabajo hecho. :/

Después de intentar y fallar en encontrar una manera de convencer a Linux de que no hiciera eso (creo que habilitar una partición de intercambio evitaría el asesino de OOM, pero hacer eso no es una opción para mí en estas máquinas en particular), se me ocurrió un truco solución alterna; Agregué un código a mi programa que verifica periódicamente la cantidad de fragmentación de la memoria informada por el kernel de Linux, y si la fragmentación de la memoria comienza a verse demasiado severa, ordena preventivamente que ocurra una desfragmentación de la memoria, para que el OOM-killer (con suerte) no se haga necesario. Si el pase de desfragmentación de la memoria no parece estar mejorando las cosas, luego de 20 intentos consecutivos, también eliminamos la memoria caché de la página de VM como una forma de liberar RAM física contigua. Todo esto es muy feo, pero no tan feo como recibir una llamada telefónica a las 3 a.m. de un usuario que quiere saber por qué su programa de servidor se bloqueó. :/

La esencia de la implementación alternativa se encuentra a continuación; tenga en cuenta que se espera que DefragTick(Milliseconds) se llame periódicamente (preferiblemente una vez por segundo).

 // Returns how safe we are from the fragmentation-based-OOM-killer visits. // Returns -1 if we can't read the data for some reason. static int GetFragmentationSafetyLevel() { int ret = -1; FILE * fpIn = fopen("/sys/kernel/debug/extfrag/extfrag_index", "r"); if (fpIn) { char buf[512]; while(fgets(buf, sizeof(buf), fpIn)) { const char * dma = (strncmp(buf, "Node 0, zone", 12) == 0) ? strstr(buf+12, "DMA") : NULL; if (dma) { // dma= eg: "DMA -1.000 -1.000 -1.000 -1.000 0.852 0.926 0.963 0.982 0.991 0.996 0.998 0.999 1.000 1.000" const char * s = dma+4; // skip past "DMA "; ret = 0; // ret now becomes a count of "safe values in a row"; a safe value is any number less than 0.500, per me while((s)&&((*s == '-')||(*s == '.')||(isdigit(*s)))) { const float fVal = atof(s); if (fVal < 0.500f) { ret++; // Advance (s) to the next number in the list const char * space = strchr(s, ' '); // to the next space s = space ? (space+1) : NULL; } else break; // oops, a dangerous value! Run away! } } } fclose(fpIn); } return ret; } // should be called periodically (eg once per second) void DefragTick(Milliseconds current_time_in_milliseconds) { if ((current_time_in_milliseconds-m_last_fragmentation_check_time) >= Milliseconds(1000)) { m_last_fragmentation_check_time = current_time_in_milliseconds; const int fragmentationSafetyLevel = GetFragmentationSafetyLevel(); if (fragmentationSafetyLevel < 9) { m_defrag_pending = true; // trouble seems to start at level 8 m_fragged_count++; // note that we still seem fragmented } else m_fragged_count = 0; // we're in the clear! if ((m_defrag_pending)&&((current_time_in_milliseconds-m_last_defrag_time) >= Milliseconds(5000))) { if (m_fragged_count >= 20) { // FogBugz #17882 FILE * fpOut = fopen("/proc/sys/vm/drop_caches", "w"); if (fpOut) { const char * warningText = "Persistent Memory fragmentation detected -- dropping filesystem PageCache to improve defragmentation."; printf("%s (fragged count is %i)\n", warningText, m_fragged_count); fprintf(fpOut, "3"); fclose(fpOut); m_fragged_count = 0; } else { const char * errorText = "Couldn't open /proc/sys/vm/drop_caches to drop filesystem PageCache!"; printf("%s\n", errorText); } } FILE * fpOut = fopen("/proc/sys/vm/compact_memory", "w"); if (fpOut) { const char * warningText = "Memory fragmentation detected -- ordering a defragmentation to avoid the OOM-killer."; printf("%s (fragged count is %i)\n", warningText, m_fragged_count); fprintf(fpOut, "1"); fclose(fpOut); m_defrag_pending = false; m_last_defrag_time = current_time_in_milliseconds; } else { const char * errorText = "Couldn't open /proc/sys/vm/compact_memory to trigger a memory-defragmentation!"; printf("%s\n", errorText); } } } }
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!