- •Конспект по курсу лекций Операционные системы
- •Структура вычислительной системы
- •Аппаратный уровень вычислительной системы
- •Системы программирования
- •Модель организации прерываний с использованием регистра «слово состояние процессора»
- •3.6.1.1 Устройство последовательного доступа
- •Организация управления внешними устройствами
- •Иерархия памяти
- •Аппаратная поддержка ос и систем программирования
- •Некоторые проблемы
- •1. Вложенные обращения к подпрограммам
- •2. Накладные расходы при смене обрабатываемой программы:
- •4. Фрагментация памяти
- •4.2.1 Регистровые окна ( register window )
- •Системный стек
- •Виртуальная память.
- •Базирование адресов.
- •Страничная память.
- •Многомашинные, многопроцессорные ассоциации.
- •Терминальные комплексы
- •Компьютерные сети.
- •Семейство протоколов tcp/ip
- •Ip адрес представляется последовательностью четырех байтов. В адресе кодируется уникальный номер сети, а также номер компьютера (сетевого устройства в сети).
- •Транспортный уровень
- •Уровень прикладных программ
- •Сетевые, распределенные ос
- •Операционные системы Основные понятия
- •Структура ос.
- •Модельная ос
- •Жизненный цикл процесса
- •Типы операционных систем
- •Системы разделения времени
- •Управление внешними устройствами. Архитектура.
- •Программное управление внешними устройствами
- •Буферизация обмена
- •Планирование дисковых обменов
- •Raid системы.
- •Файлы устройств, драйверы
- •Управление оперативной памятью
- •Двухуровневая организация
- •Структурная организация файлов
- •Атрибуты файла
- •Типовые программные интерфейсы работы с файлами
- •Подходы в практической реализации файловой системы Структура «системного» диска
- •Модели реализации файлов Непрерывные файлы
- •Файлы, имеющие организацию связанного списка.
- •Индексные узлы (дескрипторы)
- •Модели организации каталогов
- •Варианты соответствия: имя файла – содержимое файла
- •Координация использования пространства внешней памяти
- •Учет свободных блоков файловой системы Связный список свободных блоков
- •Использование битового массива
- •Организация фс Unix
- •Логическая структура каталогов
- •Внутренняя организация фс Модель версии System V Структура фс
- •Работа с массивами номеров свободных блоков
- •Работа с массивом свободных ид
- •Индексные дескрипторы
- •Адресация блоков файла
- •Файл каталог
- •Установление связей
- •Недостатки фс модели версии System V
- •Модель версии ffs bsd
- •Стратегии размещения
- •Внутренняя организация блоков
- •Структура каталога ffs
- •Понятие «процесс».
- •Процессы в ос Unix Системно-ориентированное определение процесса
- •Базовые средства организации и управления процессами
- •Семейство системных вызовов exec()
- •Использование схемы fork-exec
- •Формирование процессов 0 и 1
- •. Планирование Основные задачи планирования
- •Планирование очереди процессов на начало обработки
- •Кванты постоянной длины.
- •Кванты переменной длины
- •Класс подходов, использующих линейно возрастающий приоритет.
- •Разновидности круговорота.
- •Смешанные алгоритмы планирования
- •Планирование в системах реального времени
- •Общие критерии для сравнения алгоритмов планирования
- •Планирование в ос unix
- •Планирование в Windows nt.
- •Планирование свопинга в ос Unix
- •Взаимодействие процессов: синхронизация, тупики Параллельные процессы
- •Проблемы организации взаимного исключения
- •Тупики (deadlocks)
- •Способы реализации взаимного исключения
- •Семафоры Дейкстры
- •Мониторы
- •Обмен сообщениями
- •Классические задачи синхронизации процессов
- •Задача «читателей и писателей»
- •Задача о «спящем парикмахере»
- •Реализация взаимодействия процессов
- •Сигналы
- •Системный вызов kill()
- •Системный вызов signal()
- •Пример 1.
- •Пример 2.
- •5 Пример. Программа “Будильник”.
- •Пример. Двухпроцессный вариант программы “Будильник”.
- •Пример. Использование канала.
- •Пример. Схема взаимодействия процессов с использованием канала.
- •Пример. Реализация конвейера.
- •Пример. Совместное использование сигналов и каналов – «пинг-понг».
- •Именованные каналы. Особенность именованных каналов в ос Unix.
- •Пример. «Клиент-сервер».
- •Межпроцессное взаимодействие, проводимое по модели «главный-подчинённый».
- •Системный вызов ptrace()
- •Общая схема трассировки процессов
- •Пример. Использование трассировки.
- •Система межпроцессного взаимодействия ipc.
- •Очередь сообщений
- •Системный вызов msgget()
- •Функция msgsnd()
- •Функция msgrcv()
- •Функция msgctl()
- •Пример. Использование очереди сообщений.
- •Пример. Очередь сообщений. Модель «клиент-сервер».
- •Разделяемая память.
- •Пример. Работа с общей памятью в рамках одного процесса.
- •Семафоры
- •Пример. Использование разделяемой памяти и семафоров.
- •1Й процесс:
- •2Й процесс:
- •Механизм сокетов
- •Типы сокетов.
- •Функция создания сокета
- •Запрос на соединение
- •Прослушивание сокета
- •Подтверждение соединения
- •Прием и передача данных
- •Закрытие сокета
- •Пример. Работа с локальными сокетами
- •Пример работы с сокетами в рамках сети.
Общая схема трассировки процессов
Рассмотрим некоторый модельный пример, демонстрирующий общую схему построения отладочной программы:
...
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);
/* теперь выполняются любые действия над трассируемым процессом */
…
}
}
Предназначение процесса-потомка — разрешить трассировку себя. После вызова ptrace(PTRACE_TRACEME, 0, 0, 0) ядро устанавливает для этого процесса бит трассировки. Сразу же после этого можно заместить код процесса-потомка кодом программы, которую необходимо отладить. Отметим, что при выполнении системного вызова exec(), если для данного процесса ранее был установлен бит трассировки, ядро перед передачей управления в новую программу посылает процессу сигнал SIGTRAP. При получении данного сигнала трассируемый процесс приостанавливается, и ядро передает управление процессу-отладчику, выводя его из ожидания в вызове wait().
Процесс-родитель вызывает wait() и переходит в состояние ожидания до того момента, пока потомок не перейдет в состояние трассировки. Проснувшись, управляющий процесс, выполняя функцию ptrace(cmd, pid, addr, data) с различными кодами операций, может производить любое действие с трассируемой программой, в частности, читать и записывать данные в адресном пространстве трассируемого процесса, производить его пошаговое выполнение – при этом, как показано в примере выше, применяется та же схема: процесс-отладчик вызывает wait() в состояние ожидания, а ядро возобновляет выполнение трассируемого потомка, исполняет трассируемую команду, и вновь передает управление отладчику, выводя его из ожидания .
Пример. Использование трассировки.
Процесс, который мы будем отлаживать, осуществляет деление на 0. Это сделано для того, чтобы показать, как происходит прерывание выполнения отлаживаемого процесса, когда к нему приходит сигнал. Т.е. когда было произведено деление на 0, приходит сигнал, который мы и будем ловить в нашем отлаживаемом процессе, но в отличии от обычной ситуации, когда обработка по умолчанию на этот сигнал означала бы завершение процесса, в данном случае он будет только приостановлен. В процессе-отладчике, во-первых, описывается структура, при помощи которой будет осуществляться чтение из регистра; далее порождается процесс; в процессе-сыне запускается трассировка (вызов ptrace с командой PTRACE_TRACEME) и запускается замена тела процесса на процесс, который состоит из единственного деления на 0. Отец запускает wait(), первый выход из этого wait() будет сразу после execl() в сыне. После этого он добывает содержимое некоторых регистров, печатает их содержимое. Обратите внимание, что имена этих регистров – EIP(индексный регистр) и ESP(stack pointer) являются машинно-зависимыми, т.е. на другой машине структура REG, а именно имена полей в ней могут быть другими, но в целом структура REG описывает регистры общего назначения на данной архитектуре. Соответственно, они были получены с помощью команды PTRACE_GETREGS в вызове ptrace, затем они распечатываются, затем идет проверка на то, был ли процесс приостановлен с помощью сигнала SIGTRAP и, если этот так, то продолжается его выполнение.Если это не так (это будет в том случае, когда уже второй раз был получен сигнал SIGTRAP от отлаживаемого процесса, когда он остановился в результате деления на 0) он проверяет, что действительно в статусе передано, что этот процесс должен завершится, и он (процесс) закрывается.
Эта схема работает следующим образом: процесс – сын запускает трассировку и запускает execl(), после чего сразу приостанавливается, потому что к нему пришел сигнал SIGTRAP. Процесс-отец в цикле ожидает в wait(), выходит из ожидания, потому что процесс-сын приостанавливается, далее проверяет содержимое его регистров, печатает это содержимое и запускает опять его выполнение. Теперь процесс-сын выполняет программу, которая осуществляет деление на 0, так только он осуществил деление на 0 ему приходит сигнал SIGFPE, и так как он находится в ситуации трассировки, то он приостанавливается, и управление вновь передается процессу-отцу. Процесс-отец вновь вышел из wait(), проанализировал ситуацию, увидел из-за какого сигнала прервалось выполнение отлаживаемого процесса. Для этого он использует следующие макросы: WSTOPSIG() – это макрос, который позволяет из статуса процесса (из того что вернулось в функции wait()) получить информацию из-за чего тот был приостановлен: т.е. то, что он был приостановлен в результате сигнала или в результате какого сигнала. Далее из этого же статуса с помощью макроса WIFEXITED(), что процесс-потомок должен был завершиться, и в этом случае он просто убивается с помощью использования команды PTRACE_KILL в вызове ptrace.
/* Процесс-сын: */
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=%#x ESP=%#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);
}
}
