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