GNU/Linux >> Linux Esercitazione >  >> Linux

Come vedere il layout della memoria del mio programma in C durante l'esecuzione?

Un'altra alternativa è lo strumento pmap che scarica i dettagli della mappatura della memoria del processo:

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

pmap fa parte della collezione procps.

Inoltre, se sei interessato alla mappatura fisica, puoi dare un'occhiata a pagemap, che è reso disponibile nel recente kernel Linux per far sapere al processo che si tratta di informazioni sulla memoria fisica. Potrebbe essere utile per lo sviluppo di driver in spazio utente in cui il processo in spazio utente deve trovare l'indirizzo fisico di un buffer come destinazione DMA.

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


In Linux, per il PID di processo, guarda /proc/PID/maps e /proc/PID/smaps pseudofile. (Il processo stesso può utilizzare /proc/self/maps e /proc/self/smaps .)

Il loro contenuto è documentato in man 5 proc.

Ecco un esempio di come potresti leggere i contenuti in un elenco collegato di strutture di intervalli di indirizzi.

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 programma di esempio per utilizzare quanto sopra, 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;
}

e un Makefile per costruirlo, semplice:

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]

Nota che le tre righe rientrate nel Makefile sopra devono usa i caratteri di tabulazione, non gli spazi. Sembra che l'editor qui converta le tabulazioni in spazi, quindi devi correggerlo, ad esempio usando

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

Se non correggi il rientro e usi gli spazi in un Makefile, vedrai un messaggio di errore simile a *** missing separator. Stop .

Alcuni editor convertono automaticamente una scheda keypress in un numero di spazi, quindi potrebbe essere necessario approfondire le impostazioni dell'editor di qualunque editor tu usi. Spesso gli editor mantengono intatto un carattere di tabulazione incollato, quindi puoi sempre provare a incollare una tabulazione da un altro programma.

Per compilare ed eseguire, salva i file precedenti ed esegui:

make
./example 0

per stampare gli intervalli di memoria utilizzati dal programma esempio stesso. Se vuoi vedere, ad esempio, gli intervalli di memoria utilizzati dal tuo demone PulseAudio, esegui:

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

Tieni presente che si applicano le restrizioni di accesso standard. Un utente normale può vedere solo gli intervalli di memoria dei processi eseguiti come quell'utente; altrimenti hai bisogno dei privilegi di superutente (sudo o simili).


Linux
  1. Come reindirizzare l'output di un programma su un file zip?

  2. Come visualizzare i processi principali ordinati in base all'utilizzo effettivo della memoria?

  3. Programma Python che consuma RAM

  4. Come cambiare il layout della tastiera in i3?

  5. Come posso osservare la larghezza di banda della memoria?

Come cancellare la memoria di scambio in Linux

Come disabilitare permanentemente lo scambio in Linux

Come vedere gli utenti che hanno effettuato l'accesso in Linux

Come funziona la memoria di scambio in Linux?

Procedura:Introduzione alla programmazione – Controllo del flusso

Come profilare l'utilizzo della memoria?