Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

ОСиС_2008

.pdf
Скачиваний:
107
Добавлен:
29.05.2015
Размер:
2.65 Mб
Скачать

9. Лабораторный курс

355

но символ intr соответствует нажатию клавиши <Delete> или

<Ctrl>&<C>;

quit — приводит к посылке всем процессам оперативной группы сеанса, управляемого данным терминалом, сигнала SIGQUIT. Обычно символ quit соответствует нажатию клавиш

<Ctrl>&<\>;

susp — символ терминального останова. Ввод данного символа пользователем приводит к посылке всем процессам оперативной группы сеанса, управляемого данным терминалом, сигнала SIGTSTP. Стандартная реакция процесса на этот сигнал — переход процесса в состояние «Останов», в которое процесс может попасть из состояний «Задача», «Готов» и «Сон». («Останов» — дополнительное состояние процесса, существующее не во всех версиях UNIX.) Кроме того, оперативная группа процессов переводится в фоновый режим. Обычно символ susp соответствует нажатию клавиш <Ctrl>&<Z>.

С помощью утилиты stty пользователь может заменять клавиши (и соответствующие им коды), используемые для реализации управляющих символов: erase, kill, eof, intr, quit, susp. Пример:

$ stty erase “^f”

Здесь в качестве символа erase задается комбинация клавиш <Ctrl>&<f>. Обратите внимание, что и в других случаях часто вместо нажатия<Ctrl>&<f> можно набрать строку "^f”.

З а м е н и т е с помощью утилиты stty некоторые символы редактирования и выдачи сигналов. Проверьте результат такой замены.

9.8.3. Управляющая структура termios

Утилита stty, изменяющая кодировку управляющих символов, вносит изменения в управляющую структуру (дескриптор) дисциплины линии — termios. Аналогичные изменения можно выполнять и из программы процесса.

Для запоминания текущего состояния termios и для записи ее нового содержимого могут использоваться системные вызовы tcgetattr и tcsetattr:

#include <termios.h>

int tcgetattr(int ttyfd, struct termios *tsaved);

int tcsetattr(int ttyfd, int actions, const struct termios *tnev);

356

Одиноков В.В., Коцубинский В.П.

Первый вызов сохраняет текущее состояние терминала, которому соответствует номер файла ttyfd, в структуре tsaved. Второй вызов установит новое состояние терминала, заданное структурой tnev. При этом второй параметр этого вызова actions уточняет момент изменения атрибутов терминала. В файле <termios.h> определены возможные варианты этого параметра:

1)TCSANOW — немедленное изменение атрибутов терминала;

2)TCSADRAIN — изменение атрибутов сразу же после опустошения очереди вывода;

3)TCSAFLUSH — изменение атрибутов после опустошения очередей вывода и ввода.

Структура termios определена в файле <termios.h> и содержит:

tcflag_t c_iflag;

/* Режим ввода */

tcflag_t c_oflag;

/* Режим вывода */

tcflag_t c_cflag;

/* Управляющий режим */

tcflag_t c_lflag;

/* Режим дисциплины линии */

cc_t

c_cc[NCCS];

/* Управляющие символы */

c_iflag поле, которое служит для общего управления вводом с терминала. Каждый бит этого поля представляет собой флаг, задающий какую-то особенность этого ввода.

Три флаговые константы управляют обработкой входного символа cr (возврат каретки):

INLCR — преобразовать символ lf (перевод строки) в cr; IGNCR игнорировать cr;

ICRNL преобразовать символ cr в символ lf.

Заметим, что сама UNIX ожидает в качестве последнего символа строки символ lf. Поэтому обычно используется константа

ICRNL.

Следуюшие три константы позволяют «тормозить» символьный обмен с терминалом в том случае, если пользователь (при выводе) или программный процесс (при вводе) не успевает обработать поступающую информацию:

IXON — разрешить старт-стопное управление выводом. То есть для временной приостановки вывода на терминал пользователь должен ввести символ stop (комбинация клавиш <Ctrl>&<S>). Для продолжения вывода вводится символ start — комбинация клавиш <Ctrl>&<Q>;

