Билеты (Graur) (1114774), страница 20
Текст из файла (страница 20)
Все остальные обращения к вызову ptrace() осуществляет процессотладчик.cmd = PTRACE_PEEKDATA — чтение слова из адресного пространстваотлаживаемого процесса по адресу addr, ptrace() возвращает значение этогослова.cmd = PTRACE_PEEKUSER — чтение слова из контекста процесса. Речь идет одоступе к пользовательской составляющей контекста данного процесса,сгруппированной в некоторую структуру, описанную в заголовочном файле<sys/user.h>. В этом случае параметр addr указывает смещение относительноначала этой структуры.
В этой структуре размещена такая информация, какрегистры, текущее состояние процесса, счетчик адреса и так далее. ptrace()возвращает значение считанного слова.cmd = PTRACE_POKEDATA — запись данных, размещенных в параметре data,по адресу addr в адресном пространстве процесса-потомка.cmd = PTRACE_POKEUSER — запись слова из data в контекст трассируемогопроцесса со смещением addr. Таким образом можно, например, изменить счетчикадреса трассируемого процесса, и при последующем возобновлении трассируемогопроцесса его выполнение начнется с инструкции, находящейся по заданномуадресу.cmd = PTRACE_GETREGS, PTRACE_GETFREGS — чтение регистров общегоназначения (в т.ч.
с плавающей точкой) трассируемого процесса и запись ихзначения по адресу data.cmd = PTRACE_SETREGS, PTRACE_SETFREGS — запись в регистры общегоназначения (в т.ч. с плавающей точкой) трассируемого процесса данных,расположенных по адресу data в трассирующем процессе.cmd = PTRACE_CONT — возобновление выполнения трассируемого процесса.Отлаживаемый процесс будет выполняться до тех пор, пока не получит какой-либосигнал, либо пока не завершится.cmd = PTRACE_SYSCALL, PTRACE_SINGLESTEP — эта команда, аналогичноPTRACE_CONT, возобновляет выполнение трассируемой программы, но при этомпроизойдет ее остановка после того, как выполнится одна инструкция.
Такимобразом, используя PTRACE_SINGLESTEP, можно организовать пошаговуюотладку. С помощью команды PTRACE_SYSCALL возобновляется выполнениетрассируемой программы вплоть до ближайшего входа или выхода из системноговызова. Идея использования PTRACE_SYSCALL в том, чтобы иметь возможностьконтролировать значения аргументов, переданных в системный вызовтрассируемым процессом, и возвращаемое значение, переданное ему из системноговызова.cmd = PTRACE_KILL — завершение выполнения трассируемого процесса.Общая схема использования механизма трассировки.Рассмотрим некоторый модельный пример, демонстрирующий общую схемупостроения отладочной программы (см.
также Рис. 9):...if ((pid = fork()) == 0){ptrace(PTRACE_TRACEME, 0, 0, 0);/* сыновний процесс разрешает трассировать себя */exec(“трассируемый процесс”, 0);/* замещается телом процесса, который необходимотрассировать */}else{/* это процесс, управляющий трассировкой */wait((int ) 0);/* процесс приостанавливается до тех пор, пока оттрассируемого процесса не придет сообщение о том,что он приостановился */for(;;){ptrace(PTRACE_SINGLESTEP, pid, 0, 0);/* возобновляем выполнение трассируемойпрограммы */wait((int ) 0);/* процесс приостанавливается до тех пор, покаот трассируемого процесса не придет сообщениео том, что он приостановился */…ptrace(cmd, pid, addr, data);/* теперь выполняются любые действия надтрассируемым процессом */…}}Процесс-потомокcигналSIGTRAPptrace(PTRACE_TRACEME,0, 0, 0);exec(…);...cигналSIGTRAPПроцесс-предокwait(…);for(;;) {…ptrace(PTRACE_SINGLESTEP, …);…wait(…);…}§Рис. 9 Общая схема трассировки процессовПредназначение процесса-потомка — разрешить трассировку себя.
После вызоваptrace(PTRACE_TRACEME, 0, 0, 0) ядро устанавливает для этого процессабит трассировки. Сразу же после этого можно заместить код процесса-потомкакодом программы, которую необходимо отладить. Отметим, что при выполнениисистемного вызова exec(), если для данного процесса ранее был установлен биттрассировки, ядро перед передачей управления в новую программу посылаетпроцессу сигнал SIGTRAP. При получении данного сигнала трассируемый процессприостанавливается, и ядро передает управление процессу-отладчику, выводя егоиз ожидания в вызове wait().Процесс-родитель вызывает wait() и переходит в состояние ожидания до тогомомента, пока потомок не перейдет в состояние трассировки.
Проснувшись,управляющий процесс, выполняя функцию ptrace(cmd, pid, addr, data)с различными кодами операций, может производить любое действие страссируемой программой, в частности, читать и записывать данные в адресномпространстве трассируемого процесса, а также разрешать дальнейшее выполнениетрассируемого процесса или производить его пошаговое выполнение. Схемапошаговой отладки показана в примере выше и на рисунке: на каждом шагепроцесс-отладчик разрешает выполнение очередной инструкции отлаживаемогопроцесса и затем вызывает wait() и погружается в состояние ожидания, а ядровозобновляет выполнение трассируемого потомка, исполняет трассируемуюкоманду и вновь передает управление отладчику, выводя его из ожидания .9.
Трассировка процессов./* Процесс-сын: */int main(int argc, char **argv){/* деление на ноль – здесь процессу будет послансигнал SIGFPE – floating point exception */return argc/0;}Процесс-родитель:#include <stdio.h>#include <sys/types.h>#include <unistd.h>#include <signal.h>#include <sys/ptrace.h>#include <sys/user.h>#include <sys/wait.h>int main(int argc, char *argv[]){pid_t pid;int status;struct user_regs_struct REG;if ((pid = fork()) == 0) {/*находимся в процессе-потомке, разрешаемтрассировку */ptrace(PTRACE_TRACEME, 0, 0, 0);execl(“son”, ”son”, 0);/* замещаем телопроцесса *//* здесь процесс-потомок будет остановлен ссигналом SIG_TRAP, ожидая команды продолжениявыполнения от управляющего процесса*/}/* в процессе-родителе */while (1) {/* ждем, когда отлаживаемый процессприостановится */wait(&status);/*читаем содержимое регистров отлаживаемогопроцесса */ptrace(PTRACE_GETREGS, pid, ®, ®);/* выводим статус отлаживаемого процесса,номер сигнала, который его остановил изначения прочитанных регистров */printf("signal = %d, status = %#x, EIP=%#xESP=%#x\n", WSTOPSIG(status), status, REG.eip,REG.esp);if (WSTOPSIG(status) != SIGTRAP) {if (!WIFEXITED(status)) {/* завершаем выполнениетрассируемого процесса */ptrace (PTRACE_KILL, pid, 0, 0);}break;}/* разрешаем выполнение трассируемому процессу*/ptrace (PTRACE_CONT, pid, 0, 0);}}ДЛЯ БИЛЕТОВ 33-35 ОБЩАЯ ЧАСТЬИменование разделяемых объектов.Для всех средств IPC приняты общие правила именования объектов, позволяющиепроцессу получить доступ к такому объекту.
Для именования объекта IPCиспользуется ключ, представляющий собой целое число. Ключи являютсяуникальными во всей UNIX-системе идентификаторами объектов IPC, и зная ключдля некоторого объекта, процесс может получить к нему доступ. При этомпроцессу возвращается дескриптор объекта, который в дальнейшем используетсядля всех операций с ним. Проведя аналогию с файловой системой, можно сказать,что ключ аналогичен имени файла, а получаемый по ключу дескриптор –файловому дескриптору, получаемому во время операции открытия файла. Ключдля каждого объекта IPC задается в момент его создания тем процессом, которыйего порождает, а все процессы, желающие получить в дальнейшем доступ к этомуобъекту, должны указывать тот же самый ключ.Итак, все процессы, которые хотят работать с одним и тем же IPC-ресурсом,должны знать некий целочисленный ключ, по которому можно получить к немудоступ.
В принципе, программист, пишущий программы для работы с разделяемымресурсом, может просто жестко указать в программе некоторое константноезначение ключа для именования разделяемого ресурса. Однако, возможнаситуация, когда к моменту запуска такой программы в системе уже существуетразделяемый ресурс с таким значением ключа, и в виду того, что ключи должныбыть уникальными во всей системе, попытка породить второй ресурс с таким жеключом закончится неудачей (подробнее этот момент будет рассмотрен ниже).Генерация ключей: функция ftok().Как видно, встает проблема именования разделяемого ресурса: необходим некиймеханизм получения заведомо уникального ключа для именования ресурса, новместе с тем нужно, чтобы этот механизм позволял всем процессам, желающимработать с одним ресурсом, получить одно и то же значение ключа.Для решения этой задачи служит функция ftok():#include <sys/types.h>#include <sys/ipc.h>key_t ftok(char *filename, char proj);Эта функция генерирует значение ключа по некоторой строке символов идобавочному символу, передаваемым в качестве параметров.
Гарантируется, чтополученное таким образом значение будет отличаться от всех других значений,сгенерированных функцией ftok() с другими значениями параметров, и в то жевремя, при повторном запуске ftok() с теми же параметрами, будет получено тоже самое значение ключа.Смысл второго аргумента функции ftok() – добавочного символа – в том, что онпозволяет генерировать разные значения ключа по одному и тому же значениюпервого параметра – строки. Это позволяет программисту поддерживать нескольковерсий своей программы, которые будут использовать одну и ту же строку, норазные добавочные символы для генерации ключа, и тем самым получатвозможность в рамках одной системы работать с разными разделяемымиресурсами.Следует заметить, что функция ftok() не является системным вызовом, апредоставляется библиотекой.Общие принципы работы с разделяемыми ресурсами.Рассмотрим некоторые моменты, общие для работы со всеми разделяемымиресурсами IPC.
Как уже говорилось, общим для всех ресурсов является механизмименования. Кроме того, для каждого IPC-ресурса поддерживается идентификаторего владельца и структура, описывающая права доступа к нему. Подобно файлам,права доступа задаются отдельно для владельца, его группы и всех остальныхпользователей; однако, в отличие от файлов, для разделяемых ресурсовподдерживается только две категории доступа: по чтению и записи. Априорисчитается, что возможность изменять свойства ресурса и удалять его имеетсятолько у процесса, эффективный идентификатор пользователя которого совпадаетс идентификатором владельца ресурса.
Владельцем ресурса назначаетсяпользователь, от имени которого выполнялся процесс, создавший ресурс, однакосоздатель может передать права владельца другому пользователю. В заголовочномфайле <sys/ipc.h> определен тип struct ipc_perm, который описываетправа доступа к любому IPC-ресурсу. Поля в этой структуре содержатинформацию о создателе и владельце ресурса и их группах, правах доступа кресурсу и его ключе.Для создания разделяемого ресурса с заданным ключом, либо подключения к ужесуществующему ресурсу с таким ключом используются ряд системных вызовов,имеющих общий суффикс get. Общими параметрами для всех этих вызововявляются ключ и флаги.