1 (1119426), страница 4
Текст из файла (страница 4)
Врезультате вызов select() завершается либо по приходу события отопераций ввода/вывода, либо по истечению тайм-аута. После обработкисобытий ввода/вывода сервер не переходит сразу к ожиданию, а передэтим запрашивает ОС о завершении процессов-потомков. Для этогоследует использовать системный вызовwaitpid(-1, &status,WNOHANG); // вызов неблокирующий WNOHANG-Возможно, понадобится несколько вызовов waitpid() в случае, если19сразу несколько CGI-программ успели завершиться за время выполнениявызова select(). После того, как все завершившиеся к этому моментупроцессы обработаны, сервер снова переходит в режим ожидания событийввода/вывода (т.е.
вызывает select()).Отдельного разговора заслуживает вопрос о последовательностиобработки событий: то ли выполнять вначале все операции accept(), затемввода/вывода, затем обработку завершившихся потомков, то ли наоборот(вначале потомки, потом ввод-вывод, потом accept()), то ли использоватьочередность запросов (если запрос обрабатывается дольше всех, то егособытия по его обработке выполнять в первую очередь) и т.д. В общемслучае, ответ на этот вопрос и выбор стратегии «балансировки нагрузки навеб-сервер» весьма нетривиален, и его обсуждение выходит за рамкиданного задания.Итак, последовательность системных вызовов для одного шага циклаасинхронной обработки может быть такой:// формирование масок для вызова select()select()// вышли из select() по тайм-ауту, либо по наступлению событий.
. .// обработка входящих соединенийaccept(). . .// обработка операций ввода-выводаread()write(). . .// обработка завершившихся CGI-программwaitpid()// генерация ответа клиенту// чистка ресурсов (удаление временных файлов и т.д.)Последовательность системных вызовов для обработки CGI-запроса:pid = fork();if (pid > 0) {// родитель-сервер - продолжаем асинхронную обработку событий} else if (pid < 0) {// катастрофическая ошибка...} else {// потомок – обработка CGI-программы:// - формирование массива переменных окружения env// - перенаправление стандартного вывода во временный файл// - перенаправление стандартного ввода (для метода POST)// - собственно запуск программыexecvpe(script_filename, argv, env);// обработка ошибок запуска}20МЕТОДИЧЕСКИЕ УКАЗАНИЯ ПО ВЫПОЛНЕНИЮТРЕТЬЕГО ЭТАПА ЗАДАНИЯНа третьем этапе требуется разработать интерпретатор модельногоязыка и встроить его в веб-сервер, реализованный на предыдущих этапахвыполнения задания.Как уже отмечалось выше, для написания CGI-программ пригоденпрактическилюбойязыкпрограммирования,поддерживающийстандартный ввод/вывод и считывание значений переменных окружения.Однако специфика CGI-программ, а именно – ориентация на генерациютекста (точнее, текста на языке HTML), приводит к тому, чтоиспользование традиционных языков системного программирования типаСи/Си++ оказывается неудобным: соответствующиеCGI-программыполучаются громоздкими, трудными для понимания и сопровождения.Традиционные преимущества таких языков - компилируемость, высокаяэффективность разработанных программ, возможность управлениялюбыми системными ресурсам – перестают быть таковыми для CGIпрограмм.
Шлюзовым программам не обязательно быть максимальноэффективными, так как основное время тратится не на выполнениепрограммы генерации HTML-текста, а на передачу его по медленным(относительно процессора) сетевым каналам связи. Доступ к системнымресурсам для программ, запуск которых инициируется извне (по HTTPзапросу от веб-клиентов), должен быть не упрощен, а наоборот ограничен.Удобство разработки шлюзовых программ является более важнымтребованием к соответствующей системе программирования нежелиэффективность получаемого кода.Ниже описаны три варианта модельных языков, предлагаемых длявстраивания в реализованный веб-сервер.
Каждый из вариантов отвечаетминимальным требованиям к языкам для написания CGI-программ, аименно:- достаточно полный набор операторов, включающий в себяприсваивание, ветвление на 2 варианта (if-then-else), циклы (while-do, dowhile, for), составной оператор;- как минимум, два типа данных: целый с базовымиарифметическими операциями и строковый с операцией конкатенации;- набор стандартных функций, включающий в себя доступ кпеременным окружения, базовые функции обработки строк и символьныйввод/вывод.Кроме того, каждый из языков должен поддерживать хотя бы одиниз способов встраивания в веб-сервер.21Способы встраивания программ на модельных языках в веб-серверМы рассмотрим три способа встраивания программ:◦ Базовый.◦ Внутристраничный тег “<? ?>”.◦ Внутристраничный тег SCRIPT.В базовом способе программа из GET-запроса, обрабатываемаяинтерпретатором модельного языка – это просто текст на этом языке,генерирующий в стандартный канал вывода HTML-страницу, которая ивыдается как ответ веб-сервера.
Именно такой способ мы применяли впримере для языка Си (правда, без интерпретатора). Этот способ - самыйпростой, и он применим к любой CGI-программе. Однако он является несамым удобным для разработчика.При внутристраничном способе встраивания программа из GETзапроса представляет собой HTML-текст, в который встроены фрагментыCGI-программы. Интерпретатор такой программы должен различатьHTML-текст и код CGI-программы.
HTML-текст просто выводится встандартный канал, а фрагменты программы выполняются сразу после ихсчитывания из текста файла. В этом случае надо писать фрагменты,генерирующие только изменяющиеся части страницы, а постоянные частиможно сразу оформлять как HTML-текст. В результате общий объем CGIпрограммы существенно уменьшается (как и усилия по ее написанию ипониманию).Разумеется, фрагменты CGI-программы должны четко выделятьсясинтаксически, а соответствующий интерпретатор должен уметь различатьHTML-текст и собственно программу. Удобно включать фрагментыпрограммы внутрь специальных тегов.
При втором способе встраиванияиспользуется тег “<? ?>”, который не входит в HTML. Части программыразмещаются между открывающим “<?” и закрывающим “?>” тегами.Например, текст на языке PHP, выводящий тот же текст, что и впредыдущем примере на Си, выглядит так:Content-type: text/html<html><body>Приветствую! Вы ввели аргументы:<? echo "$QUERY_STRING с адреса $REMOTE_ADDR" ?><body></html>Здесь фрагмент программы на PHP содержит оператор вывода echo.Все остальное — это просто HTML-текст.При третьем способе используется стандартный HTML-тег SCRIPT.Этот тег используется для встраивания исполняемых программ в любые22HTML-страницы, причем выполняться эти программы могут как сервером(для генерации содержимого страницы), так и клиентом-браузером.
Насбудет интересовать только первый вариант.Тег SCRIPT имеет следующий вид:<SCRIPT LANGUAGE=”lang_name” RUNAT=”server”>текст программы</SCRIPT>Атрибут LANGUAGE содержит имя языка (в общем случае могутподдерживаться разные языки) – по умолчанию это JavaScript. АтрибутRUNAT=”server” сообщает интерпретатору о том, что соответствующийфрагмент должен выполняться на сервере, а не на клиенте. Заметим, что поумолчанию этот атрибут соответствует клиенту, поэтому при отсутствииатрибута RUNAT интерпретатор обязан выводить весь тег SCRIPT в томвиде, как он есть, в стандартный вывод (для последующей обработкиклиентом).Приведем пример программы на модельном JavaScript, которыйгенерирует ту же самую страницу, что и в предыдущих примерах:<html><body>Приветствую! Вы ввели аргументы:<SCRIPT LANGUAGE=”JavaScript” RUNAT=”server”>Response.w rite(Приветствую! Вы ввели аргументы: +Environment[“QUERY_STRING”] + “с адреса ” +Environment[“REMOTE_ADDR”]);<body></html>Также как и в примере на PHP, фрагмент программы на JavaScriptсодержит обращение к функции вывода ( только она называется write иявляется методом объекта Response).
Весь остальной текст — это HTML.Разумеется, внутристраничные методы встраивания доставляютдополнительные «заботы» интерпретатору: ведь он должен анализироватьне только текст на «своем» языке, но и HTML-текст. При этом «родной»текст должен быть проанализирован, переведен в промежуточноепредставление (например, разновидность ПОЛИЗ [5]) и выполнен(интерпретирован). В свою очередь, HTML-текст должен быть простоперенаправлен на стандартный вывод, поэтому требования к анализу иобработкеHTML-текста существенно ниже. Интерпретатор долженвычленить текст программы и выполнить его, весь остальной текст перенаправить.
Обработка возможных ошибок в HTML, проверкакорректности и возможности отображения HTML-текста - задача другихинструментов.23ОПИСАНИЕ МОДЕЛЬНЫХ ЯЗЫКОВДля описания модельных языков используются следующиесоглашения и обозначения (традиционные для расширенной БНФ):• запись вида {α} означает итерацию цепочки α, т.е. цепочки вида: ε,α, αα, ααα и т.д.;• запись вида [α] означает: α или «пусто» (ε);• для отличия метасимволов БНФ (фигурных и квадратных скобок, атакже вертикальной черты) от терминальных символов модельныхязыков последние выделены жирным шрифтом и жирным шрифтоми подчеркнуты ({}[]|);• служебные (ключевые) слова модельных языков выделены жирнымшрифтом (for)Модельный язык программированияВ качестве первого варианта языка предлагается использоватьмодельныйязыкпрограммированияиззаданияпрактикума,разработанного Т.В.Руденко [6].
Единственное (и обязательное)расширение этого языка, необходимое для использования в CGIтехнологии, это добавление специальных переменных для доступа кпеременным окружения. Эти переменные имеют вид: $Name, гдеидентификатор Name – это имя переменной окружения (например,$QUERY_STRING). Все переменные окружения имеют тип string. Онимогут появляться везде в выражениях строкового типа, но их значение неможет быть изменено. Заметим, что в задании не определен синтаксиспонятия идентификатор. Однако для наших целей имя (идентификатор)переменной окружения должно быть совместимо с требованиями ОСUNIX, поэтому будем требовать, чтобы в идентификатор (по-крайней мередля имен переменных окружения) могли входить латинские буквы, цифрыи символ подчеркивания «_». Регистр букв имеет значение.Модельный язык должен поддерживать единственный способвстраивания — внешний (внутристраничные не используются).Модельный JavaScript (MJS)Второй вариант модельного языка основан на языке JavaScript[7].ПеременныеМы ссылаемся на переменные с помощью имен (идентификаторов).Имя — это последовательность латинских букв, цифр и знаковподчеркивания(«_»).
Регистр букв имеет значение (A и a – это разныеидентификаторы). Имя не может совпадать ни с одним из служебных слов(регистр букв в служебных словах также имеет значение — он всегда24нижний).Важнейшей особенностью языкаMJS является динамическаятипизация переменных, что означает, что переменная может иметьзначение любого типа. Тип значения определяется при присваивании илиинициализации переменной и может измениться при последующихприсваиваниях. Поэтому необходимость в явном объявлении переменныхотпадает, и в «родном» языке JavaScript переменные можно не объявлять.Не объявленная явно переменная начинает существовать с моментапервого присвоения ей значения. Однако такая практика ухудшает какэффективность, так и надежность программного кода, поэтому вмодельном варианте переменные необходимо явно объявить до первогоиспользования с помощью служебного слова var, например:var x = “string value”;var Y;Такое объявление (а точнее — определение) может появиться везде,где может появиться оператор (оно и является частным случаемоператора).С понятием переменной связано понятие «область действия».
Еслипеременная объявлена на верхнем уровне программы (т.е. непосредственновнутри какого-либо тега SCRIPT), то она является глобальной и ее областьдействия — вся программа (начиная с точки объявления). В противномслучае переменная локальна и ее область действия — блок, где появилосьее объявление. После выхода из блока локальные переменные перестаютсуществовать.Простые типы данных и операции.В MJS есть 3 простых типа данных: строковый (String), числовой(Number) и логический (Boolean).Константы строкового типа данных содержат любые символы,заключенные в двойные кавычки (“Пример строки”), либо в одинарныекавычки ('Еще один пример строки').