如何在运行时查看我的程序在C中的内存布局?

我希望看到用C语言编写的程序的内存布局,以便我可以在运行时实际理解内存的所有不同部分,例如BSS或Heap for ex?中的更改。

回答:

在Linux中,对于进程PID,请查看/proc/PID/maps/proc/PID/smaps伪文件。(该过程本身可以使用/proc/self/maps/proc/self/smaps。)

它们的内容记录在man 5 proc中。


这是一个如何将内容读入地址范围结构的链接列表的示例。

#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 */

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

}

使用以上示例的示例程序 :

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

}

和一个使构建起来很简单的 :

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 $@

请注意,上面的Makefile中的三行缩进行 使用制表符,而不是空格。似乎这里的编辑器将制表符转换为空格,因此您需要修复此问题,例如通过使用

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

如果您不解决缩进问题,而在Makefile中使用空格,则会看到类似于的错误消息*** missing separator. Stop

某些编辑器会自动将tab按键转换为多个空格,因此您可能需要研究使用的任何编辑器的编辑器设置。通常,编辑者会完整保留粘贴的制表符,因此您始终可以尝试从另一个程序粘贴制表符。

要编译并运行,请保存以上文件并运行:

make

./example 0

打印示例程序本身使用的内存范围。例如,如果您想查看PulseAudio守护程序使用的内存范围,请运行:

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

请注意,适用标准访问限制。普通用户只能看到以该用户身份运行的进程的内存范围。否则,您需要超级用户权限(sudo或类似权限)。

以上是 如何在运行时查看我的程序在C中的内存布局? 的全部内容, 来源链接: utcz.com/qa/419181.html

回到顶部