¿Cómo obtener el uso de la memoria en tiempo de ejecución usando C ++?

90

Necesito obtener el uso de mem VIRT y RES en tiempo de ejecución de mi programa y mostrarlos.

Lo que intenté hasta ahora:

getrusage ( http://linux.die.net/man/2/getrusage )

int who = RUSAGE_SELF; 
struct rusage usage; 
int ret; 

ret=getrusage(who,&usage);

cout<<usage.ru_maxrss;

pero siempre obtengo 0.

jww
fuente
3
Esto depende del sistema; parece que su sistema no admite informes de maxrss a través de getrusage. ¿Puede decirnos qué distribución está utilizando?
tvanfosson

Respuestas:

79

En Linux, nunca encontré una solución ioctl () . Para nuestras aplicaciones, codificamos una rutina de utilidad general basada en la lectura de archivos en / proc / pid . Hay varios de estos archivos que dan resultados diferentes. Aquí está el que decidimos (la pregunta estaba etiquetada como C ++, y manejamos la E / S usando construcciones de C ++, pero debería ser fácilmente adaptable a las rutinas de C i / o si es necesario):

#include <unistd.h>
#include <ios>
#include <iostream>
#include <fstream>
#include <string>

//////////////////////////////////////////////////////////////////////////////
//
// process_mem_usage(double &, double &) - takes two doubles by reference,
// attempts to read the system-dependent data for a process' virtual memory
// size and resident set size, and return the results in KB.
//
// On failure, returns 0.0, 0.0

void process_mem_usage(double& vm_usage, double& resident_set)
{
   using std::ios_base;
   using std::ifstream;
   using std::string;

   vm_usage     = 0.0;
   resident_set = 0.0;

   // 'file' stat seems to give the most reliable results
   //
   ifstream stat_stream("/proc/self/stat",ios_base::in);

   // dummy vars for leading entries in stat that we don't care about
   //
   string pid, comm, state, ppid, pgrp, session, tty_nr;
   string tpgid, flags, minflt, cminflt, majflt, cmajflt;
   string utime, stime, cutime, cstime, priority, nice;
   string O, itrealvalue, starttime;

   // the two fields we want
   //
   unsigned long vsize;
   long rss;

   stat_stream >> pid >> comm >> state >> ppid >> pgrp >> session >> tty_nr
               >> tpgid >> flags >> minflt >> cminflt >> majflt >> cmajflt
               >> utime >> stime >> cutime >> cstime >> priority >> nice
               >> O >> itrealvalue >> starttime >> vsize >> rss; // don't care about the rest

   stat_stream.close();

   long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages
   vm_usage     = vsize / 1024.0;
   resident_set = rss * page_size_kb;
}

int main()
{
   using std::cout;
   using std::endl;

   double vm, rss;
   process_mem_usage(vm, rss);
   cout << "VM: " << vm << "; RSS: " << rss << endl;
}
Don Wakefield
fuente
¿Tiene alguna garantía sobre la estructura / proc / self / stat en diferentes plataformas * nix? ... No estoy seguro, pero si es así, estará bien.
bayda
Bueno, a lo largo de los años he usado principalmente Solaris, HP-UX y Linux. / proc / self / stat parece ser un Linux-ismo. La versión original del programa anterior tenía bloques #if para Solaris ya que difería.
Don Wakefield
Supongo que el OP solo se preocupa por Linux en función del etiquetado de preguntas. Reading / proc será tan bueno como puedas. En Solaris también puede obtener información sobre todo tipo de cosas a través de kstat (aunque a menudo replica lo que puede obtener por otros medios).
stsquad
Llego solo 10 años tarde a la fiesta, pero ¿le importaría decirme por qué divide vsize entre 1024.0 en lugar de 1024?
a_river_in_canada
1
re: why 1024.0?- Le dice al compilador que convierta a doble PRIMERO y luego haga la división para obtener el resultado doble. La otra opción: vm_usage = vsize / 1024;haría la división primero (perdiendo precisión como sugirió @DonWakefield) y luego convertiría a doble.
Jesse Chisholm
52

David Robert Nadeau ha puesto una buena función C multiplataforma autónoma para obtener el tamaño del conjunto residente del proceso (uso de memoria física) en su sitio web:

/*
 * Author:  David Robert Nadeau
 * Site:    http://NadeauSoftware.com/
 * License: Creative Commons Attribution 3.0 Unported License
 *          http://creativecommons.org/licenses/by/3.0/deed.en_US
 */

#if defined(_WIN32)
#include <windows.h>
#include <psapi.h>

#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#include <sys/resource.h>

#if defined(__APPLE__) && defined(__MACH__)
#include <mach/mach.h>

#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
#include <fcntl.h>
#include <procfs.h>

#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
#include <stdio.h>

#endif

#else
#error "Cannot define getPeakRSS( ) or getCurrentRSS( ) for an unknown OS."
#endif





/**
 * Returns the peak (maximum so far) resident set size (physical
 * memory use) measured in bytes, or zero if the value cannot be
 * determined on this OS.
 */
size_t getPeakRSS( )
{
#if defined(_WIN32)
    /* Windows -------------------------------------------------- */
    PROCESS_MEMORY_COUNTERS info;
    GetProcessMemoryInfo( GetCurrentProcess( ), &info, sizeof(info) );
    return (size_t)info.PeakWorkingSetSize;

#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
    /* AIX and Solaris ------------------------------------------ */
    struct psinfo psinfo;
    int fd = -1;
    if ( (fd = open( "/proc/self/psinfo", O_RDONLY )) == -1 )
        return (size_t)0L;      /* Can't open? */
    if ( read( fd, &psinfo, sizeof(psinfo) ) != sizeof(psinfo) )
    {
        close( fd );
        return (size_t)0L;      /* Can't read? */
    }
    close( fd );
    return (size_t)(psinfo.pr_rssize * 1024L);

#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
    /* BSD, Linux, and OSX -------------------------------------- */
    struct rusage rusage;
    getrusage( RUSAGE_SELF, &rusage );
#if defined(__APPLE__) && defined(__MACH__)
    return (size_t)rusage.ru_maxrss;
#else
    return (size_t)(rusage.ru_maxrss * 1024L);
#endif

#else
    /* Unknown OS ----------------------------------------------- */
    return (size_t)0L;          /* Unsupported. */
#endif
}





/**
 * Returns the current resident set size (physical memory use) measured
 * in bytes, or zero if the value cannot be determined on this OS.
 */
size_t getCurrentRSS( )
{
#if defined(_WIN32)
    /* Windows -------------------------------------------------- */
    PROCESS_MEMORY_COUNTERS info;
    GetProcessMemoryInfo( GetCurrentProcess( ), &info, sizeof(info) );
    return (size_t)info.WorkingSetSize;

#elif defined(__APPLE__) && defined(__MACH__)
    /* OSX ------------------------------------------------------ */
    struct mach_task_basic_info info;
    mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
    if ( task_info( mach_task_self( ), MACH_TASK_BASIC_INFO,
        (task_info_t)&info, &infoCount ) != KERN_SUCCESS )
        return (size_t)0L;      /* Can't access? */
    return (size_t)info.resident_size;

#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
    /* Linux ---------------------------------------------------- */
    long rss = 0L;
    FILE* fp = NULL;
    if ( (fp = fopen( "/proc/self/statm", "r" )) == NULL )
        return (size_t)0L;      /* Can't open? */
    if ( fscanf( fp, "%*s%ld", &rss ) != 1 )
    {
        fclose( fp );
        return (size_t)0L;      /* Can't read? */
    }
    fclose( fp );
    return (size_t)rss * (size_t)sysconf( _SC_PAGESIZE);

#else
    /* AIX, BSD, Solaris, and Unknown OS ------------------------ */
    return (size_t)0L;          /* Unsupported. */
#endif
}

Uso

size_t currentSize = getCurrentRSS( );
size_t peakSize    = getPeakRSS( );

Para obtener más información, consulte el sitio web, también proporciona una función para obtener el tamaño de la memoria física de un sistema .

pepper_chico
fuente
2
Será mejor que amplíe #pragma comment(lib, "psapi.lib")el #if defined(_WIN32)alcance.
Bloodmoon
1
@Bloodmon ¿y si alguien está usando Windows pero no un compilador de Microsoft? Ese pragma haría fallar el compilador.
Adrian
Este código usa rusage :: ru_maxrss de getrusage, que el OP informó que no funciona para ella.
facetus
21

Antiguo:

maxrss indica la memoria máxima disponible para el proceso. 0 significa que no se impone ningún límite al proceso. Lo que probablemente desee es el uso de datos no compartidos ru_idrss.

Nuevo: Parece que lo anterior en realidad no funciona, ya que el kernel no llena la mayoría de los valores. Lo que sí funciona es obtener la información de proc. Sin embargo, en lugar de analizarlo uno mismo, es más fácil usar libproc (parte de procps) de la siguiente manera:

// getrusage.c
#include <stdio.h>
#include <proc/readproc.h>

int main() {
  struct proc_t usage;
  look_up_our_self(&usage);
  printf("usage: %lu\n", usage.vsize);
}

Compilar con " gcc -o getrusage getrusage.c -lproc"

Paul de Vrieze
fuente
1
Excepto que ninguno de los campos está disponible en Linux.
jmanning2k
2
Esto es incorrecto. maxrss es el uso máximo de memoria del proceso, no el máximo disponible, que sería getrlimit (RLIMIT_DATA, & rl).
jmanning2k
1
La #include <proc/readproc.h>solución funcionó muy bien para mí en Ubuntu. Tuve que instalar el paquete libproc-dev. usage.vm_dataes una aproximación lo suficientemente cercana a lo que necesitaba. Su elección de estadística de memoria se documenta aquí: /usr/include/proc/readproc.hLas que probé parecen estar en bytes, no en páginas. No creo que mi proceso usara 46 millones de páginas. Los comentarios de que esta solución no funciona en Linux parecen equivocados.
Allan Stokes
2
El enlazador correcto es: -lprocps
Sembiance
Funciona muy bien, ¡esta debería ser la respuesta aceptada!
Pekov
8

Una forma más elegante para el método Don Wakefield:

#include <iostream>
#include <fstream>

using namespace std;

int main(){

    int tSize = 0, resident = 0, share = 0;
    ifstream buffer("/proc/self/statm");
    buffer >> tSize >> resident >> share;
    buffer.close();

    long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages
    double rss = resident * page_size_kb;
    cout << "RSS - " << rss << " kB\n";

    double shared_mem = share * page_size_kb;
    cout << "Shared Memory - " << shared_mem << " kB\n";

    cout << "Private Memory - " << rss - shared_mem << "kB\n";
    return 0;
}
Qsiris
fuente
7

Las respuestas existentes son mejores sobre cómo obtener el valor correcto, pero al menos puedo explicar por qué getrusage no funciona para usted.

hombre 2 getrusage:

La estructura anterior [rusage] se tomó de BSD 4.3 Reno. No todos los campos son significativos en Linux. En este momento (Linux 2.4, 2.6) solo se mantienen los campos ru_utime, ru_stime, ru_minflt, ru_majflt y ru_nswap.

jmanning2k
fuente
3

Además de su forma
, puede llamar al comando system ps y obtener el uso de memoria de su salida.
o leer información de / proc / pid (ver estructura PIOCPSINFO)

bayda
fuente
PIOCPSINFO no está realmente disponible en ningún Linux que haya usado. Leer desde / proc / pid es bastante común. Publicaré código de ejemplo para Linux en una respuesta ...
Don Wakefield
yes Las estructuras / proc / pid pueden ser diferentes en diferentes plataformas * nix, pero si tiene PIOCPSINFO, no importa. He tenido una situación en la que esta estructura no estaba definida en alguna versión de Solaris. He usado la salida ps en este caso.
bayda
2

En su sistema hay un archivo llamado /proc/self/statm. El sistema de archivos proc es un pseudo-sistema de archivos que proporciona una interfaz para las estructuras de datos del kernel. Este archivo contiene la información que necesita en columnas con solo números enteros separados por espacios.

No de columna:

  1. = tamaño total del programa (VmSize en / proc / [pid] / status)

  2. = tamaño del conjunto residente (VmRSS en / proc / [pid] / status)

Para obtener más información, consulte el ENLACE .

Jakub Krawczuk
fuente
1

Estoy usando otra forma de hacerlo y suena realista. Lo que hago es obtener el PID del proceso mediante la función getpid () y luego uso el archivo / proc / pid / stat. Creo que la columna 23 del archivo de estadísticas es vmsize (mira la publicación de Don). Puede leer el vmsize del archivo donde lo necesite en el código. En caso de que se pregunte cuánto puede usar la memoria un fragmento de código, puede leer ese archivo una vez antes de ese fragmento y una vez después y puede restarlos entre sí.


fuente
1

Basado en la solución de Don W, con menos variables.

void process_mem_usage(double& vm_usage, double& resident_set)
{
    vm_usage     = 0.0;
    resident_set = 0.0;

    // the two fields we want
    unsigned long vsize;
    long rss;
    {
        std::string ignore;
        std::ifstream ifs("/proc/self/stat", std::ios_base::in);
        ifs >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore
                >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore
                >> ignore >> ignore >> vsize >> rss;
    }

    long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages
    vm_usage = vsize / 1024.0;
    resident_set = rss * page_size_kb;
}
ϹοδεMεδιϲ
fuente
0

Estaba buscando una aplicación de Linux para medir la memoria máxima utilizada. valgrind es una herramienta excelente, pero me estaba dando más información de la que quería. La época parecía ser la mejor herramienta que pude encontrar. Mide el uso de memoria "de aguas altas" (RSS y virtual). Vea esta respuesta .

jtpereyda
fuente