IXANY — продолжать вывод при нажатии любого символа. Эта константа дополняет предыдущую, позволяя использовать для продолжения вывода вместо символа start любой символ;

9. Лабораторный курс

357

IXOFF — разрешить старт-стопное управление вводом. Если установлен соответствующий флаг, система сама посылает терминалу символ stop в том случае, если заполнен буфер ввода. Когда в этом буфере появится место, терминалу будет передан символ start.

Каждая из перечисленных флаговых констант имеет такую же длину, как и поле c_iflag. Причем только один бит (флаг) установлен в 1. Все остальные биты флаговой константы сброшены в 0. Поэтому для установки какого-либо флага в поле c_iflag достаточно выполнить побитовое логическое сложение этого поля с соответствующей флаговой константой. Например, пусть tdes — структура типа termios. Тогда для игнорирования символа cr достаточно записать оператор:

tdes.c_iflag |= IGNCR

Для сброса требуемого флага в поле c_iflag достаточно выполнить побитовое логическое умножение этого поля с инверсией соответствующей флаговой константой. Например, для отмены игнорирования символа cr достаточно записать оператор:

tdes.c_iflag &= ~IGNCR

c_oflag поле, которое служит для общего управления выводом на терминал. Каждый бит этого поля представляет собой флаг, задающий какую-то особенность этого вывода. Некоторые из флаговых констант:

OPOST — содержит наиболее важный флаг в этом поле. Если он сброшен, то выводимые символы передаются без изменений. Иначе символы подвергаются преобразованию, заданному остальными флагами этого поля;

ONLCR — преобразовать символ перевода строки lf в символ возврата каретки cr и символ lf;

OCRNL – преобразовать символ cr в символ lf.

c_cflag поле, содержащее параметры, управляющие портом терминала: скорость ввода-вывода, контроль четности и т.д. Обычно эти параметры задаются самой UNIX.

c_lflag поле, задающее режим дисциплины линии. Оно содержит флаги, задаваемые следующими константами:

ICANON — канонический построчный ввод. Если флаг сброшен, то неканонический ввод;

ISIG —– разрешить обработку символов прерываний (intr и quit). Если флаг сброшен, то при получении этих символов сигналы

358

Одиноков В.В., Коцубинский В.П.

процессам не посылаются, а сами символы intr и quit передаются в программу без изменений;

IEXTEN — разрешить дополнительную, зависящую от реализации обработку вводимых символов;

ECHO — разрешить отображение вводимых символов на экране;

ECHOE — отображать символ удаления erase как «erase– пробел–erase». В результате курсор сначала переместится на одну позицию влево, во-вторых, стоящий в этой позиции символ будет затерт пробелом (при этом курсор вернется на соседнюю позицию вправо), а в-третьих, курсор опять переместится на соседнюю позицию слева, указывая таким образом на первую свободную позицию;

TOSTOP — посылать сигнал SIGTTOU при попытке вывода фонового процесса.

c_cc — массив, содержащий специальные символы редактирования и выдачи сигналов, рассмотренные ранее. Каждый символ занимает в массиве c_cc позицию, задаваемую соответствующей константой из файла <termios.h>:

VERASE — символ стирания erase; VKILL символ удаления строки kill;

VSTOP символ остановки передачи данных stop; VSTART символ продолжения передачи данных start; VEOF символ конца файла eof ;

VINTR символ прерывания intr; VQUIT символ завершения quit;

VSUSP символ временной приостановки выполнения susp.

Пример

Следующий фрагмент программы изменяет значение символа quit для терминала, выполняющего стандартный ввод (номер фай-

ла — 0):

struct termios tdes;

/* Получение настроек терминала */

tcgetattr(0, &tdes);

 

tdes.c_cc[VQUIT] = “^y”;

/* CTRL–Y */

/* Изменение настроек терминала */ tcsetattr(0, TCSAFLUSH, &tdes);

9.8.4. Задание

9. Лабораторный курс

359

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

1)задает старт-стопное управление выводом на экран. Причем для продолжения вывода может использоваться любой символ;

