GNU/Linux >> Tutoriales Linux >  >> Linux

¿Cómo ver el diseño de memoria de mi programa en C durante el tiempo de ejecución?

Otra alternativa es la herramienta pmap que volca los detalles del mapeo de la memoria del proceso:

    pmap [ -x | -d ] [ -q ] pids...
    pmap -V

pmap es parte de la colección procps.

Además, si está interesado en el mapeo físico, puede echar un vistazo al mapa de páginas, que está disponible en el kernel de Linux reciente para que el proceso sepa que es información de memoria física. Puede ser útil para el desarrollo de controladores de espacio de usuario donde el proceso de espacio de usuario necesita encontrar la dirección física de un búfer como destino DMA.

https://www.kernel.org/doc/Documentation/vm/pagemap.txt


En Linux, para el PID del proceso, mire /proc/PID/maps y /proc/PID/smaps pseudoarchivos. (El proceso en sí puede usar /proc/self/maps y /proc/self/smaps .)

Su contenido está documentado en man 5 proc.

Este es un ejemplo de cómo puede leer el contenido en una lista vinculada de estructuras de rango de direcciones.

mem-stats.h :

#ifndef   MEM_STATS_H
#define   MEM_STATS_H
#include <stdlib.h>
#include <sys/types.h>

#define PERMS_READ               1U
#define PERMS_WRITE              2U
#define PERMS_EXEC               4U
#define PERMS_SHARED             8U
#define PERMS_PRIVATE           16U

typedef struct address_range address_range;
struct address_range {
    struct address_range    *next;
    void                    *start;
    size_t                   length;
    unsigned long            offset;
    dev_t                    device;
    ino_t                    inode;
    unsigned char            perms;
    char                     name[];
};

address_range *mem_stats(pid_t);
void free_mem_stats(address_range *);

#endif /* MEM_STATS_H */

mem-stats.c :

#define _POSIX_C_SOURCE 200809L
#define _BSD_SOURCE
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "mem-stats.h"

void free_mem_stats(address_range *list)
{
    while (list) {
        address_range *curr = list;

        list = list->next;

        curr->next = NULL;
        curr->length = 0;
        curr->perms = 0U;
        curr->name[0] = '\0';

        free(curr);
    }
}

address_range *mem_stats(pid_t pid)
{
    address_range *list = NULL;
    char          *line = NULL;
    size_t         size = 0;
    FILE          *maps;

    if (pid > 0) {
        char namebuf[128];
        int  namelen;

        namelen = snprintf(namebuf, sizeof namebuf, "/proc/%ld/maps", (long)pid);
        if (namelen < 12) {
            errno = EINVAL;
            return NULL;
        }

        maps = fopen(namebuf, "r");
    } else
        maps = fopen("/proc/self/maps", "r");

    if (!maps)
        return NULL;

    while (getline(&line, &size, maps) > 0) {
        address_range *curr;
        char           perms[8];
        unsigned int   devmajor, devminor;
        unsigned long  addr_start, addr_end, offset, inode;
        int            name_start = 0;
        int            name_end = 0;

        if (sscanf(line, "%lx-%lx %7s %lx %u:%u %lu %n%*[^\n]%n",
                         &addr_start, &addr_end, perms, &offset,
                         &devmajor, &devminor, &inode,
                         &name_start, &name_end) < 7) {
            fclose(maps);
            free(line);
            free_mem_stats(list);
            errno = EIO;
            return NULL;
        }

        if (name_end <= name_start)
            name_start = name_end = 0;

        curr = malloc(sizeof (address_range) + (size_t)(name_end - name_start) + 1);
        if (!curr) {
            fclose(maps);
            free(line);
            free_mem_stats(list);
            errno = ENOMEM;
            return NULL;
        }

        if (name_end > name_start)
            memcpy(curr->name, line + name_start, name_end - name_start);
        curr->name[name_end - name_start] = '\0';

        curr->start = (void *)addr_start;
        curr->length = addr_end - addr_start;
        curr->offset = offset;
        curr->device = makedev(devmajor, devminor);
        curr->inode = (ino_t)inode;

        curr->perms = 0U;
        if (strchr(perms, 'r'))
            curr->perms |= PERMS_READ;
        if (strchr(perms, 'w'))
            curr->perms |= PERMS_WRITE;
        if (strchr(perms, 'x'))
            curr->perms |= PERMS_EXEC;
        if (strchr(perms, 's'))
            curr->perms |= PERMS_SHARED;
        if (strchr(perms, 'p'))
            curr->perms |= PERMS_PRIVATE;

        curr->next = list;
        list = curr;
    }

    free(line);

    if (!feof(maps) || ferror(maps)) {
        fclose(maps);
        free_mem_stats(list);
        errno = EIO;
        return NULL;
    }
    if (fclose(maps)) {
        free_mem_stats(list);
        errno = EIO;
        return NULL;
    }

    errno = 0;
    return list;
}

