Откуда начинается пространство памяти процесса и где оно заканчивается? - PullRequest
5 голосов
/ 09 апреля 2010

На платформе Windows я пытаюсь сбросить память из моего приложения, где лежат переменные. Вот функция:


void MyDump(const void *m, unsigned int n)
{
        const unsigned char *p = reinterpret_cast<const unsigned char *>(m);
        char buffer[16];
        unsigned int mod = 0;

        for (unsigned int i = 0; i < n; ++i, ++mod) {
                if (mod % 16 == 0) {
                        mod = 0;

                        std::cout << " | ";

                        for (unsigned short j = 0; j < 16; ++j) {
                                switch (buffer[j]) {
                                        case 0xa:
                                        case 0xb:
                                        case 0xd:
                                        case 0xe:
                                        case 0xf:
                                                std::cout << " ";

                                                break;

                                        default: std::cout << buffer[j];
                                }
                        }

                        std::cout << "\n0x" << std::setfill('0') << std::setw(8) << std::hex << (long)i << " | ";
                 }

                buffer[i % 16] = p[i];

                std::cout << std::setw(2) << std::hex << static_cast<unsigned int>(p[i]) << " ";

                if (i % 4 == 0 && i != 1)
                        std::cout << " ";
        }
}

Теперь, как я могу узнать, с какого адреса начинается мое пространство памяти процесса, где хранятся все переменные? И как мне теперь, как долго это площадь?

Например:


MyDump(0x0000 /* <-- Starts from here? */, 0x1000 /* <-- This much? */);

С уважением,
nhaa123

Ответы [ 6 ]

7 голосов
/ 09 апреля 2010

Краткий ответ на этот вопрос: вы не можете подойти к этой проблеме таким образом. Способ размещения процессов в памяти сильно зависит от компилятора и операционной системы, и определить, где находится весь код и переменные, непросто. Чтобы точно и полностью найти все переменные, вам нужно написать большую часть отладчика самостоятельно (или позаимствовать их из кода реального отладчика).

Но, возможно, вы могли бы немного сузить сферу вашего вопроса. Если то, что вам действительно нужно, это просто трассировка стека, то генерировать ее не так сложно: Как можно получить трассировку стека в C?

Или, если вы хотите проверить сам стек, легко получить указатель на текущую вершину стека (просто объявите локальную переменную, а затем получите ее адрес). Самый простой способ получить дно стека - объявить переменную в main, сохранить ее адрес в глобальной переменной и использовать этот адрес позже как «bottom» (это легко, но не совсем «чисто»).

Получить картину кучи намного сложнее, потому что вам нужны обширные знания о внутренней работе кучи, чтобы знать, какие ее части выделены в данный момент. Так как размер кучи в основном «неограничен», это достаточно много данных для печати, если вы просто распечатываете все, даже неиспользованные части. Я не знаю, как это сделать, и я настоятельно рекомендую вам не тратить время на попытки.

Получение изображения статических глобальных переменных не так плохо, как куча, но и сложно. Они живут в сегментах данных исполняемого файла, и если вы не хотите входить в сборку и анализ исполняемых форматов, просто избегайте этого.

2 голосов
/ 09 апреля 2010

Обзор

То, что вы пытаетесь сделать, абсолютно возможно, и есть даже инструменты, которые помогут, но вам придется выполнять больше работы, чем я думаю, вы ожидаете.

В вашем случае вас особенно интересует "где лежат переменные". API кучи системы в Windows станет для вас невероятной помощью. Ссылка действительно неплохая, и хотя это не будет единый непрерывный регион, API скажет вам, где находятся ваши переменные.

В общем, хотя, ничего не зная о том, где находится ваша память, вам придется выполнить сканирование всего адресного пространства процесса. Если вам нужны только данные, вам также придется выполнить некоторую фильтрацию, потому что в них также присутствует бессмысленность кода и стека. Наконец, чтобы избежать сбоя при сегментировании во время выгрузки адресного пространства, вам может потребоваться добавить обработчик сигнала segfault, который позволяет пропускать не отображенную память во время выгрузки.

Схема памяти процесса

В рабочем процессе у вас будет несколько непересекающихся отрезков памяти для распечатки. Они будут включать:

  1. Скомпилированный код (только для чтения),
  2. данные стека (локальные переменные),
  3. Статические глобалы (например, из общих библиотек или в вашей программе) и
  4. Данные динамической кучи (все от malloc или new).

Ключом к разумному сбросу памяти является возможность определить, какой диапазон адресов принадлежит какому-либо семейству. Это ваша основная работа, когда вы сбрасываете программу. Некоторое из этого вы можете сделать, прочитав адреса функций (1) и переменных (2, 3 и 4), но если вы хотите напечатать больше, чем несколько вещей, вам понадобится некоторая помощь.

Для этого у нас есть ...

Полезные инструменты

