如何在运行时查看我的 C 程序的内存布局? [英] How to see memory layout of my program in C during run-time?

查看:11
本文介绍了如何在运行时查看我的 C 程序的内存布局?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在 C 中查看我的程序的内存布局,以便我可以在运行时了解内存的所有不同部分,例如 BSS 或堆中的变化?

I would like to see Memory layout of my program in C so that i can understand all the different segments of the Memory practically during run-time like change in BSS or Heap for ex ?

推荐答案

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

In Linux, for process PID, look at /proc/PID/maps and /proc/PID/smaps pseudofiles. (The process itself can use /proc/self/maps and /proc/self/smaps.)

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

Their contents are documented in man 5 proc.

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

Here's an example of how you might read the contents into a linked list of address range structures.

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] = '';

        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",
                         &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] = '';

        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:

An example program to use the above, 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, "
");
        fprintf(stderr, "Usage: %s [ -h | --help ]
", argv[0]);
        fprintf(stderr, "       %s PID
", argv[0]);
        fprintf(stderr, "
");
        fprintf(stderr, "You can use PID 0 as an alias for the command itself.
");
        fprintf(stderr, "
");
        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.
", pid, strerror(errno));
                return EXIT_FAILURE;
            }

            printf("Process %d:
", pid);
            for (curr = list; curr != NULL; curr = curr->next)
                printf("	%p .. %p: %s
", curr->start, (void *)((char *)curr->start + curr->length), curr->name);
            printf("
");
            fflush(stdout);

            free_mem_stats(list);

        } else {
            fprintf(stderr, "%s: Invalid PID.
", argv[arg]);
            return EXIT_FAILURE;
        }

    return EXIT_SUCCESS;
}

还有一个 Makefile 让构建变得简单:

and a Makefile to make building it, 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 $@

请注意,上述 Makefile 中的三个缩进行必须使用制表符,而不是空格.似乎这里的编辑器将制表符转换为空格,因此您需要修复它,例如使用

Note that the three indented lines in the Makefile above must use tab characters, not spaces. It seems that the editor here converts tabs to spaces, so you need to fix that, for example by using

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

如果您不修复缩进,并在 Makefile 中使用空格,您将看到类似于 *** 缺少分隔符的错误消息.停止.

If you don't fix the indentation, and use spaces in a Makefile, you'll see an error message similar to *** missing separator. Stop.

某些编辑器会自动将 tab 按键转换为多个空格,因此您可能需要深入研究您使用的任何编辑器的编辑器设置.通常,编辑器会保持粘贴的制表符不变,因此您可以随时尝试从其他程序粘贴制表符.

Some editors automatically convert a tab keypress into a number of spaces, so you may need to delve into the editor settings of whatever editor you use. Often, editors keep a pasted tab character intact, so you can always try pasting a tab from another program.

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

To compile and run, save the above files and run:

make
./example 0

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

to print the memory ranges used by the example program itself. If you want to see, say, the memory ranges used by your PulseAudio daemon, run:

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

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

Note that standard access restrictions apply. A normal user can only see the memory ranges of the processes that run as that user; otherwise you need superuser privileges (sudo or similar).

这篇关于如何在运行时查看我的 C 程序的内存布局?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