Un programa de ejemplo para usar el anterior, example.c :

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "mem-stats.h"

int main(int argc, char *argv[])
{
    int  arg, pid;
    char dummy;

    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s PID\n", argv[0]);
        fprintf(stderr, "\n");
        fprintf(stderr, "You can use PID 0 as an alias for the command itself.\n");
        fprintf(stderr, "\n");
        return EXIT_SUCCESS;
    }

    for (arg = 1; arg < argc; arg++)
        if (sscanf(argv[arg], " %i %c", &pid, &dummy) == 1) {
            address_range *list, *curr;

            if (!pid)
                pid = getpid();

            list = mem_stats((pid_t)pid);
            if (!list) {
                fprintf(stderr, "Cannot obtain memory usage of process %d: %s.\n", pid, strerror(errno));
                return EXIT_FAILURE;
            }

            printf("Process %d:\n", pid);
            for (curr = list; curr != NULL; curr = curr->next)
                printf("\t%p .. %p: %s\n", curr->start, (void *)((char *)curr->start + curr->length), curr->name);
            printf("\n");
            fflush(stdout);

            free_mem_stats(list);

        } else {
            fprintf(stderr, "%s: Invalid PID.\n", argv[arg]);
            return EXIT_FAILURE;
        }

    return EXIT_SUCCESS;
}

y un archivo Make para construirlo, simple:

CC      := gcc
CFLAGS  := -Wall -Wextra -O2 -fomit-frame-pointer
LDFLAGS := 
PROGS   := example

.PHONY: all clean

all: clean $(PROGS)

clean:
    rm -f *.o $(PROGS)

%.o: %.c
    $(CC) $(CFLAGS) -c $^

example: mem-stats.o example.o
    $(CC) $(CFLAGS) $^ $(LDFLAGS) -o [email protected]

Tenga en cuenta que las tres líneas sangradas en el Makefile anterior deben use caracteres de tabulación, no espacios. Parece que el editor aquí convierte tabulaciones en espacios, por lo que debe corregir eso, por ejemplo, usando

sed -e 's|^  *|\t|' -i Makefile

Si no corrige la sangría y usa espacios en un Makefile, verá un mensaje de error similar a *** missing separator. Stop .

Algunos editores convierten automáticamente una pestaña presione la tecla en varios espacios, por lo que es posible que deba profundizar en la configuración del editor de cualquier editor que use. A menudo, los editores mantienen intacto un carácter de tabulación pegado, por lo que siempre puede intentar pegar una tabulación de otro programa.

Para compilar y ejecutar, guarde los archivos anteriores y ejecute:

make
./example 0

para imprimir los rangos de memoria utilizados por el propio programa de ejemplo. Si desea ver, por ejemplo, los rangos de memoria utilizados por su demonio PulseAudio, ejecute:

./example $(ps -o pid= -C pulseaudio)

Tenga en cuenta que se aplican restricciones de acceso estándar. Un usuario normal solo puede ver los rangos de memoria de los procesos que se ejecutan como ese usuario; de lo contrario, necesita privilegios de superusuario (sudo o similar).


Linux
  1. Cómo borrar la caché de memoria en Linux

  2. ¿Cómo redirigir la salida de un programa a un archivo zip?

  3. ¿Cómo ver los principales procesos ordenados por el uso real de la memoria?

  4. Disposición de la memoria del programa en Linux

  5. ¿Cómo reducir el uso de memoria de ClamAV?

Cómo borrar la memoria de intercambio en Linux

Cómo deshabilitar permanentemente el intercambio en Linux

Cómo ver los usuarios registrados en Linux

¿Cómo funciona el intercambio de memoria en Linux?

Cómo:Introducción a la programación - Control de flujo

¿Cómo perfilar el uso de la memoria?