GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Wie kann ich das Speicherlayout meines Programms in C während der Laufzeit sehen?

Eine weitere Alternative ist das pmap-Tool, das die Details der Prozessspeicherzuordnung ausgibt:

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

pmap ist Teil der procps-Sammlung.

Wenn Sie an physischer Zuordnung interessiert sind, können Sie sich auch pagemap ansehen, die im aktuellen Linux-Kernel verfügbar gemacht wird, um den Prozess über die Informationen zum physischen Speicher zu informieren. Es könnte für die Entwicklung von User-Space-Treibern nützlich sein, wo User-Space-Prozesse die physikalische Adresse eines Puffers als DMA-Ziel finden müssen.

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


Sehen Sie sich unter Linux für die Prozess-PID /proc/PID/maps an und /proc/PID/smaps Pseudodateien. (Der Prozess selbst kann /proc/self/maps verwenden und /proc/self/smaps .)

Ihr Inhalt ist in man 5 proc dokumentiert.

Hier ist ein Beispiel dafür, wie Sie den Inhalt in eine verknüpfte Liste von Adressbereichsstrukturen einlesen könnten.

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;
}

Ein Beispielprogramm, um das obige zu verwenden, 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;
}

und ein Makefile um es einfach zu machen:

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]

Beachten Sie, dass die drei eingerückten Zeilen im obigen Makefile müssen Verwenden Sie Tabulatorzeichen, keine Leerzeichen. Es scheint, dass der Editor hier Tabulatoren in Leerzeichen umwandelt, also müssen Sie das beheben, zum Beispiel mit

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

Wenn Sie die Einrückung nicht korrigieren und Leerzeichen in einem Makefile verwenden, sehen Sie eine Fehlermeldung ähnlich der von *** missing separator. Stop .

Einige Editoren konvertieren automatisch einen Tab Tastendruck in eine Reihe von Leerzeichen, daher müssen Sie sich möglicherweise mit den Editoreinstellungen des von Ihnen verwendeten Editors befassen. Oft behalten Editoren ein eingefügtes Tabulatorzeichen bei, sodass Sie jederzeit versuchen können, ein Tabulatorzeichen aus einem anderen Programm einzufügen.

Speichern Sie zum Kompilieren und Ausführen die obigen Dateien und führen Sie Folgendes aus:

make
./example 0

um die vom Beispielprogramm selbst verwendeten Speicherbereiche auszudrucken. Wenn Sie beispielsweise die von Ihrem PulseAudio-Daemon verwendeten Speicherbereiche sehen möchten, führen Sie Folgendes aus:

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

Beachten Sie, dass standardmäßige Zugriffsbeschränkungen gelten. Ein normaler Benutzer kann nur die Speicherbereiche der Prozesse sehen, die unter diesem Benutzer ausgeführt werden; andernfalls benötigen Sie Superuser-Rechte (sudo oder ähnliches).


Linux
  1. Messung der RAM-Nutzung eines Programms?

  2. Wie leite ich die Ausgabe eines Programms in eine Zip-Datei um?

  3. Wie kann ich die Top-Prozesse nach tatsächlicher Speichernutzung sortiert anzeigen?

  4. Python-Programm frisst RAM

  5. Wie kann ich die Speicherbandbreite beobachten?

So löschen Sie den Auslagerungsspeicher unter Linux

So deaktivieren Sie Swap dauerhaft unter Linux

So sehen Sie angemeldete Benutzer in Linux

Wie funktioniert Swap-Speicher in Linux?

Gewusst wie:Einführung in die Programmierung – Flusssteuerung

Wie kann ich die Speichernutzung profilieren?