<<
>>

Терминальный вывод

Вообще говоря, консольный вывод проще ввода, так как здесь действиями управляет операционная система, и не нужно бояться, что события произойдут в неподходящее время. Более того, процесс еще больше упрощается за счет того, что в MINIX 3 консолью является отображаемый на память экран.
Прерывания не нужны, вывод производится путем копирования данных из одной области памяти в другую. Однако в этом случае все операции по управлению экраном, включая обработку управляющих последовательностей, ложатся на программное обеспечение драйвера. Как и при изучении ввода в предыдущем разделе, мы проследим, какие действия происходят при выводе символа на консольный дисплей. В этом примере предполагается, что вывод производится на активный дисплей. Небольшое усложнение, вносимое виртуальными консолями, мы обсудим позже.

Обычно, когда процесс хочет что-нибудь напечатать, он вызывает функцию print f. Эта функция делает системный вызов write, который отправляет файловой системе сообщение с указателем на выводимые символы (но не сами символы).

Затем файловая система посылает сообщение драйверу терминала, а тот извлекает указанные ему символы и копирует их в видеопамять. Основные подпрограммы, из которых складывается терминальный вывод, показаны на рис. 3.23.

Рис. 3.23. Основные подпрограммы терминального вывода. Штриховой линией обозначено прямое копирование символов в буфер ramqueue подпрограммой cons_write

Когда драйвер терминала получает сообщение, требующее от него вывести что- либо на экран, вызывается подпрограмма do_write (строка 14029), которая сохраняет параметры в массиве tty_table структуры tty, соответствующей выбранной консоли.

Затем вызывается функция handle_event (та же функция, которая вызывается при обнаружении установленного флага tty_events). Эта подпрограмма при каждом вызове выполняет для указанного устройства как запись, так и чтение. В случае с консолью это означает, что сначала будут обработаны еще не принятые клавиатурные данные. Если необработанного ввода нет, переданные символы добавляются к другим символам, ожидающим вывода. Затем вызывается процедура cons_write (строка 16036), выполняющая вывод для отображаемых на память экранов. Последняя использует подпрограмму phys_copy, чтобы скопировать блок символов от пользовательского процесса в локальный буфер, причем, возможно, это действие будет повторено несколько раз, так как локальный буфер вмещает только 64 байта. Когда буфер укомплектовывается, каждый 8-разрядный байт переносится в другой буфер, ramqueue, представляющий собой массив 16-разрядных слов. Дополнительные байты заполняются текущим значением байта атрибутов, который определяет цвет символов, цвет фона и некоторые другие параметры текста. Когда это возможно, символы копируются напрямую в буфер ramqueue, но некоторые символы, например управляющие символы, образующие ESC-последовательности, необходимо обрабатывать особо. Кроме того, специальная обработка требуется тогда, когда позиция символа превышает ширину экрана или буфер ramqueue заполнен. В этом случае, чтобы передать символы и выполнить необходимые дополнительные действия, вызывается функция out_char (строка 16119). Так, каждый раз когда на последней строке экрана принимается символ перевода строки, делается вызов подпрограммы scroll_screen (строка 16205), a parse_escape принимает символы, образующие ESC-последовательность. Обычно out_char вызывает подпрограмму flush (строка 16259), которая копирует ramqueue в видеопамять при помощи ассемблерной подпрограммы mem_vid_copy. Функция flush также вызывается после того, как в ramqueue передается последний символ, чтобы гарантировать, что отображены все символы. Конечный результат работы flush — команда, предаваемая микросхеме видеоконтроллера 6845 с целью отобразить в нужном месте курсор.

Байты, поступающие от пользовательского процесса, было бы логично выводить по одному, в цикле. Тем не менее для систем класса Pentium с защитой памяти более эффективно предварительно накапливать их в буфере ramqueue, а затем копировать весь блок вызовом mem_vid_copy. Интересно, что этот прием применялся в ранних версиях MINIX еще тогда, когда использовались более старые процессоры, не поддерживающие защищенного режима. Предшественник mem_ vid_copy решал другую проблему — проблему синхронизации. Дело в том, что у старых экранов запись в видеопамять необходимо было производить при очистке экрана, во время вертикального обратного хода луча, чтобы избежать появления на экране «мусора». Сейчас такое устаревшее оборудование не поддерживается MINIX 3, так как это приводит к слишком большим потерям производительности. Тем не менее сама техника копирования целого блока из ramqueue применяется и поныне, хотя и для другой цели.