Вместо того, чтобы просто слепо искать адресное пространство от 0 до 2 ^ 64 (которое, как мы все знаем, мучительно огромны), вы захотите использовать инструменты разработчика ОС и компилятора, чтобы сузить свой поиск. Кому-то там нужны эти инструменты, может даже больше, чем вам; это просто вопрос их поиска. Вот некоторые из них, о которых я знаю.

Отказ от ответственности: я не знаю многих эквивалентов Windows для многих из этих вещей, хотя я уверен, что они где-то существуют.

Я уже упоминал API кучи системы Windows . Это лучший сценарий для вас. Чем больше вещей вы сможете найти в этом ключе, тем точнее и проще будет ваша свалка. Действительно, операционная система и среда выполнения C довольно много знают о вашей программе. Это вопрос извлечения информации.

В Linux типы памяти 1 и 3 доступны через такие утилиты, как / proc / pid / maps. В / proc / pid / maps вы можете видеть диапазоны адресного пространства, зарезервированного для библиотек и программного кода. Вы также можете увидеть биты защиты; Например, диапазоны только для чтения - это, вероятно, код, а не данные.

Для советов по Windows Марк Руссинович написал несколько статей о том, как узнать адресное пространство процесса Windows и где хранятся разные вещи. Я полагаю, у него там могут быть хорошие указатели.

2 голосов
/ 09 апреля 2010

Ну, вы не можете, на самом деле ... по крайней мере, не в переносной манере. Для стека вы можете сделать что-то вроде:

void* ptr_to_start_of_stack = 0;
int main(int argc, char* argv[])
{
    int item_at_approximately_start_of_stack;
    ptr_to_start_of_stack = &item_at_approximately_start_of_stack;
    // ... 
    // ... do lots of computation
    // ... a  function called here can do something similar, and
    // ... attempt to print out from ptr_to_start_of_stack to its own
    // ... approximate start of stack
    // ... 
    return 0;
}

С точки зрения попытки взглянуть на диапазон кучи, во многих системах вы можете использовать функцию sbrk () (в частности, sbrk(0)), чтобы получить указатель на начало кучи (обычно он увеличивается вверх, начиная с конца адресного пространства, в то время как стек обычно уменьшается с начала адресного пространства).

Тем не менее, это действительно плохая идея . Мало того, что это зависит от платформы, но информация, которую вы можете получить от нее, на самом деле не так полезна, как хорошая регистрация. Предлагаю вам ознакомиться с Log4Cxx .

Хорошая практика ведения журнала, в дополнение к использованию отладчика, такого как GDB, действительно является лучшим способом. Попытка отладить вашу программу с помощью полного дампа памяти - это все равно, что найти иголку в стоге сена, и поэтому она не так полезна, как вы думаете. Ведение журнала, где логически может быть проблема, более полезно.

1 голос
/ 09 апреля 2010

Предполагается, что вы работаете в основной операционной системе. Вам понадобится помощь операционной системы, чтобы узнать, какие адреса в вашем виртуальном пространстве памяти отображены на страницах. Например, в Windows вы бы использовали VirtualQueryEx (). Получаемый дамп памяти может достигать двух гигабайт, вряд ли вы быстро обнаружите что-нибудь узнаваемое.

Ваш отладчик уже позволяет вам проверять память по произвольным адресам.

1 голос
/ 09 апреля 2010

AFAIK, это зависит от ОС, вы должны посмотреть, например, сегментация памяти.

0 голосов
/ 09 апреля 2010

Вы не можете, по крайней мере, не переносимо. И вы не можете делать много предположений.

Если вы не используете это на CP / M или MS-DOS.

Но с современными системами, где и как расположены ваши данные и код, в общем случае, на самом деле не ваше дело.

Вы можете играть в игры компоновщика и тому подобное, чтобы лучше контролировать карту памяти для вашего исполняемого файла, но вы не будете иметь никакого контроля, скажем, над общими библиотеками, которые вы можете загрузить, и т. Д.

Нет гарантии, что любой ваш код, например, находится даже в непрерывном пространстве. Виртуальная память и загрузчик могут размещать код практически в любом месте. Также нет никакой гарантии, что ваши данные находятся рядом с вашим кодом. На самом деле, нет никакой гарантии, что вы даже сможете ПРОЧИТАТЬ пространство памяти, где живет ваш код. (Выполнить, да. Читать, возможно, нет.)

На высоком уровне ваша программа разбита на 3 раздела: код, данные и стек. ОС размещает их там, где считает нужным, а диспетчер памяти контролирует, что и где вы можете видеть.

Есть все виды вещей, которые могут испачкать эти воды.

Тем не менее.

Если хочешь.

Вы можете попробовать использовать "маркеры" в своем коде. Например, поместите функцию в начале вашего файла с именем «startHere ()», а затем одну в конце с именем «endHere ()». Если вам повезет, для одной файловой программы у вас будет непрерывный кусочек кода между указателями на функции startHere и endHere.

То же самое со статическими данными. Вы можете попробовать ту же концепцию, если вам это вообще интересно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...