Выбрать главу

245:  sigaction(SIGTERM, &sact, NULL);

246:

247:  /* установить измененные настройки termios */

248:  tcsetattr(pf, TCSANOW, &pts);

249:  tcsetattr(STDIN_FILENO, TCSANOW, &sts);

250:

251:  ufds[0].fd = STDIN_FILENO;

252:  ufds[0].events = POLLIN;

253:  ufds[1].fd = pf;

254:  ufds[1].events = POLLIN;

255:

256:  do {

257:   int r;

258:

259:   r = poll(ufds, 2, -1);

260:   if ((r < 0) && (errno != EINTR))

261:    die(1, "неожиданный сбой poll", "");

262:

263:   /* сначала проверить возможность завершения */

264:   if ((ufds[0].revents | ufds[1].revents) &

265:    (POLLERR | POLLHUP | POLLNVAL)) {

266:    done = 1;

267:    break;

268:   }

269:

270:   if (ufds[1].revents & POLLIN) {

271:    /* pf содержит символы */

272:    i = read (pf, buf, BUFSIZE);

273:    if (i >= 1) {

274:     write(STDOUT_FILENO, buf, i);

275:    } else {

276:     done = 1;

277:    }

278:   }

279:   if (ufds[0].revents & POLLIN) {

280:    /* стандартный ввод содержит символы */

281:    i = read(STDIN_FILENO, buf, BUFSIZE);

282:    if (i >= 1) {

283:     if (raw) {

284:      write(pf, buf, i);

285:     } else {

286:      cook_buf(pf, buf, i);

287:     }

288:    } else {

289:     done = 1;

290:    }

291:   }

292:  } while (!done);

293:

294:  /* восстановить первоначальные настройки терминала и завершиться*/

295:  tcsetattr(pf, TCSANOW, &pots);

296:  tcsetattr(STDIN_FILENO, TCSANOW, &sots);

297:  exit(0);

298: }

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

Функция symbolic_speed() в строке 19 преобразует целочисленную скорость в символическую, которую поддерживает termios. К сожалению, termios не предназначен для работы с произвольными скоростями, так что каждая скорость, которую вы хотите использовать, должна быть частью интерфейса пользователь — ядро[110].

Обратите внимание, что предусмотрены и довольно высокие скорости. Не все последовательные порты поддерживают скорости 230 400 или 460 800 бит/с; в стандарте POSIX определены скорости лишь до 38 400 бит/с. Чтобы сделать эту программу переносимой, каждую строку над строкой, в которой устанавливается скорость 38 400 бит/с, потребуется расширить до трех строк, как показано ниже:

#ifdef В460800

if (speednum >= 460800) return B46000;

#end if

Это позволит пользователям устанавливать скорости выше тех, которые последовательные порты способны поддерживать, а исходный код теперь будет компилироваться в любой системе с POSIX termios. (Как упоминалось ранее в этой главе, любой последовательный порт может отказаться принять на обработку любую установку termios, которую он не способен поддержать, включая также установки скорости. Поэтому установка B460800 не означает, что можно установить скорость порта равной 460 800 бит/с.)

В строках 44—55 определяются глобальные переменные для передачи некоторых переменных обработчику сигналов и сам обработчик сигналов. Обработчик сигналов предназначен для восстановления настроек termios на обоих интерфейсах tty при доставке сигнала, поэтому ему необходимо получить доступ к структурам, содержащим старые настройки termios. Ему также необходимо знать файловый дескриптор или последовательный порт (файловый дескриптор для стандартных входных данных не меняется, поэтому компилируется в бинарный). Этот код идентичен коду в случае нормального пути завершения, который рассматривается позже. Обработчик сигналов затем прикрепляется к сигналам, которые завершат процесс, если они были проигнорированы.

Функции send_escape() и cook_buf() будут рассматриваться позже. Они используются как часть обработки входных данных в цикле ввода-вывода в конце функции main().

Условно скомпилированный sleep(600) в начале функции main() предназначен для отладки. С целью отладки программ, модифицирующих настройки termios для стандартных входных или выходных данных, лучше всего присоединить процесс в другой окне или терминальном сеансе. Однако это означает, что нельзя установить точку прерывания на основную функцию и проходить по одной инструкции за раз. Необходимо запустить программу, найти идентификатор ее процесса и присоединиться к ней из отладчика. Более подробно этот процесс описан далее в настоящей главе.

Поэтому если необходимо отладить код, запускаемый до того, как программа дождется входных данных, нужно перевести программу в режим ожидания, чтобы оставить время для присоединения. После прикрепления режим ожидания прерывается, поэтому длительный режим ожидания безопасен. Чтобы активизировать это свойство, скомпилируйте robin.с с опцией -DDSLEEP.

Игнорируя отладку, мы в первую очередь анализируем опции, используя библиотеку popt, описанную в главе 26, а затем открываем последовательный порт, с которым будем взаимодействовать.

Затем мы вызываем функцию tcgetattr(), чтобы получить существующую конфигурацию termios последовательного порта, а затем сохраняем копию в pots, чтобы восстановить ее по окончании.

Начиная со строки 183, мы модифицируем установки последовательного порта:

183: pts.c_lflag &= ~ICANON;

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

184: pts.c_lflag &= ~(ECHO | ECHOCTL | ECHONL);

Это отключает локальный эхо-контроль в последовательном порте:

185: pts.c_cflag |= HUPCL;

Если подключен модем, HUPCL сообщает ему об отбое при закрытии устройства конечной программой:

186: pts.с_сс[VMIN] = 1;

187: pts.с_сс[VTIME] = 0;

Когда tty находится в неформатируемом режиме, эти две установки определяют поведение системного вызова read(). Эта особая установка сообщает, что при вызове read() мы хотим, чтобы он подождал с возвратом, пока не считаются один или несколько байтов. Мы никогда не вызовем read(), пока не будем знать, что остался хотя бы один байт для чтения, потому это функциональный эквивалент неблокирующего read(). Определение VMIN и VTIME сложное, как показано далее в настоящей главе.

вернуться

110

На man-странице setserial описан способ обхода этого ограничения, специфический для Linux.