Читаем Архитектура операционной системы UNIX полностью

    предпринять действия к активизации таймера (таблица ответных сигналов);

   sleep (до поступления данных с терминала);

  }

 /* в списке для неструктурированных вводных данных есть информация */

  if (терминал в режиме без обработки)

   скопировать все данные из списка для неструктурированных вводных данных в канонический список;

  else { /* терминал в каноническом режиме */

   do while (в списке для неструктурированных вводных данных есть символы) {

    копировать по одному символу из списка для неструктурированных вводных данных в канонический список: выполнить обработку символов стирания и удаления;

    if (символ — "возврат каретки" или "конец файла") break; /* выход из цикла */

   }

  }

 }

 do while(в каноническом списке еще есть символы и не исчерпано количество символов, указанное в вызове функции read)

  копировать символы из символьных блоков канонического списка в адресное пространство задачи;

}

Рисунок 10.15. Алгоритм чтения с терминала

Обработка символов в направлении ввода и в направлении вывода асимметрична, что видно из наличия двух символьных списков для ввода и одного — для вывода. Строковый интерфейс выводит данные из пространства задачи, обрабатывает их и помещает их в список для хранения выводных данных. Для симметрии следовало бы иметь только один список для вводных данных. Однако, в таком случае потребовалось бы использование программы обработки прерываний для интерпретации символов стирания и удаления, что сделало бы процедуру более сложной и длительной и запретило бы возникновение других прерываний на все критическое время. Использование двух символьных списков для ввода подразумевает, что программа обработки прерываний может просто сбросить символы в список для неструктурированных вводных данных и возобновить выполнение процесса, осуществляющего чтение, который собственно и возьмет на себя работу по интерпретации вводных данных. При этом программа обработки прерываний немедленно помещает введенные символы в список для хранения выводных данных, так что пользователь испытывает лишь минимальную задержку при просмотре введенных символов на терминале.

char input[256];

main() {

 register int i;

 for (i = 0; i ‹ 18; i++) {

  switch (fork()) {

  case -1: /* ошибка */

   printf("операция fork не выполнена из-за ошибки\n");

   exit();

  default: /* родительский процесс */

   break;

  case 0: /* порожденный процесс */

   for (;;) {

    read(0, input, 256); /* чтение строки */

    printf("%d чтение %s\n",i,input);

   }

  }

 }

}

Рисунок 10.16. Конкуренция за данные, вводимые с терминала

На Рисунке 10.16 приведена программа, в которой родительский процесс порождает несколько процессов, осуществляющих чтение из файла стандартного ввода, конкурируя за получение данных, вводимых с терминала. Ввод с терминала обычно осуществляется слишком медленно для того, чтобы удовлетворить все процессы, ведущие чтение, поэтому процессы большую часть времени находятся в приостановленном состоянии в соответствии с алгоритмом terminal_read, ожидая ввода данных. Когда пользователь вводит строку данных, программа обработки прерываний от терминала возобновляет выполнение всех процессов, ведущих чтение; поскольку они были приостановлены с одним и тем же уровнем приоритета, они выбираются для запуска с одинаковым уровнем приоритета. Пользователь не в состоянии предугадать, какой из процессов выполняется и считывает строку данных; успешно созданный процесс печатает значение переменной i в момент его создания. Все другие процессы в конце концов будут запущены, но вполне возможно, что они не обнаружат введенной информации в списках для хранения вводных данных и их выполнение снова будет приостановлено. Вся процедура повторяется для каждой введенной строки; нельзя дать гарантию, что ни один из процессов не захватит все введенные данные.

Перейти на страницу:

Все книги серии Серия книг по программному обеспечению издательства prentice hall

Похожие книги