2)задает новые значения (по вашему выбору) символов стирания erase, удаления строки kill, прерывания intr, временной приостановки выполнения susp.

Проверить произведенные установки: а) с помощью утилиты cat выполнить вывод на экран достаточно большого текстового файла; б) выполнить редактирование строки символов, используя символы erase и kill; в) из командной строки запустить один оперативный и один фоновый процесс, выполняющие бесконечные циклы, а затем проверить на них действие сигналов intr и susp.

Примечание. После выполнения перечисленных действий требуется восстановить прежнее состояние терминала, так как иначе другие процессы «не поймут» новые настройки терминала.

9.9.Лабораторная работа № 9. Датаграммные локальные каналы

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

9.9.1. Подготовка к выполнению работы

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

1)понятие информационного канала и его характеристики

(п. 2.5.1);

2)сокеты (п. 4.6.3) — понятие сокета; имена сокетов; системные вызовы для реализации датаграммного канала;

3)понятие протокола (подразд. 3.4).

9.9.2. Порядок выдачи системных вызовов

В качестве простейшего примера создания и использования локального датаграммного канала рассмотрим получение двумя процессами «эха» символьной строки. В этом примере процесс-

360

Одиноков В.В., Коцубинский В.П.

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

 

9. Лабораторный курс

 

361

 

СЕРВЕР

 

 

 

socket

 

КЛИЕНТ

 

 

 

 

(СОЗДАТЬ

socket

 

 

СОКЕТ)

 

 

(СОЗДАТЬ

 

 

 

 

 

СОКЕТ)

 

 

bind

 

 

 

(СВЯЗАТЬ

bind

 

СОКЕТ_АДРЕС)

(СВЯЗАТЬ

 

 

 

 

 

СОКЕТ_АДРЕС)

 

recvfrom

sendto

 

 

(ПОЛУЧИТЬ

 

 

ДАТАГРАММУ)

 

 

(ПОСЛАТЬ

 

 

 

 

 

Ожидание

Данные ДАТАГРАММУ)

 

 

 

датаграммы

(строка)

 

 

 

 

 

recvfrom

 

 

 

 

 

(ПОЛУЧИТЬ

 

 

 

 

ДАТАГРАММУ)

 

sendto

 

Ожидание

 

(ПОСЛАТЬ

Данные

датаграммы

 

ДАТАГРАММУ)

(«эхо» строки)

 

)

 

 

 

 

close

(ЗАКРЫТЬ СОКЕТ)

Рис. 69. Пример создания и использования датаграммного канала

9.9.3. Создание сокета

Создание сокета выполняет процесс-«владелец», используя системный вызов socket. Его определение:

#include <sys/types.h> #include <sys/socket.h>

int socket(int domain, int type, int protocol);

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

362

Одиноков В.В., Коцубинский В.П.

domain — определяет область применения создаваемого канала: AF_UNIX — процессы, соединяемые каналом, выполняются на одной ЭВМ; AF_INET — соединяемые процессы выполняются на удаленных ЭВМ, подключенных к сети Internet;

type — определяет устойчивость создаваемого канала: SOCK_ STREAM — виртуальное соединение; SOCK_DGRAM — передача датаграмм;

protocol — тип протокола, которому должны следовать подпрограммы ядра, обслуживающие сокеты, расположенные по обоим концам информационного канала. Обычно в качестве данного параметра задают 0, что эквивалентно выбору протокола самим ядром.

Если системный вызов socket завершился с ошибкой, то он возвращает значение (–1). Иначе в результате своего выполнения socket возвращает номер нового сокета, который может использоваться далее процессом-«владельцем» во всех операциях с данным сокетом (и с соответствующим информационным каналом).

Пример создания сокета:

int

sock;

if

