Další alternativou je nástroj pmap, který vypíše podrobnosti o mapování paměti procesu:
pmap [ -x | -d ] [ -q ] pids...
pmap -V
pmap je součástí kolekce procps.
Pokud máte zájem o fyzické mapování, můžete se podívat na mapu stránek, která je k dispozici v nedávném jádře Linuxu, aby proces věděl, že jde o informace o fyzické paměti. Může to být užitečné pro vývoj ovladačů uživatelského prostoru, kde proces uživatelského prostoru potřebuje najít fyzickou adresu vyrovnávací paměti jako cíl DMA.
https://www.kernel.org/doc/Documentation/vm/pagemap.txt
V Linuxu najdete PID procesu v /proc/PID/maps
a /proc/PID/smaps
pseudosoubory. (Samotný proces může používat /proc/self/maps
a /proc/self/smaps
.)
Jejich obsah je dokumentován v man 5 proc.
Zde je příklad toho, jak můžete načíst obsah do propojeného seznamu struktur rozsahu adres.
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;
}
Příklad programu k použití výše uvedeného, 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;
}
a Makefile aby bylo sestavení jednoduché:
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]
Všimněte si, že tři odsazené řádky v Makefile výše musí používejte znaky tabulátoru, nikoli mezery. Zdá se, že editor zde převádí tabulátory na mezery, takže to musíte opravit, například pomocí
sed -e 's|^ *|\t|' -i Makefile
Pokud odsazení neopravíte a použijete mezery v Makefile, zobrazí se chybová zpráva podobná *** missing separator. Stop
.
Některé editory automaticky převádějí kartu stiskněte klávesu do několika mezer, takže se možná budete muset ponořit do nastavení editoru jakéhokoli editoru, který používáte. Editoři často ponechávají vložený znak tabulátoru nedotčený, takže můžete vždy zkusit vložit tabulátor z jiného programu.
Chcete-li zkompilovat a spustit, uložte výše uvedené soubory a spusťte:
make
./example 0
vytisknout rozsahy paměti používané samotným ukázkovým programem. Pokud chcete vidět, řekněme, rozsahy paměti používané vaším démonem PulseAudio, spusťte:
./example $(ps -o pid= -C pulseaudio)
Pamatujte, že platí standardní omezení přístupu. Normální uživatel může vidět pouze rozsahy paměti procesů, které běží jako tento uživatel; jinak potřebujete oprávnění superuživatele (sudo
nebo podobně).