Главная страница  Межпроцессное взаимодействие (состязание) 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 [ 82 ] 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

интерпретируемое в логическом контексте как ложь), значит, процесс затребовал обычный таймер, поэтому вызывается функция cause sig, передающая процессу сигнал SIGALARM. Запись в массиве watch dog используется и тогда, когда сработал синхронный сигнальный таймер. В этом случае в массив вместо адреса сторожевой функции помещается адрес функции cause alarm. Такой же подход неплох и для обычного таймера, только уже с адресом функции cause sig, но тогда нам бы пришлось переписать функцию cause sig так, чтобы она принимала аргументы, а номер процесса определяла бы из глобальной переменной. Или же переписать остальные функции так, чтобы они получали ненужные им аргументы.

Мы обсудим функцию cause sig, когда станем изучать системную задачу в следующем разделе. Назначение этой функции - посылать сообщение менеджеру памяти. Здесь сначала нужно определить, ожидает ли менеджер памяти сообщение. Если да, то сообщение о срабатывании таймера отправляется, в противном случае делается отметка передать сообщение при первой оказии.

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

Закончив облуживание таймеров, do clocktick приступает к проверке, не подошло ли время запланировать на выполнение следующий процесс. Время кванта выполнения хранится в переменой sched time, объявленной как PRIVATE. Обычно значение этой переменной декрементируется с каждым тиком таймера. В тех случаях, когда активизируется do clocktick, обработчик прерывания сам не уменьшает значение переменной, позволяя сделать это функции do clocktick, которая также проверяет, стало ли значение равно нулю. Значение sched ticks не сбрасывается при запуске нового процесса (поскольку менеджер памяти и файловая система должны продолжать работать), вместо этого значение сбрасывается через каждые SCHED RATE тиков. Перед тем как отобрать у текущего процесса процессор, проверяется, что он выполнялся в течение хотя бы одного полного такта планировщика.

Следующая подпрограмма, do getuptime, занимает всего одну строчку. Она копирует текущее значение переменной realtime (количество тактов таймера с момента загрузки системы) в соответствующее поле сообщения. Этот метод позволяет любому процессу получить информацию о времени работы системы, но для задач издержки на передачу сообщения чересчур велики, поэтому имеется соответствующая функция, get uptime, к которой задачи могут обращаться напрямую. Так как эта функция вызывается не через сообщение для задачи часов, ей приходится самостоятельно добавлять к значению realtime количество отложенных тиков. Чтобы в то время, когда делается обращение к переменной pending ticks, не произошло прерываний таймера, оно обрамлено вызовами lock и unlock.

Функция do get time вычисляет текущее значение времени исходя из значений realtime и boot time (момент загрузки системы в секундах). Функция do set time имеет обратное назначение. Она вычисляет новое значение boot time, основываясь на переданном ей значении реального времени и времени работы системы.



Следующие две подпрограммы, do setalarm и do setsyn alarm, настолько похожи, что мы будем рассматривать их вместе. Обе они сначала извлекают из сообщения информацию о процессе, которому будет передан сигнал, а также время задержки. Затем do setalarm извлекает адрес функции, которую необходимо вызвать, а несколькими строками далее этот адрес заменяется нулевым указателем, если целевой процесс является пользовательским процессом, а не задачей. Как мы уже видели ранее, подпрограмма do clocktick сравнивает значение данного указателя с нулем и тем самым определяет, нужно ли послать целевому процессу сигнал или вызвать сторожевую функцию у задачи. Далее обе функции вычисляют оставшееся до срабатывания таймера время (в секундах) и записывают его в ответное сообщение. В завершение обе функции вызывают common setalarm. При этом do setsyn alarm всегда передает common setalarm значение cause alarm.

Функция common setalarm завершает работу, начатую одной из двух только что описанных функций. Затем она записывает время срабатывания таймера в таблицу процессов, а в массив watch dog заносит адрес сторожевой функции (этот адрес также может быть равен нулю или быть ссылкой на функцию cause alarm). Далее функция сверяет все записи в таблице процессов, чтобы определить, какой таймер сработает следующим, точно так же, как это делается в do clocktick.