Доступная консоли область видеопамяти задается полями c_start и c_limit структуры console. Текущее положение курсора хранится в полях c_column и c_row. Координатам (0,0) соответствует верхний левый угол экрана, с этого места аппаратное обеспечение начинает заполнять экран. Изображение начинается с адреса, задаваемого значением c_org, и занимает 80 х 25 символов (4000 байт). Другими словами, микросхема 6845 извлекает из видеопамяти слово по адресу со смещением c_org и отображает в левом верхнем углу байт символа, а второй байт использует для задания цветов, мигания и прочих атрибутов. Затем чип извлекает из памяти следующее двухбайтовое слово и отображает символ в позиции (1, 0). Этот процесс продолжается до тех пор, пока не достигнута позиция (79, 0), после чего начинается рисование второй строки экрана, начиная с координат (0, 1).

При включении компьютера экран очищается, и данные записываются в видеопамять, начиная с положения c_start, a c_org присваивается то же значение, что и c_start. Таким образом, первая строка текста выводится в верхней строке экрана.

Когда вывод переходит на следующую строку в результате либо прихода символа перевода строки, либо заполнения верхней строки экрана, новые символы помещаются по адресу, равному c_start плюс 80. Со временем все 25 строк экрана оказываются заполненными и требуется прокрутка экрана. Некоторым программам, например текстовым редакторам, требуется возможность прокручивать экран и в обратном направлении, когда курсор находится в верхней строке экрана и нужно перейти выше по тексту.

Существует два подхода к решению задачи прокрутки экрана. Когда применяется программная прокрутка, символу с координатами (0, 0) всегда соответствует один и тот же адрес видеопамяти, с нулевым смещением относительно c_Бtart. Видеоконтроллер всегда выводит этот символ на одном месте благодаря тому, что в c_.org хранится один и тот же адрес. Когда необходимо выполнить прокрутку, область видеопамяти, начинающаяся со второй строки экрана (смещение 80 относительно с_зЬагЬ), копируется в начало занимаемой консолью области (смещение 0). Положение самой занимаемой экраном области памяти не меняется. В результате содержимое экрана перемещается на одну строку вверх. Эта операция занимает время, необходимое процессору для сдвига 80 х 24 = 1920 слов памяти. При аппаратной прокрутке данные никуда не перемещаются. Вместо этого контроллер получает команду выводить данные с другого адреса, например со слова 80. Для этого новое значение записывается в c_.org, чтобы его можно было использовать в дальнейшем, а также копируется в нужный регистр видеоконтроллера. Такая схема работает, если контроллер достаточно интеллектуален и при достижении конца видеопамяти (адрес в переходит на ее начало

(адрес в с_зЬагЬ), или же при такой емкости видеопамяти, когда в ней помещается более одного экрана, то есть 80 х 2000 слов.

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

Так, контроллер с видеопамятью объемом 32 768 байт может хранить в памяти 204 полные строки по 160 байт каждая и 179 раз выполнить аппаратную прокрутку, прежде чем невозможность перехода в начало станет проблемой. Затем потребуется копирование области памяти с целью переместить данные с последних 24 строк в начало видеобуфера. При любом методе нижняя строка экрана заполцяется пробелами, чтобы гарантировать ее очистку.

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

прокрутку. Любому видеоконтроллеру, обладающему достаточно большой для поддержки нескольких виртуальных консолей памятью, время от времени необходима программная прокрутка, хотя номинально поддерживается аппаратная. Чем меньший объем памяти доступен каждому виртуальному дисплею, тем чаще требуется программная прокрутка. Предел достигается, когда создается максимально возможное количество виртуальных консолей. При этом каждая прокрутка оказывается программной.

Положение курсора относительно начала видеопамяти можно вычислить, исходя из значения в полях с_со1шпп и с_гс^, но быстрее хранить его в явном виде (c_cur). Когда на экран выводится символ, он записывается в память по адресу c_cur, который после вывода обновляется, как и значение в поле c_column. Все поля структуры console, оказывающие влияние на текущее положение вывода и начало экрана в памяти, перечислены в табл. 3.10.

Таблица 3.10. Поля структуры console, связанные с текущей позицией на экране

Поле_____ Значение____________________________________________________

c_start Начало видеопамяти для консоли

cjimit Предел видеопамяти для консоли

c_column Номер текущего столбца (0-79), считая слева

c_row Номер текущей строки (0-24), считая сверху

c_cur Смещение курсора в видеопамяти

c_org Область памяти, отводимая под экран. В микросхеме 6845 эта область

___________ адресуется регистром базы_____________________________________________

При обработке символов, изменяющих текущую позицию (то есть символов перевода строки, забоя и т.

д.), соответственным образом изменяются значения полей c_column, c_row и c_cur. Это делается в конце кода процедуры flush, где вызывается функция set_6845, которая устанавливает регистры видеоконтроллера.

Драйвер терминала поддерживает ESC-последовательности, то есть гибкие возможности управления экраном для редакторов и прочих интерактивных программ. Поддерживаемые последовательности являются подмножеством стандарта ANSI и достаточны для простого переноса на MINIX 3 большинства программ, написанных для другого аппаратного обеспечения и других операционных систем. Есть две категории ESC-последовательностей: те, которые никогда не имеют параметров, и те, которые могут содержать варьируемые данные. Единственный представитель первой категории, реализованный в MINIX 3, это последовательность ESC М, выполняющая обратный перевод строки. При этом курсор поднимается на одну строку вверх, а если курсор уже находится на верхней строке, экран прокручивается на строку вниз. У последовательностей второй категории могут быть один или два численных параметра. Все такие цепочки символов начинаются с символов ESC [. Символ [ является преамбулой управляющей последовательности. Список последовательностей, описываемых стандартом ANSI и поддерживаемых MINIX 3, приведен в таблице 3.8.

Анализ управляющих последовательностей не так прост. В MINIX 3 корректные последовательности могут содержать от двух символов, как ESC М, до 8, как последовательности с двумя двухразрядными параметрами. Например, последовательность ESC [20; 60Н перемещает курсор на строку 20, в столбце 60. Если параметр один, его можно опускать, при наличии двух параметров позволено опустить оба. Когда параметр не указывается или указывается значение, превышающее граничное, используется значение по умолчанию, которое равно наименьшему допустимому значению.

Посмотрим, какие ESC-последовательности позволяют переместить курсор в верхний левый угол экрана.

♦ Последовательность ESC [Н. Так как оба параметра опущены, берутся наименьшие значения.

♦ Последовательность ESC [1; 1Н переместит курсор в строку 1, столбец 1 (в ANSI нумерация столбцов и строк начинается с единицы).

+ Опустив только один из параметров, можно получить последовательности ESC [ 1 ; Н и ESC [ ; 1Н. Отсутствующий параметр по умолчанию будет считаться равным 1.

♦ К тому же результату приведет последовательность ESC [ 0 ; ОН, так как в ней оба параметра меньше минимально допустимой величины. Они будут заменены значениями по умолчанию.

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

Для разбора ESC-последовательностей в MINIX 3 реализован конечный автомат. Переменная состояния этого автомата c_esc_state в структуре console обычно имеет значение 0. Когда функция out_char обнаруживает символ ESC, она устанавливает переменную состояния c_esc_state в 1, и последующие символы интерпретируются функцией parse_escape (строка 16293). Если следующий полученный символ является преамбулой ESC-последовательности, выполняется переход в состояние 2; в противном случае последовательность считается завершенной и вызывается функция do_escape (строка 16352). В состоянии 2, пока на вход поступают цифровые символы, вычисляется значение числовых параметров. А именно: предыдущее значение (которое изначально равно 0) умножается на 10, и к нему добавляется значение полученного числового символа. Значения параметров хранятся в массиве, и когда поступает символ точки с запятой, осуществляется переход к следующей ячейке массива. В MINIX 3 этот массив содержит всего два элемента, но принцип тот же. Когда полученный символ оказывается нечисловым и не точкой с запятой, последовательность считается завершенной и снова вызывается do_escape. Символ, являющийся текущим на момент обращения к do_escape, используется для того, чтобы определить, какое именно действие необходимо выполнить и как интерпретировать параметры, будь то значения по умолчанию или считанные из потока символов данные. Подробнее этот конечный автомат рассматривается в пункте 3.8.6.

<< | >>
Источник: Э. ТАНЕНБАУМ, А. ВУДХАЛЛ. ОПЕРАЦИОННЫЕ СИСТЕМЫ Разработка и реализация 3-е издание. 2007

Еще по теме Терминальный вывод:

  1. ПОРОГ ТЕРМИНАЛЬНЫЙ
  2. Выводы
  3. Выводы
  4. Выводы
  5. Выводы
  6. 7. Выводы, основанные на эмоциях
  7. 2.4.3. Выводы
  8. Выводы
  9. Выводы
  10. Выводы
  11. Выводы
  12. Выводы
  13. Выводы