((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)

{

printf(“Невозможно создать сокет\n”); exit(1);

}

 

9.9.4. Связывание сокета со своим адресом

Только что созданный сокет еще не имеет адреса (внешнего имени). Для получения такого адреса необходимо выполнить системный вызов bind:

#include <sys/types.h> #include <sys/socket.h>

int bind(int sockfd, struct sockaddr *address, int add_len);

где sockfd — номер сокета, созданного с помощью вызова socket; address — адрес (указатель) структуры типа sockaddr; add_len — длина структуры типа sockaddr.

В случае успешного завершения вызова bind он возвращает значение 0, иначе (–1).

Структура sockaddr — структура обобщенного адреса сокета, которая определяется в файле <sys/socket.h>:

9. Лабораторный курс

363

struct

sockaddr {

 

sa_family_t sa_family;

/* Область применения канала */

char

sa_data[ ];

/* Внешнее имя сокета */

};

 

 

При выполнении программой процесса конкретного вызова bind структура sockaddr заменяется или структурой sockaddr_un, используемой при внутримашинном обмене, или структурой sockaddr_in, используемой при взаимодействии удаленных процессов. Структура sockaddr_un определяется в файле <sys/un.h>:

struct

sockaddr_un {

 

sa_family_t sun_family;

/* ==AF_UNIX */

char

sun_path[108];

/* Имя-путь файла-сокета */

};

 

 

Применение вызова bind в программе зависит от роли, которую играет данная программа среди взаимодействующих программ. Адрес сокета, принадлежащего программе-серверу, всегда известен заранее, для того чтобы программы-клиенты могли посылать свои запросы по этому адресу. Пример выполнения вызова bind в сервере:

#define LEN sizeof(struct sockaddr_un) struct sockaddr_un server;

int sock, s_len;

. . . . . . . . . . . . /* Создание сокета с номером sock*/

/* Задание адреса своего сокета */ unlink(“/home/vlad/abc.server”); bzero(&server, LEN); server.sun_family = AF_UNIX;

strcpy(server.sun_path, “/home/vlad/abc.server”); s_len=sizeof(server.sun_family)+strlen(server.sun_path);

/* Связывание сокета сервера с его адресом */

if (bind(sock, (struct sockaddr *) &server, s_len) < 0)

{

printf(“Ошибка связывания сокета с адресом\n”); exit(1);

}

В приведенном фрагменте программы производится связывание адреса (имени-пути /home/vlad/abc.server) сокета с номером этого сокета sock. Возможно, ранее это имя-путь уже использовалось для создания файла или сокета. Поэтому в начале данного фрагмента производится уничтожение файла (сокета), созданного

364

Одиноков В.В., Коцубинский В.П.

прежде. Далее структура server адреса сокета сначала обнуляется, а затем заполняется. После этого определяется длина этой структуры s_len и выполняется вызов bind.

В отличие от сервера, имя-путь сокета клиента может быть заранее не известно и может быть выбрано клиентом в известной степени произвольно. Единственное требование — в пределах системы имя-путь сокета должно быть уникальным. Для получения уникального внешнего имени сокета удобно использовать библиотечную функцию mktemp, которая, получая на входе шаблон имени-пути файла, получает уникальное имя-путь на основе номера текущего процесса. Например, для шаблона /tmp/clnt.XXX производится замена трех символов «X». Соответствующий фрагмент программы-клиента:

#define LEN sizeof(struct sockaddr_un) struct sockaddr_un client;

int sock, c_len;

. . . . . . . . . . . .

/* Создание сокета с номером sock*/ /* Задание адреса своего сокета */ bzero(&client, LEN);

client.sun_family = AF_UNIX; strcpy(client.sun_path, “/tmp/clnt.XXX”); mktemp(client.sun_path);

c_len= sizeof(client.sun_family) + strlen(client.sun_path); /* Связывание сокета клиента со своим адресом */ if (bind(sock, (struct sockaddr *) &client, c_len) < 0)

{

printf(“Ошибка связывания сокета с адресом\n”); exit(1);

}

9.9.5. Прием и передача датаграмм

Выполнение вызовов socket и bind каждым из двух взаимодействующих процессов приведет к созданию двух сокетов, каждый из которых имеет как локальное, так и внешнее имя (адрес). Этого достаточно для создания датаграммного информационного канала. Пользуясь этим каналом, процесс может отправлять свои датаграммы другому процессу, а также принимать ответные датаграммы.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]