Код cause alarm несложен. В элемент массива syn table, соответствующий целевому процессу, помещается значение TRUE. Кроме того, если задача синхронного сигнального таймера не активна, ей отправляется сообщение, чтобы она проснулась .

Реализация задачи синхронного сигнального таймера

Структура задачи синхронного сигнального таймера, syn alarm task, следует общей структуре всех задач. После инициализации она переходит в бесконечный цикл, принимающий и отправляющий сообщения. Инициализация сводится к тому, что задача заявляет о своей активности, записывая в переменную syn al alive значение TRUE, а после сообщает о том, что ей нечем заняться, обнуляя (FALSE) все элементы массива syn table. В этой таблице имеются ячейки для всех процессов.

Внешний цикл программы начинается с того, что функция декларирует, что ее работа завершена, после чего управление передается во внутренний цикл. В этом цикле проверяются все записи в массиве syn table. Если обнаруживается процесс, ожидающий сигнала синхронного таймера, соответствующая ему запись сбрасывается, соответствующему процессу отправляется сигнал CLOCK INT и объявляется, что еще есть поле деятельности. Завершив внутренний цикл, программа не останавливается, ожидая новых сообщений, если не установлен флаг work done (работа завершена). Если для задачи имеется работа, передавать ей сообщения не нужно, так как cause alarm обращается к таблице syn alarm напрямую. В результате, пока имеются сигналы, ожидающие доставки, внешний цикл программы очень быстро проворачивается несколько раз.

Фактически эта задача не используется в обычном установочном пакете MINIX. Она будет использоваться сетевым сервером только в том слзае, если перекомпилировать ядро, включив поддержку сети. Сетевой сервер нуждается именно в этом



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

Реализация обработчика прерываний часов

Код обработчика прерываний часов - пример компромисса между потребностью делать как можно меньше (ради минимизации времени обработки) и необходимостью изредка выполнять достаточно сложные действия. Обработчик изменяет значения небольшого количества переменных и проверяет значения некоторых других. Код clock handler начинается с того, что делается учет процессорного времени. В MINIX отслеживается как процессорное время пользовательских процессов, так и время системы. Текушее время отдается пользовательскому процессу, если он исполнялся в момент возникновения прерывания. Системное время соответствует случаям, когда исполнялась файловая система или менеджер памяти. Ссылка на последний запланированный пользовательский процесс (два сервера не учитываются) всегда хранится в переменной bilLptr. После того как завершены действия по учету процессорного времени, увеличивается значение одной из основной переменных, pending ticks. Чтобы узнать, нужно ли пробудить терминал или послать сообшение задаче часов, необходимо знать реальное время, но каждый раз обновлять значение переменной realtime - дорогостоящая операция, так как обращение к этой переменной производится через блокировки. Чтобы избежать накладных расходов, обработчик вычисляет собственную версию реального времени в локальной переменной now. Существует небольшая вероятность погрешности в значении этой переменной, но у подобной ошибки не будет серьезных последствий.

Оставшаяся часть кода обработчика зависит от проверок различных условий. Как терминал, так и принтер нуждаются в том, чтобы время от времени получать управление. Для терминала предусмотрена глобальная переменная tty timeout, поддерживаемая задачей терминала. Она хранит время, соответствующее моменту получения управления задачей терминала в следующий раз. Для принтера нужно проверить значения нескольких переменных, объявленных в коде модуля принтера как PRIVATE, для этого служит функция pr restart, которая быстро завершается даже в худшем случае, если принтер не отвечает. Далее делается проверка, не имеется ли сработавших таймеров и не завершился ли квант времени, при выполнении этого условия активизируется задача часов. Последнее условие - сложное, оно является логической конъюнкцией трех более простых условий. Следующий далее код interrupt(CLOCK):

приводит к тому, что задаче часов отправляется сообщение HARD INT.

Обсуждая работу do clocktick, мы упомянули, что функция уменьшает значение переменной sched ticks и проверяет, не обнулилась ли она, то есть истек ли квант времени. В упомянутом выше сложном составном условии проверяется равенство sched ticks единице. Если задача часов еще не активизирована, необходимо уменьшить на единицу значение sched ticks, и если оно стало нулевым.



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 [ 82 ] 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования.