2016-04-09 9 views
15

मैं सी में अपने प्रोग्राम का मेमोरी लेआउट देखना चाहता हूं ताकि मैं रन-टाइम के दौरान मेमोरी के सभी अलग-अलग हिस्सों को समझ सकूं जैसे कि बीएसएस या हेप में बदलाव के लिए?रन-टाइम के दौरान सी में मेरे प्रोग्राम के मेमोरी लेआउट को कैसे देखें?

+0

किसी भी विशेष मंच पर? – isedev

+0

@isedev: मुख्य रूप से लिनक्स –

+1

@ सुर्याप्रकाशपेटेल जीडीबी पर एक नज़र डालें। या अगर आप कुछ दृश्य चाहते हैं तो इडा का उपयोग करें। – fuz

उत्तर

17

लिनक्स में, प्रक्रिया पीआईडी ​​के लिए, /proc/PID/maps और /proc/PID/smaps छद्म जीवों को देखें। (प्रक्रिया ही /proc/self/maps और /proc/self/smaps उपयोग कर सकते हैं।)

उनकी सामग्री man 5 proc में दर्ज कर रहे हैं।


यहां एक उदाहरण दिया गया है कि आप पता श्रेणी संरचनाओं की एक लिंक की गई सूची में सामग्री को कैसे पढ़ सकते हैं।

मेम-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 */ 

मेम-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 में तीन दांतेदार लाइनों। ऐसा लगता है कि संपादक यहां रिक्त स्थान के लिए टैब बदल देता है, ताकि आप

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

का उपयोग कर आप खरोज ठीक नहीं है, और एक Makefile में रिक्तियों का उपयोग करके कि ठीक करने के लिए, उदाहरण के लिए, आप एक देखेंगे की जरूरत है *** missing separator. Stop के समान त्रुटि संदेश।

कुछ एडिटर स्वचालित रूप से रिक्त स्थान की एक संख्या में एक टैब कुंजी दबाने कनवर्ट करते हैं, तो आप जो कुछ भी संपादक आप उपयोग के संपादक सेटिंग्स में तल्लीन करना पड़ सकता है। अक्सर, संपादक चिपकाए गए टैब वर्ण को बरकरार रखते हैं, ताकि आप हमेशा किसी अन्य प्रोग्राम से टैब पेस्ट करने का प्रयास कर सकें।

संकलन करने के लिए और चलाने के लिए, ऊपर फ़ाइलों को सहेजने और

make 
./example 0 

चलाने उदाहरण कार्यक्रम में ही द्वारा प्रयुक्त स्मृति पर्वतमाला मुद्रित करने के लिए। आप देखना चाहते हैं, तो आपके पल्सऑडियो डेमॉन द्वारा प्रयुक्त स्मृति पर्वतमाला, चलाने

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

ध्यान दें कि मानक पहुंच प्रतिबंध लागू होते हैं, का कहना है। एक सामान्य उपयोगकर्ता केवल उस उपयोगकर्ता के रूप में चलने वाली प्रक्रियाओं की स्मृति श्रृंखला देख सकता है; अन्यथा आपको सुपरसुर विशेषाधिकारों की आवश्यकता है (sudo या इसी तरह)।

2

यदि आप लिनक्स पर स्थिर कोर डंप प्राप्त करने के लिए गोरकोर का उपयोग करते हैं, तो यह gdb का हिस्सा है ...

gcore $pid > Corefile 

या

gcore -o core_dump $pid 

डिबग करने के लिए चल रहे एक कार्यक्रम gdb

gdb -p 1234 

का उपयोग कर तो यह में चारों ओर से प्रहार इसे करने के लिए देते हैं। यह देखने के लिए कि यह कैसे बाहर

(gdb) maint info sections 
Exec file: 
    `/home/foo/program', file type elf32-i386. 
[0]  0x8048134->0x8048147 at 0x00000134: .interp ALLOC LOAD READONLY DATA HAS_CONTENTS 
[1]  0x8048148->0x8048168 at 0x00000148: .note.ABI-tag ALLOC LOAD READONLY DATA HAS_CONTENTS 
[2]  0x8048168->0x804818c at 0x00000168: .note.gnu.build-id ALLOC LOAD 
..... 
..... 
[23]  0x8049a40->0x8049ad1 at 0x00000a40: .data ALLOC LOAD DATA HAS_CONTENTS 
[24]  0x8049ad1->0x8049ad4 at 0x00000ad1: .bss ALLOC 

रखी है रजिस्टरों में चारों ओर से प्रहार करने के लिए उपयोग करें

(gdb) info all-registers 
eax   0xfffffdfc -516 
ecx   0x0 0 
edx   0x1 1 
ebx   0xffeedc28 -1123288 
esp   0xffeedc0c 0xffeedc0c 
ebp   0xffeedc78 0xffeedc78 
esi   0x1308 4872 
edi   0x45cf 17871 
.... snipped 

आप एक विशेष समारोह उपयोग disassemble के लिए इस्तेमाल किया विधानसभा देखना चाहते हैं। इसका उपयोग स्मृति में पते के साथ भी किया जा सकता है।

pmap [ -x | -d ] [ -q ] pids... 
    pmap -V 

pmap procps संग्रह का हिस्सा है:

(gdb) disassemble main 
Dump of assembler code for function main: 
    0x080483f0 <+0>: lea 0x4(%esp),%ecx 
    0x080483f4 <+4>: and $0xfffffff0,%esp 
    0x080483f7 <+7>: mov $0x8048780,%edx 
    0x080483fc <+12>: pushl -0x4(%ecx) 
    0x080483ff <+15>: push %ebp 
    0x08048400 <+16>: mov %esp,%ebp 
    .... 
    .... 
2

एक अन्य विकल्प pmap उपकरण है जो इस प्रक्रिया स्मृति मानचित्रण विवरण उदासीनता है।

यदि आप भौतिक मानचित्रण में रुचि रखते हैं, तो आप पेजमैप पर एक नज़र डाल सकते हैं, जिसे हाल ही में लिनक्स कर्नेल में उपलब्ध कराया गया है ताकि प्रक्रिया को इसकी भौतिक स्मृति जानकारी पता चल सके। यह उपयोगकर्ता अंतरिक्ष चालक विकास के लिए उपयोगी हो सकता है जहां उपयोगकर्ता अंतरिक्ष प्रक्रिया को बफर के भौतिक पते को डीएमए गंतव्य के रूप में ढूंढने की आवश्यकता होती है।

https://www.kernel.org/doc/Documentation/vm/pagemap.txt

संबंधित मुद्दे