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).