もう 1 つの代替手段は、プロセス メモリ マッピングの詳細をダンプする pmap ツールです。
pmap [ -x | -d ] [ -q ] pids...
pmap -V
pmap は procps コレクションの一部です。
また、物理マッピングに興味がある場合は、最近の Linux カーネルで利用可能になったページマップを見て、プロセスに物理メモリ情報であることを知らせることができます。これは、ユーザー空間プロセスが DMA 宛先としてバッファーの物理アドレスを見つける必要があるユーザー空間ドライバーの開発に役立つ場合があります。
https://www.kernel.org/doc/Documentation/vm/pagemap.txt
Linux では、プロセス PID については、/proc/PID/maps
を参照してください。 と /proc/PID/smaps
疑似ファイル。 (プロセス自体は /proc/self/maps
を使用できます そして /proc/self/smaps
.)
それらの内容は man 5 proc に文書化されています。
アドレス範囲構造のリンクされたリストにコンテンツを読み込む方法の例を次に示します。
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;
}
上記を使用するサンプル プログラム 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;
}
と Makefile 構築を簡単にする:
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]
上記の Makefile の 3 つのインデントされた行はしなければならないことに注意してください。 スペースではなく、タブ文字を使用してください。ここのエディターはタブをスペースに変換するようです。たとえば、
を使用して修正する必要があります。sed -e 's|^ *|\t|' -i Makefile
インデントを修正せず、Makefile でスペースを使用すると、*** missing separator. Stop
のようなエラー メッセージが表示されます。 .
一部のエディターは tab を自動的に変換します キーをいくつかのスペースに押し込むため、使用するエディターのエディター設定を詳しく調べる必要がある場合があります。多くの場合、編集者は貼り付けられたタブ文字をそのまま保持するため、いつでも別のプログラムからタブを貼り付けることができます。
コンパイルして実行するには、上記のファイルを保存して実行します:
make
./example 0
サンプルプログラム自体で使用されるメモリ範囲を出力します。たとえば、PulseAudio デーモンが使用するメモリ範囲を確認したい場合は、次を実行します。
./example $(ps -o pid= -C pulseaudio)
標準のアクセス制限が適用されることに注意してください。通常のユーザーは、そのユーザーとして実行されているプロセスのメモリ範囲のみを表示できます。それ以外の場合は、スーパーユーザー権限が必要です (sudo
など)