Андрей Робачевский - Операционная система UNIX
- Название:Операционная система UNIX
- Автор:
- Жанр:
- Издательство:BHV - Санкт-Петербург
- Год:1997
- Город:Санкт-Петербург
- ISBN:5-7791-0057-8
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Андрей Робачевский - Операционная система UNIX краткое содержание
Книга посвящена семейству операционных систем UNIX и содержит информацию о принципах организации, идеологии и архитектуре, объединяющих различные версии этой операционной системы.
В книге рассматриваются: архитектура ядра UNIX (подсистемы ввода/вывода, управления памятью и процессами, а также файловая подсистема), программный интерфейс UNIX (системные вызовы и основные библиотечные функции), пользовательская среда (командный интерпретатор shell, основные команды и утилиты) и сетевая поддержка в UNIX (протоколов семейства TCP/IP, архитектура сетевой подсистемы, программные интерфейсы сокетов и TLI).
Для широкого круга пользователей
Операционная система UNIX - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Следующие два вызова используются сервером только при взаимодействии, основанном на предварительном создании виртуального канала между сервером и клиентом.
Системный вызов listen(2) информирует систему, что сервер готов принимать запросы. Он имеет следующий вид:
#include
#include
int listen(int sockfd, int backlog);
Здесь параметр sockfd
определяет сокет, который будет использоваться для получения запросов. Предполагается, что сокет был предварительно связан с известным адресом. Параметр backlog
указывает максимальное число запросов на установление связи, которые могут ожидать обработки сервером. [45] Если в момент получения запроса на установление связи очередь ожидающих запросов достигла своего максимального значения, вызов connect(2) клиента завершится с ошибкой ECONNREFUSED для домена UNIX ( AF_UNIX ). Для других доменов результат зависит от того, поддерживает ли протокол повторную передачу запроса. Например, протокол TCP (домен AF_INET ) будет передавать повторные запросы, пока число запросов в очереди не уменьшится, либо не произойдет тайм-аут, определенный для протокола. В последнем случае вызов клиента завершится с ошибкой ETIMEDOUT .
Фактическую обработку запроса клиента на установление связи производит системный вызов
#include
#include
int accept(int sockfd, struct sockaddr *clntaddr,
int* addrlen);
Вызов accept(2) извлекает первый запрос из очереди и создает новый сокет, характеристики которого не отличаются от сокета sockfd
, и таким образом завершает создание виртуального канала со стороны сервера. Одновременно accept(2) возвращает параметры удаленного коммуникационного узла — адрес клиента clntaddr
и его размер addrlen
. Новый сокет используется для обслуживания созданного виртуального канала, а полученный адрес клиента исключает анонимность последнего. Дальнейший типичный сценарий взаимодействия имеет вид:
sockfd = socket(...);
Создать сокет
bind(sockfd, ...);
Связать его с известным локальным адресом
listen(sockfd, ...);
Организовать очередь запросов
for(;;) {
newsockfd = accept(sockfd, ...);
Получить запрос
if (fork() == 0) {
Породить дочерний процесс
close(sockfd);
Дочерний процесс
...
exit(0);
} else
close(newsockfd);
Родительский процесс
}
В этом сценарии, в то время как дочерний процесс обеспечивает фактический обмен данными с клиентом, родительский процесс продолжает "прослушивать" поступающие запросы, порождая для каждого из них отдельный процесс-обработчик. Очередь позволяет буферизовать запросы на время, пока сервер завершает вызов accept(2) и затем создает дочерний процесс. Заметим, что новый сокет newsockfd
, полученный в результате вызова accept(2) , адресует полностью определенный коммуникационный канал: протокол и полные адреса обоих узлов — клиента и сервера. Напротив, для сокета sockfd
определена только локальная часть канала. Это позволяет серверу продолжать использовать sockfd
для "прослушивания" последующих запросов.
Наконец, если для сокетов потока при приеме и передаче данных могут быть использованы стандартные вызовы read(2) и write(2) , то сокеты дата- грамм должны пользоваться специальными системными вызовами (эти вызовы также доступны для сокетов других типов):
#include
#include
int send(int s, const char *msg, int len, int flags);
int sendto(int s, const char *msg, int len, int flags,
const struct sockaddr* toaddr, int tolen);
int recv(int s, char *buf, int len, int flags);
int recvfrom(int s, char *buf, int len, int flags,
struct sockaddr* fromaddr, int* fromlen);
Функции send(2) и sendto(2) используются для передачи данных удаленному узлу, а функции recv(2) и recvfrom(2) — для их приема. Основным различием между ними является то, что функции send(2) и recv(2) могут быть использованы только для "подсоединенного" сокета, т.е. после вызова connect(2) .
Все эти вызовы используют в качестве первого аргумента дескриптор сокета, через который производится обмен данными. Аргумент msg
содержит сообщение длиной len
, которое должно быть передано по адресу toaddr
, длина которого составляет tolen
байтов. Для функции send(2) используется адрес получателя, установленный предшествовавшим вызовом connect(2) . Аргумент buf
представляет собой буфер, в который копируются полученные данные.
Параметр flags
может принимать следующие значения:
MSG_OOB |
Передать или принять экстренные данные вместо обычных |
MSG_PEEK |
Просмотреть данные, не удаляя их из системного буфера (последующие операции чтения получат те же данные) |
Пример использования сокетов
В заключение приведем пример использования сокетов для организации межпроцессного взаимодействия. Поскольку в данном разделе не затрагиваются сетевые вопросы, то и сокеты, которые будут использованы в примере, принадлежат домену UNIX. Как и в предыдущих примерах, функциональность нашей распределенной системы не отличается разнообразием: клиент посылает серверу сообщение "Здравствуй, Мир!", а сервер отправляет его обратно клиенту, который после получения выводит сообщение на экран.
В примере использованы сокеты датаграмм, которые в домене UNIX практически не отличаются от сокетов потока. В качестве адреса сервера предлагается имя файла ./echo.server(мы полагаем, что в системе запущен только один сервер из данного каталога). Предполагается, что клиенты заранее знают этот адрес. Сервер связывает созданный сокет с этим локальным адресом и таким образом регистрируется в системе. Начиная с этого момента он готов к получению и обработке сообщений. Сервер начинает бесконечный цикл, ожидая сообщений от клиентов, блокируясь на вызове recvfrom(2) . При получении сообщения сервер отправляет его обратно, вызывая sendto(2) .
#include
#include
#include
#define MAXBUF 256 char
buf[MAXBUF];
main() {
struct sockaddr_un serv_addr, clnt_addr;
int sockfd;
int saddrlen, caddrlen, max caddrlen, n;
/* Создадим сокет */
if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0} {
printf("Невозможно создать сокет\n");
exit(1);
}
/* Свяжем сокет с известным локальным адресом. Поскольку адрес
в домене UNIX представляет собой имя файла, который будет
создан системным вызовом bind(2), сначала удалим файл с этим
именем в случае, если он сохранился от предыдущего запуска
сервера */
unlink("./echo_server");
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sun_family = AF_UNIX;
strcpy(serv_addr.sun_path, "./echo.server");
saddrlen =
sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path);
Интервал:
Закладка: