2011. Машбук (1114722), страница 43
Текст из файла (страница 43)
Поскольку такой переход невозможноосуществить ни оператором return, ни оператором goto, программист будет вынужденсоздавать какие-то громоздкие структуры для обработки ошибок на каждом уровневложенности.Возможность передавать управление в точку, находящуюся в одной извызывающих функций, предоставляется двумя системными вызовами, реализующимимеханизм нелокальных переходов:#include <setjmp.h>int setjmp(jmp_buf env);void longjmp(jmp_buf env, int val);Вызов setjmp() используется для регистрации некоторой точки кода, которая вдальнейшем будет использоваться в качестве пункта назначения для нелокальногоперехода, а вызов longjmp() – для перехода в одну из ранее зарегистрированныхконечных точек.При обращении к вызову setjmp(), происходит сохранение параметров текущейточки кода (значения счетчика адреса, позиции стека, регистров процессора и реакций насигналы).
Все эти значения сохраняются в структуре типа jmp_buf, которая передаетсявызову setjmp() в качестве параметра. При этом вызов setjmp() возвращает 0.После того, как нужная точка кода зарегистрирована с помощью вызоваsetjmp(), управление в нее может быть передано при помощи вызова longjmp(). Приэтом в качестве первого параметра ему указывается та структура, в которой былизафиксированы атрибуты нужной нам точки назначения. После осуществления вызоваlongjmp() процесс продолжит выполнение с зафиксированной точки кода, т.е.
с тогоместа, где происходит возврат из функции setjmp(), но в отличие от первого обращенияк setjmp(), возвращающим значением setjmp() станет не 0, а значение параметраval в вызове longjmp(), который произвел переход.Отметим, что если программист желает определить в программе несколько точекназначения для нелокальных переходов, каждая из них должна быть зарегистрирована всвоей структуре типа jmp_buf. С другой стороны, разумеется, на одну и ту же точкуназначения можно переходить из разных мест программы, при этом, чтобы различить, изкакой точки был произведен нелокальный переход, следует указывать при переходахразные значения параметра val.
В любом случае, при вызове longjmp() значениепараметра val не должно быть нулевым (даже если оно есть 0, то возвращаемое значениеsetjmp() будет установлено в 1). Кроме того, переход должен производиться только натакие точки, которые находятся в коде одной из вызывающих функций для той функции,откуда осуществляется переход (в том числе, переход может быть произведен из функцииобработчика сигнала). При этом в момент перехода все содержимое стека, используемоетекущей функцией и всеми вызывающими, вплоть до необходимой, освобождается.Пример. Использование нелокальных переходов.#include <signal.h>#include <setjmp.h>jmp_buf env;void abc(int s)158{…longjmp(env,1);/*переход- в точку *** */}int main(int argc, char **argv){…if (setjmp(env) == 0)/* запоминается данная точка процесса - *** */{signal(SIGINT,abc);/* установка реакции насигнал */…/*цикл обработки данных после вызова функцииsetjmp()*/}else{…/* цикл обработки данных после возврата изобработчика сигнала */}...}3.1.6 Трассировкапроцессов–модельвзаимодействия «главный-подчиненный»межпроцессногоДостаточно часто при организации многопроцессной работы необходимо наличиевозможности, когда один процесс является главным по отношению к другим процессам(трассировка процессов).
В частности, это необходимо для организации средств отладки,когда есть процесс-отладчик и отлаживаемый процесс. Для механизма отладки полезно,чтобы отладчик мог в произвольные моменты времени останавливать отлаживаемыйпроцесс и, когда отлаживаемый процесс остановлен, осуществлять действия по егоотладке: просматривать содержимое тела процесса, при необходимости корректироватьтело процесса и т.д.
Также является полезным возможность установки контрольных точекв отлаживаемом процессе. Очевидно, что полномочия процесса-отладчика по отношениюк отлаживаемому процессу являются полномочиями главного, т.е. отладчик можетосуществлять управление, в то время как отлаживаемый процесс может лишьподчиняться.В UNIX трассировка возможна только между родственными процессами: процессродитель может вести трассировку только непосредственно порожденных им потомков,при этом трассировка начинается только после того, как процесс-потомок даетразрешение на это.Далее схема взаимодействия процессов путем трассировки такова: выполнениеотлаживаемого процесса-потомка приостанавливается всякий раз при получении имкакого-либо сигнала, а также при выполнении вызова exec().
Если в это времяотлаживающий процесс осуществляет системный вызов wait(), этот вызов немедленновозвращает управление. В то время, как трассируемый процесс находится вприостановленном состоянии, процесс-отладчик имеет возможность анализировать иизменять данные в адресном пространстве отлаживаемого процесса и в пользовательской159составляющей его контекста.
Далее, процесс-отладчик возобновляет выполнениетрассируемого процесса до следующей приостановки (либо, при пошаговом выполнении,для выполнения одной инструкции).Для организации взаимодействия «главный–подчиненный» ОС Unix предоставляетсистемный вызов ptrace().#include <sys/ptrace.h>int ptrace(int cmd, int pid, int addr, int data);В этом системном вызове параметр cmd обозначает код выполняемой команды,pid — идентификатор процесса-потомка (который мы хотим трассировать), addr —некоторый адрес в адресном пространстве процесса-потомка, и, наконец, data — словоинформации.Посредством системного вызова ptrace() можно решать две задачи.
С однойстороны, с помощью этого вызова подчиненный процесс может разрешить родительскомупроцессу проводить свою трассировку: для этого в качестве параметра cmd необходимоуказать команду PTRACE_TRACEME. С другой стороны, с помощью этого же системноговызова процесс отладчик может манипулировать отлаживаемым процессом.
Для этогоиспользуются остальные значения параметра cmd.Системный вызов ptrace() позволяет выполнять следующие действия:1. читать данные из сегмента кода и сегмента данных отлаживаемого процесса;2. читать некоторые данные из контекста отлаживаемого процесса (в частности, имеетсявозможность чтения содержимого регистров);3. осуществлять запись в сегмент кода, сегмент данных и в некоторые области контекстаотлаживаемого процесса (в т.ч. модифицировать содержимое регистров). Следуетотметить, что производить чтение и запись данных (а также осуществлятьбольшинство управляющих команд над отлаживаемым процессом) можно лишь тогда,когда трассируемый процесс приостановлен;4.
продолжать выполнение отлаживаемого процесса с прерванной точки или спредопределенного адреса сегмента кода;5. исполнять отлаживаемый процесс в пошаговом режиме. Пошаговый режим — эторежим, обеспечиваемый аппаратурой компьютера, который вызывает прерываниепосле исполнения каждой машинной команды отлаживаемого процесса (т.е. послеисполнения каждой машинной команды процесс приостанавливается).Рассмотрим основные коды операций (параметр cmd) системного вызова ptrace().cmd = PTRACE_TRACEME — ptrace() с таким кодом операции сыновнийпроцесс вызывает в самом начале своей работы, позволяя тем самым трассировать себя.Все остальные обращения к вызову ptrace() осуществляет процесс-отладчик.cmd = PTRACE_PEEKDATA — чтение слова из адресного пространстваотлаживаемого процесса по адресу addr, ptrace() возвращает значение этого слова.cmd = PTRACE_PEEKUSER — чтение слова из контекста процесса. Речь идет одоступе к пользовательской составляющей контекста данного процесса, сгруппированнойв некоторую структуру, описанную в заголовочном файле <sys/user.h>.
В этом случаепараметр addr указывает смещение относительно начала этой структуры. В этойструктуре размещена такая информация, как регистры, текущее состояние процесса,счетчик адреса и так далее. ptrace() возвращает значение считанного слова.cmd = PTRACE_POKEDATA — запись данных, размещенных в параметре data,по адресу addr в адресном пространстве процесса-потомка.cmd = PTRACE_POKEUSER — запись слова из data в контекст трассируемогопроцесса со смещением addr. Таким образом можно, например, изменить счетчик адресатрассируемого процесса, и при последующем возобновлении трассируемого процесса еговыполнение начнется с инструкции, находящейся по заданному адресу.160cmd = 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 — завершение выполнения трассируемого процесса.Пример. Общая схема использования механизма трассировки.