netlib.narod.ru | < Назад | Оглавление | Далее > |
При определенных обстоятельствах, например, когда сбойная программа пытается обратиться к памяти за пределами отведенного ей пространства памяти, процесс может выполнить дамп памяти. Так называемый «дамп памяти» сводится к записи образа пространства памяти процесса, наряду с некоторой идентификационной информацией о самом приложении и о его состоянии, в файл для дальнейшего изучения с применением отладчика типа gdb. (В именах этих функций слово «core» является устаревшим синонимом слова «memory» — память.)
Безусловно, в вашем коде никогда не возникают подобные ошибки, но поскольку они могут появиться у менее талантливого программиста из соседнего отдела, и он придет к вам за помощью, рассмотрим эту тему.
Различные двоичные обработчики выполняют дамп памяти по-разному. (Двоичные обработчики описаны в главе 7.) Наиболее распространенным двоичным форматом Linux является ELF, поэтому рассмотрим, как двоичный обработчик ELF выполняет дамп памяти.
8748: Функция elf_core_dump начинает свою работу здесь. Поскольку дамп памяти процесса является результатом получения сигнала (который он мог послать сам себе, например, путем вызова функции abort), номер сигнала представлен параметром signr. Значение signr не влияет на то, как будет выполнен дамп памяти процесса и будет ли он выполнен вообще, но пользователь, рассматривая файл дампа в отладчике, сможет узнать, какой сигнал вызвал дамп памяти, и получить подсказку о том, что пошло не так, как надо. Параметр regs указывает на объект struct pt_regs (см. строку 11546), который содержит описание регистров процессора. Кроме других причин, параметр regs важен, поскольку он включает содержимое регистра EIP — указателя команды, который определяет, какая команда выполнялась при получении сигнала.
8771: Немедленно возвращает управление, если процесс не смог пройти некоторые основные проверки допустимости, первой из которых является проверка наличия установленного флажка dumpable. Обычно флажок dumpable процесса (строка 16359) установлен; он, как правило, очищается при смене идентификаторов пользователя или группы процесса. Это, по-видимому, является мерой защиты. Вряд ли можно допустить создание доступного для чтения файла дампа недоступного для чтения выполняемого модуля, для которого, например, была выполнена команда setuid root, поскольку это противоречит цели применения недоступного для чтения выполняемого модуля (защита).
Функция elf_core_dump также немедленно возвращает управление, если предел размера файла дампа не позволяет вывести в дамп ни одной страницы или если другие потоки ссылаются на память, которую она должна выгрузить. Выполнение дампа памяти связано с выходом из процесса, а с точки зрения пользователя, процесс еще существует, если продолжает существовать какой-либо из его потоков.
Если процесс прошел эти проверки, функция elf_core_dump продолжает действовать и очищает бит dumpable с тем, чтобы ей не пришлось предпринять попытку снова выполнить дамп памяти процесса. (Однако ничто не говорит о том, что это может случиться; по мнению автора, это просто ненужная мера предосторожности.)
8785: Входит в цикл для подсчета числа областей VMA, для которых может быть выполнен дамп без превышения размера файла дампа. Хотя функция elf_core_dump хранит счетчик в переменной под названием segs, она подсчитывает не «сегменты памяти» в той трактовке, которую мы используем в этой главе. Не следует думать, что в имени этой переменной есть какой-то особый смысл. Поскольку функция elf_core_dump записывает некоторую информацию заголовка в файл дампа перед дампом областей VMA и поскольку размер этих заголовков не учитывается в расчетах, вывод может немного превышать предел размера файла дампа. Это можно было бы легко исправить: простой метод состоял бы в уменьшении предела по мере записи заголовков и переноса цикла за пределы кода, в котором выполняется запись заголовков. Более полное решение было бы немного сложнее.
8805: Формат файла дампа ELF определен в соответствии с официальным стандартом; его первым компонентом является заголовок с описанием файла. Формат заголовка определен в соответствии с типом struct elfhdr (см. строки 14726 и 14541) и функция elf_core_dump заполняет локальную переменную elf этого типа.
8827: Устанавливает имя файла, в который должен быть выполнен дамп, и пытается открыть этот файл. Изменив значение #if 0 в строке 8828 на #if 1, мы могли бы предусмотреть включение в имена файлов дампа имен выполняемых модулей, которые привели к их созданию (или, по крайней мере, первых 16 символов этих имен — см. член comm объекта struct task_struct, который определен в строке 16406). Это средство иногда может стать чрезвычайно полезным; было бы прекрасно иметь возможность взглянуть на имя файла дампа и сразу же узнать, какое приложение явилось причиной его создания. Однако такое поведение является нестандартным и может нарушить работу существующего кода, например, работу контрольных сценариев, которые периодически проверяют наличие файла с именем «core», поэтому следует придерживаться стандартной практики и вместо этого называть эти файлы просто «core». Однако было бы неплохо, если бы этот параметр стал настраиваемым параметром ядра. Отметим, что эта версия дальнейшего развития разработок позволяет понять, почему таким внешне необычным способом в строке 8756 определена локальная переменная corefile.
8853: Устанавливает флажок PF_DUMPCORE (строка 16448), сигнализирующий о том, что процесс выполняет дамп памяти. Этот флажок не используется в коде, рассматриваемом в данной книге, но просто для вашего сведения отметим, что он применяется в учете процессов. Учет процессов предусматривает слежение за использованием ресурсов процессом и сбор другой соответствующей информации, включая то, выполнял ли процесс дамп памяти после выхода; именно эта информация первоначально использовалась в вычислительных центрах, позволяя определить, какую сумму нужно предъявить к оплате каждому отделу или пользователю за потребление ресурсов. Разве не замечательно, что эти дни уже давно позади?
8855: Выполняется запись заголовка файла дампа ELF, который был подготовлен ранее. В этом участвуют некоторые скрытые переключатели управления: макрокоманда DUMP_WRITE, чье определение начинается в строке 8707, заставляет функцию elf_core_dump закрыть файл и вернуть управление, если запись окончилась неудачей.
8862: За заголовком файла дампа ELF следует ряд заметок; каждая из них имеет определенное назначение и регистрирует конкретную информацию о процессе. Мы по очереди рассмотрим каждую из этих заметок. Заметка (с типом struct memelfnote, строка 8666) содержит указатель на дополнительные данные (ее член data) и длину данных (ее член datasz; основной объем работы по заполнению заметки фактически состоит в заполнении структуры с дополнительными данными и записи в заметке указателя на эту структуру.
Некоторая информация записывается в нескольких заметках. Причины этого повторения в коде не объясняются, хотя, по крайней мере, эти причины отчасти связаны с имитацией поведения других вариантов Unix. Поддержка форматов файлов, совместимых с другими платформами, помогает переносить в Linux такие программы, как gdb; лучше немного потратить места на повторную запись данных, чем отложить перенос программ и усложнить сопровождение столь важных инструментальных средств.
8865: В заметке 0 регистрируется наследие процесса, сигналы и загрузка процессора, во вспомогательной структуре данных prstatus (типа struct elf_prstatus; см. строку 14774). Заметим, в частности, что функция elf_core_dump хранит номер сигнала, который заставил процесс выполнить дамп ядра, в строке 8869. Поэтому если вы (или, скорее, не столь талантливый программист из соседнего отдела) выполните gdb на файле дампа и получите сообщение «Program terminated with signal 11, Segmentation fault», вы будете знать, откуда исходила эта информация.
8916: В заметке 1 регистрируется общая информация процесса — его владелец, состояние, приоритет и так далее — во вспомогательной структуре данных psinfo (типа struct elf_prpsinfo; см. строку 14813). Строка 8922 содержит в высшей степени непригодный, хотя и правильный, индекс массива в строке с литеральной константой; выбранный символ представляет собой мнемоническое обозначение состояния процесса. Это те же буквенные обозначения состояния, о которых сообщает поле STAT программы ps (безусловно, за исключением тех случаев, когда индекс находится за пределами диапазона). Более интересной является строка 8945, в которой выполняется копирование имени выполняемого модуля (вплоть до 16 символов, как было описано ранее) в эту заметку. И программа gdb, и «файл» программы используют это поле для сообщения о том, какая программа породила дамп памяти.
8948: В заметке 2 регистрируется объект struct task_struct вызвавшего дамп процесса, который четко представляет большой объем важной информации о процессе. Поскольку часть информации в объекте struct task_struct состоит из указателей, которые больше не на что не указывают при просмотре этого кода в отладчике, функция elf_core_dump затем выводит отдельно часть информации, соответствующую этим указателям и, что важнее всего, пространство памяти процесса.
8954: Если в системе имеется FPU (floating-point unit или математический сопроцессор), записывается заметка с его состоянием. Иначе, в строке 8957 уменьшается число заметок, которые должны быть записаны в файл.
8968: Для каждой созданной заметки записывается заголовок с описанием заметки; сама заметка появится позже. Заголовок относится к типу struct elf_phdr; его определение см. в строках 14727 и 14581.
8992: Это первый проход функции записи пространства памяти процесса. В этом проходе функция записывает информацию заголовка (тоже типа phdr) с описанием всех областей VMA, которые были обработаны для записи.
9016: И наконец, функция elf_core_dump действительно записывает заметки, которые она так трудолюбиво подготовила ранее.
9022: Переход вперед в файле к следующей границе в 4 Кб, где должны начаться фактические данные файла дампа. Макрокоманда DUMP_SEEK, используемая для этой цели, определена в строке 8710, и подобно DUMP_WRITE, она заставляет функцию elf_core_dump вернуть управление, если поиск оканчивается неудачей.
9024: После этой настройки все остальное кажется неинтересным. Но это главная часть дампа памяти: запись каждой области VMA процесса, вплоть до предела, рассчитанного ранее и записанного в переменной segs. После этого — небольшая очистка, и функция elf_core_dump заканчивает свою работу.
netlib.narod.ru | < Назад | Оглавление | Далее > |