GNU/Linux >> Tutoriels Linux >  >> Linux

Comment voir la disposition de la mémoire de mon programme en C pendant l'exécution ?

Une autre alternative est l'outil pmap qui vide les détails du mappage de la mémoire du processus :

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

pmap fait partie de la collection procps.

De plus, si vous êtes intéressé par le mappage physique, vous pouvez jeter un œil au pagemap, qui est mis à disposition dans le noyau Linux récent pour faire savoir au processus qu'il s'agit d'informations sur la mémoire physique. Cela peut être utile pour le développement de pilotes d'espace utilisateur où le processus d'espace utilisateur doit trouver l'adresse physique d'un tampon en tant que destination DMA.

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


Sous Linux, pour le processus PID, regardez /proc/PID/maps et /proc/PID/smaps pseudofichiers. (Le processus lui-même peut utiliser /proc/self/maps et /proc/self/smaps .)

Leur contenu est documenté dans man 5 proc.

Voici un exemple de la façon dont vous pourriez lire le contenu dans une liste chaînée de structures de plages d'adresses.

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 exemple de programme pour utiliser ce qui précède, 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;
}

et un Makefile pour rendre la construction 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]

Notez que les trois lignes en retrait dans le Makefile ci-dessus doivent utilisez des tabulations et non des espaces. Il semble que l'éditeur ici convertisse les tabulations en espaces, vous devez donc corriger cela, par exemple en utilisant

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

Si vous ne corrigez pas l'indentation et utilisez des espaces dans un Makefile, vous verrez un message d'erreur similaire à *** missing separator. Stop .

Certains éditeurs convertissent automatiquement un onglet appuyez sur la touche dans un certain nombre d'espaces, vous devrez donc peut-être vous plonger dans les paramètres de l'éditeur de l'éditeur que vous utilisez. Souvent, les éditeurs conservent un caractère de tabulation collé intact, vous pouvez donc toujours essayer de coller une tabulation à partir d'un autre programme.

Pour compiler et exécuter, enregistrez les fichiers ci-dessus et exécutez :

make
./example 0

pour imprimer les plages de mémoire utilisées par le programme d'exemple lui-même. Si vous voulez voir, par exemple, les plages de mémoire utilisées par votre démon PulseAudio, exécutez :

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

Notez que des restrictions d'accès standard s'appliquent. Un utilisateur normal ne peut voir que les plages de mémoire des processus qui s'exécutent sous cet utilisateur ; sinon vous avez besoin des privilèges de superutilisateur (sudo ou similaire).


Linux
  1. Mesurer l'utilisation de RAM d'un programme ?

  2. Comment rediriger la sortie d'un programme vers un fichier Zip ? ?

  3. Comment les pipelines limitent-ils l'utilisation de la mémoire ?

  4. Comment voir les principaux processus triés par utilisation réelle de la mémoire ?

  5. Comment observer la bande passante mémoire ?

Comment effacer la mémoire d'échange sous Linux

Comment désactiver définitivement Swap sous Linux

Comment voir les utilisateurs connectés sous Linux

Comment fonctionne la mémoire d'échange sous Linux ?

Guide pratique :Introduction à la programmation - Contrôle de flux

Comment profiler l'utilisation de la mémoire ?