Книга: Разработка приложений в среде Linux. Второе издание

16.3.2. Последовательные коммуникации

16.3.2. Последовательные коммуникации

В качестве примера программирования обоих концов tty рассмотрим программу, подключающую текущий терминал к последовательному порту. На одном tty программа под названием robin сообщается с вами во время набора. На другом tty она взаимодействует с последовательным портом. С целью мультиплексирования вводных и выходных данных на локальном tty и последовательном порте программа использует системный вызов poll(), описанный в главе 13.

Ниже приведен полный код программы robin.с, за которым даны объяснения.

  1: /* robin.с */
  2:
  3: #include <sys/poll.h>
  4: #include <errno.h>
  5: #include <fcntl.h>
  6: #include <popt.h>
  7: #include <stdio.h>
  8: #include <stdlib.h>
  9: #include <signal.h>
 10: #include <string.h> /* для strerror() */
 11: #include <termios.h>
 12: #include <unistd.h>
 13:
 14: void die(int exitcode, const char *error, const char *addl) {
 15:  if (error) fprintf(stderr, "%s: %sn", error, addl);
 16:  exit(exitcode);
 17: }
 18:
 19: speed_t symbolic_speed(int speednum) {
 20:  if (speednum >= 460800) return B460800;
 21:  if (speednum >= 230400) return B230400;
 22:  if (speednum >= 115200) return B115200;
 23:  if (speednum >= 57600) return B57600;
 24:  if (speednum >= 38400) return B38400;
 25:  if (speednum >= 19200) return B19200;
 26:  if (speednum >= 9600) return B9600;
 27:  if (speednum >= 4800) return B4800;
 28:  if (speednum >= 2400) return B2400;
 29:  if (speednum >= 1800) return B1800;
 30:  if (speednum >= 1200) return B1200;
 31:  if (speednum >= 600) return B600;
 32:  if (speednum >= 300) return B300;
 33:  if (speednum >= 200) return B200;
 34:  if (speednum >= 150) return B150;
 35:  if (speednum >= 134) return B134;
 36:  if (speednum >= 110) return B110;
 37:  if (speednum >= 75) return B75;
 38:  return B50;
 39: }
 40:
 41: /* Это нужно для области видимости в пределах файла, так что
 42:  * их можно будет использовать в обработчиках сигналов */
 43: /* старые настройки порта termios для восстановления */
 44: static struct termios pots;
 45: /* старые настройки stdout/stdin termios для восстановления */
 46: static struct termios sots;
 47: /* файловый дескриптор порта */
 48: int pf;
 49:
 50: /* восстановить первоначальные настройки терминала при выходе */
 51: void cleanup_termios(int signal) {
 52:  tcsetattr(pf, TCSANOW, &pots);
 53:  tcsetattr(STDIN_FILENO, TCSANOW, &sots);
 54:  exit(0);
 55: }
 56:
 57: /* обработать одиночный управляющий символ */
 58: void send_escape(int fd, char c) {
 59:  switch (c) {
 60:  case 'q':
 61:   /* восстановить настройки termios и выйти */
 62:   cleanup_termios(0);
 63:   break;
 64:  case 'b':
 65:   /* послать символ разрыва*/
 66:   tcsendbreak(fd, 0);
 67:   break;
 68:  default:
 69:   /* пропустить символ */
 70:   /* "C- C-" sends "C-" */
 71:   write(fd, &c, 1);
 72:   break;
 73:  }
 74:  return;
 75: }
 76:
 77: /* обработать управляющие символы, записывая их в вывод */
 78: void cook_buf(int fd, char * buf, int num) {
 79:  int current = 0;
 80:  static int in_escape = 0;
 81:
 82:  if (in_escape) {
 83:   /* cook_buf последний раз вызывался с незавершенной
 84:      управляющей последовательностью */
 85:   send_escape(fd, buf[0]);
 86:   num--;
 87:   buf++;
 88:   in_escape = 0;
 89:  }
 90:  while (current < num) {
 91: # define CTRLCHAR(c) ((c)-0x40)
 92:   while ((current < num) && (buf[current] != CTRLCHAR('W')))
 93:    current++;
 94:   if (current) write (fd, buf, current);
 95:   if (current < num) {
 96:    /* найден управляющий символ */
 97:    current++;
 98:    if (current >= num) {
 99:     /*интерпретировать первый символ следующей последовательности*/
100:     in_escape = 1;
101:     return;
102:    }
103:    send_escape(fd, buf[current]);
104:   }
105:   num -= current;
106:   buf += current;
107:   current = 0;
108:  }
109:  return;
110: }
111:
112: int main(int argc, const char * argv[]) {
113:  char с; /* используется для разбора аргументов */
114:  struct termios pts; /* настройки termios для порта */
115:  struct termios sts; /* настройки termios для stdout/stdin */
116:  const char *portname;
117:  int speed = 0; /* используется при разборе аргументов для скорости */
118:  struct sigaction sact; /* используется для инициализации обработчика сигналов */
119:  struct pollfd ufds[2]; /* взаимодействие с poll() */
120:  int raw = 0; /* неформатированный режим? */
121:  int flow = 0; /* тип управления потоком, если применяется*/
122:  int crnl = 0; /* посылать ли символ возврата каретки с символом новой строки? */
123:  int i = 0; /* используется в цикле мультиплексирования*/
124:  int done = 0; 125: # define BUFSIZE 1024
126:  char buf[BUFSIZE];
127:  poptContext optCon; /* контекст опций командной строки */
128:  struct poptOption optionsTable[] = {
129:   { "bps", 'b', POPT_ARG_INT, &speed, 0,
130:     "скорость передачи сигналов, бит/с",
131:     "<BPS>" },
132:   { "crnl", 'с', POPT_ARG_VAL, &crnl, 'с',
133:     "посылать символ возврата каретки с символом новой строки", NULL },
134:   { "hwflow", 'h', POPT_ARG_VAL, &flow, 'h',
135:     "использовать аппаратное управление потоком", NULL },
136:   { "swflow", 's', POPT_ARG_VAL, &flow, 's',
137:     "использовать программное управление потоком", NULL },
138:   { "noflow", 'n', POPT_ARG_VAL, &flow, 'n',
139:     "отключить управление потоком", NULL },
140:   { "raw", 'r', POPT_ARG_VAL, &raw, 1,
141:     "включить неформатированный режим", NULL },
142:   POPT_AUTOHELP
143:   { NULL, '', 0, NULL, '', NULL, NULL }
144:  };
145:
146: #ifdef DSLEEP
147:  /* ожидать 10 минут, что позволить подключить отладчик */
148:  sleep(600);
149: #endif
150:
151:  optCon = poptGetContext("robin", argc, argv, optionsTable, 0);
152:  poptSetOtherOptionHelp(optCon, "<port>");
153:
154:  if (argc < 2) {
155:   poptPrintUsage(optCon, stderr, 0);
156:   die(1, "He достаточно аргументов", "");
157:  }
158:
159:  if ((с = poptGetNextOpt(optCon)) < -1) {
160:   /* ошибка во время обработки опций */
161:   fprintf(stderr, "%s: %sn",
162:    poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
163:    poptStrerror(c));
164:   return 1;
165:  }
166:  portname = poptGetArg(optCon);
167:  if (!portname) {
168:   poptPrintUsage(optCon, stderr, 0);
169:   die(1, "He указано имя порта", "");
170:  }
171:
172:  pf = open(portname, O_RDWR);
173:  if (pf < 0) {
174:   poptPrintUsage(optCon, stderr, 0);
175:   die(1, strerror(errno), portname);
176:  }
177:  poptFreeContext(optCon);
178:
179:  /* изменить конфигурацию порта */
180:  tcgetattr(pf, &pts);
181:  pots = pts;
182:  /* некоторые настройки устанавливаются произвольно */
183:  pts.c_lflag &= ~ICANON;
184:  pts.c_lflag &= ~(ECHO | ECHOCTL | ECHONL);
185:  pts.c_cflag |= HUPCL;
186:  pts.c_cc[VMIN] = 1;
187:  pts.c_cc[VTIME] = 0;
188:
189:  /* Стандартная обработка CR/LF: это неинтеллектуальный терминал.
190:   * Не транслируется:
191:   * нет NL -> отображение CR/NL в выводе,
192:   * нет CR -> отображение NL во вводе.
193:   */
194:  pts.c_oflag &= ~ONLCR;
195:  pts.c_iflag &= ~ICRNL;
196:
197:  /* Теперь перейти на сторону локального терминала */
198:  tcgetattr(STDIN_FILENO, &sts);
199:  sots = sts;
200:  /* и снова несколько произвольных настроек */
201:  sts.c_iflag &= ~(BRKINT | ICRNL);
202:  sts.c_iflag |= IGNBRK;
203:  sts.c_lflag &= ~ISIG;
204:  sts.c_cc[VMIN] = 1;
205:  sts.c_cc[VTIME] = 0;
206:  sts.c_lflag &= ~ICANON;
207:  /* нет локального эхо: разрешить эхо-контроль на другом конце */
208:  sts.c_lflag &= ~(ECHO | ECHOCTL | ECHONL);
209:
210:  /* обработка опций сейчас будет модифицировать pts и sts */
211:  switch (flow) {
212:  case 'h' :
213:   /* аппаратное управление потоком */
214:   pts.c_cflag |= CRTSCTS;
215:   pts.c_iflag &= ~(IXON | IXOFF | IXANY);
216:   break;
217:  case 's':
218:   /* программное управление потоком */
219:   pts.c_cflag &= ~CRTSCTS;
220:   pts.c_iflag |= IXON | IXOFF | IXANY;
221:   break;
222:  case 'n':
223:   /* отключение управления потоком */
224:   pts.c_cflag &= ~CRTSCTS;
225:   pts.c_iflag &= ~(IXON | IXOFF | IXANY);
226:   break;
227:  }
228:  if (crnl) {
229:   /* послать CR с NL */
230:   pts.c_oflag |= ONLCR;
231:  }
232:
233:  /* скорость не изменяется, пока не будет указано -b */
234:  if (speed) {
235:   cfsetospeed(&pts, symbolic_speed(speed));
236:   cfsetispeed(&pts, symbolic_speed(speed));
237:  }
238:
239:  /* установить обработчик сигналов для восстановления
240:   * старого обработчика termios */
241:  sact.sa_handler = cleanup_termios;
242:  sigaction(SIGHUP, &sact, NULL);
243:  sigaction(SIGINT, &sact, NULL);
244:  sigaction(SIGPIPE, &sact, NULL);
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 сложное, как показано далее в настоящей главе.

Настройки termios по умолчанию включают преобразование некоторых символов в конце строки. Это подходит для модемных линий и терминальных сеансов, но при соединении двух tty повтор преобразования нежелателен. Нежелательно отображать символы новой строки на пару "возврат каретки/перевод строки" при выводе, а также принимаемый возврат каретки — на перевод строки при вводе, поскольку мы уже получаем пары "возврат каретки/перевод строки" с удаленной системы.

194: pts.c_oflag &= ~ONLCR;
195: pts.c_iflag &= ~ICRNL;

Без этих двух строк использование программы robin для соединения с другим компьютером Linux или Unix приведет к тому, что удаленная система увидит, что вы нажимаете клавишу <Enter> дважды всякий раз, когда вы нажимаете ее один раз. И всякий раз, когда она будет пытаться отобразить новую строку на вашем экране, вы будете видеть две строки. Поэтому каждый раз при нажатии <Enter> (предполагая, что у вас получится зарегистрироваться в этих установках терминала) вы увидите отраженные подсказки. Если же вы запустите vi, то увидите символы ~, расположенные через строку, а не в каждой строке.

Таким образом, мы внесли в настройки termios все изменения, которые необходимо сделать перед обработкой аргументов командной строки. Теперь приступим к модификации настроек tty, предоставляющего стандартные входные и выходные данные. Поскольку это один tty, необходимо обработать лишь один файловый дескриптор из пары. Мы отдали предпочтение стандартному вводу, выбрав соглашение, установленное программой stty. И снова все начинается с получения и сохранения атрибутов.

Затем потребуется модифицировать несколько флагов:

201: sts.c_iflag &= ~(BRKINT | ICRNL);
202: sts.c_iflag |= IGNBRK;
203: sts.c_lflag &= ~ISIG;

Отключение BRKINT играет роль только в том случае, если robin вызывается из регистрационного сеанса, присоединенного к другому последовательному порту, на котором можно получить разрыв. Его отключение означает, что драйвер tty не посылает SIGINT в robin, когда в стандартном устройстве ввода robin возникает разрыв, поскольку robin не может сделать ничего полезного при получении разрыва. Отключение ICRNL предотвращает сообщение в robin о любых символах возврата каретки ('r') как о символах новой строки ('n'). Как и при отключении BRKINT это действует лишь тогда, когда сеанс регистрации присоединяется к другому последовательному порту. Также это действует тогда, когда символы возврата каретки не игнорируются (то есть если не установлен флаг IGNCR).

Функция IGNBRK заставляет драйвер tty игнорировать разрывы. Включение IGNBRK будет здесь излишним. При установке IGNBRK игнорируется BRKINT. Но не беда, если вы установите обе функции.

Это флаги обработки входных данных. Также модифицируется локальный флаг обработки: отключается ISIG. Это предотвращает драйвер tty от передачи SIGINT, SIGQUIT и SIGTSTP при получении соответствующего символа (INTR, QUIT или SUSP). Мы делаем это потому, что хотим переслать эти символы на удаленную систему (или на другое устройство, подключенное к последовательному порту) для последующей обработки там.

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

По умолчанию мы оставляем последовательный порт в том состоянии управления потоком, в котором мы его находим. Однако в строке 212 есть опции аппаратного управления потоком (использующего управляющие провода CTS и RTS), программного управления потоком (резервирование ^S и ^Q для STOP и START соответственно) и полного отключения управления потоком.

212: case 'h':
213:  /* аппаратное управление потоком */
214:  pts.c_cflag |= CRTSCTS;
215:  pts.c_iflag &= ~(IXON | IXOFF | IXANY);
216:  break;
217: case 's' :
218:  /* программное управление потоком */
219:  pts.c_cflag &= ~CRTSCTS;
220:  pts.c_iflag |= IXON | IXOFF | IXANY;
221:  break;
222: case 'n':
223:  /* отключение управления потоком */
224:  pts.q._cflag &= ~CRTSCTS;
225:  pts.c_iflag &= ~(IXON | IXOFF | IXANY);
226:  break;

Обратите внимание, что программное управление потоком включает три флага.

IXON Прекратить пересылать выходные данные при получении символа STOP (обычно ^S) и начать заново при получении символа START (обычно ^Q).
IXOFF Передать символ STOP, когда во входящем буфере накопится слишком много данных. В случае прочтения достаточного количества данных отправите символ START.
IXANY Позволить любому принятому символу, не только START, перезапустить вывод. (Этот флаг обычно реализован в системах Unix, но не определен в POSIX.)

Когда другая программа использует robin как вспомогательную, может помешать обработка спецсимволов (robin обычно интерпретирует последовательность ^), поэтому во избежание такой обработки устанавливается неформатируемый режим. В строке 120 мы предоставляем переменную, определяющую, включен ли неформатируемый режим; по умолчанию этот режим не включается. В строке 140 мы сообщаем popt о том, как информировать, когда в командной строке была указана опция -r или -raw, включающая неформатируемый режим.

Некоторые системы требуют передачи им символа возврата каретки для представления новой строки. Слово "системы" здесь следует понимать буквально; например, это применимо ко многим интеллектуальным периферийным устройствам, например, источникам бесперебойного питания (UPS) с последовательными портами, поскольку они предназначены для функционирования в DOS, где пара "возврат каретки/перевод строки" всегда используется для обозначения новой строки. В строке 228 определяется это DOS-ориентированное поведение.

228: if (crnl) {
229:  /* послать CR с NL */
230:  pts.c_oflag |= ONLCR;
231: }

Последняя часть обработки опций управляет скоростью передачи в битах в секунду[111]. Вместо включения огромного вложенного оператора выбора мы вызываем уже описанную ранее функцию symbolic_speed(), чтобы получить speed_t, понимаемый termios, как показано в строке 233.

233: /* скорость не изменяется, пока не будет указано -b */
234: if (speed) {
235:  cfsetospeed(&pts, symbolic_speed(speed));
236:  cfsetispeed(&pts, symbolic_speed(speed));
237: }

Перед тем, как зафиксировать изменения наших копий структур termios, в строке 241 регистрируются обработчики для важных сигналов, которые могут в противном случае вызвать уничтожение процесса и оставить tty в неформатируемом состоянии. Более подробно обработчики сигналов рассматриваются в главе 12.

241: sact.sa_handler = cleanup_termios;
242: sigaction(SIGHUP, &sact, NULL);
243: sigaction(SIGINT, &sact, NULL);
244: sigaction(SIGPIPE, &sact, NULL);
245: sigaction(SIGTERM, &sact, NULL);

Как только обработчик сигналов окажется на месте для восстановления старых настроек termios в случае уничтожения robin, мы можем благополучно обновить установки termios.

248: tcsetattr(pf, TCSANOW, &pts);
249: tcsetattr(STDIN_FILENO, TCSANOW, &sts);

На этом этапе программа robin готова читать и записывать символы. У robin есть два файловых дескриптора для чтения: данные с последовательного порта и данные с клавиатуры. Для мультиплексирования ввода-вывода между четырьмя файловыми дескрипторами используется poll() (см. главу 13).

Цикл poll() делает упрощенческое предположение о том, что он всегда может записать столько, сколько в состоянии прочитать. Это почти всегда верно и не вызывает проблем на практике, поскольку блокирование на короткие периоды незаметно при нормальных условиях. Цикл никогда не считывает из файлового дескриптора, пока poll() не сообщит, что файловый дескриптор ожидает считывания данных, чтобы мы знали, что блокировки во время чтения нет.

Для данных, поступающих с клавиатуры, может понадобиться обработка управляющих последовательностей перед записью, если пользователь не выбрал неформатируемый режим при запуске robin. Вместо включения этого кода в цикл мы вызываем функцию cook_buf() (строка 78), которая при необходимости обращается к send_escape() (строка 58). Обе эти функции просты. Единственный трюк состоит в том, что cook_buf() может быть вызвана один раз с управляющим символом, а затем второй раз с интерпретируемым символом, а также в оптимизации количества вызовов функции write().

Функция cook_buf() вызывает функцию send_escape() один раз для каждого символа, которому предшествует неотменяемый управляющий символ ^. Символ q восстанавливает исходные установки termios и завершается вызовом обработчика сигнала (с фальшивым номером сигнала 0), что восстанавливает настройки termios перед выходом. Символ b генерирует состояние разрыва, которое является длинной строкой, состоящей из нулей. Любой другой символ, включая второй управляющий символ ^, передается в последовательный порт без изменений.

Если какой-то из входных файловых дескрипторов вернет признак конца файла, robin выходит из цикла poll() и передает управление обработке завершения, что соответствует обработчику сигнала: восстановление старых настроек termios на обоих входных файловых дескрипторов и завершение. В неформатируемом режиме существует только два способа завершения robin: закрыть один из файловых дескрипторов или передать ей сигнал.

Оглавление книги


Генерация: 1.383. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз