7.11. Функция fcntl
7.11. Функция fcntl
Сокращение fcntl означает «управление файлами» (file control). Эта функция выполняет различные операции управления дескрипторами. Перед описанием этой функции и ее влияния на сокет нам нужно составить некоторое более общее представление о ее возможностях. В табл. 7.9 приводятся различные операции, выполняемые функциями fcntl и ioctl и маршрутизирующими сокетами.
Таблица 7.9. Операции функций fcntl и ioctl и маршрутизирующих сокетов
Операция fcntl ioctl Маршрутизирующий сокет Posix.1g Установка сокета для неблокируемого ввода-вывода F_SETFL, O_NONBLOCK FIONBIO fcntl Установка сокета для ввода-вывода, управляемого сигналом F_SETFL, O_ASYNC FIOASYNC fcntl Установка владельца сокета F_SETOWN SIOCSPGRP или FIOSETOWN fcntl Получение владельца сокета F_GETOWN SIOCGPGRP или FIOGETOWN fcntl Получение текущего количества байтов в приемном буфере сокета FIONREAD Проверка, находится ли процесс на отметке внеполосных данных SIOCATMARK sockatmark Получение списка интерфейсов SIOCGIFCONF Sysctl Операции интерфейсов SIOC[GS]IFxxx Кэш-операции ARP SIOCxARP RTM_xxx Операции таблицы маршрутизации SIOGxxxRT RTM_xxxПервые шесть операций могут применяться к сокетам любым процессом, следующие две (операции над интерфейсами) используются реже, а последние две (ARP и таблица маршрутизации) выполняются администрирующими программами, такими как ifconfig и route. О различных операциях функции ioctl мы поговорим подробнее в главе 17, а о маршрутизирующих сокетах — в главе 18.
Существует множество способов выполнения первых четырех операций, но, как указано в последней колонке, стандарт POSIX определяет, что функция fcntl является предпочтительным способом. Отметим также, что POSIX предлагает функцию sockatmark (см. раздел 24.3) как наиболее предпочтительный способ тестирования на предмет пребывания процесса на отметке внеполосных данных. Оставшиеся операции с пустой последней колонкой не стандартизованы POSIX.
ПРИМЕЧАНИЕ
Отметим также, что первые две операции, устанавливающие сокет для неблокируемого ввода-вывода и для ввода-вывода, управляемого сигналом, традиционно применялись с использованием команд FNDELAY и FASYNC функции fcntl. POSIX определяет константы О_xxx.
Функция fcntl предоставляет следующие возможности, относящиеся к сетевому программированию:
? Неблокируемый ввод-вывод. Мы можем установить флаг состояния файла O_NONBLOCK, используя команду F_SETFL для отключения блокировки сокета. Неблокируемый ввод-вывод мы описываем в главе 16.
? Управляемый сигналом ввод-вывод. Мы можем установить флаг состояния файла O_ASYNC, используя команду F_SETFL, после чего при изменении состояния сокета будет генерироваться сигнал SIGIO. Мы рассмотрим это в главе 25.
? Команда F_SETOWN позволяет нам установить владельца сокета (идентификатор процесса или идентификатор группы процессов), который будет получать сигналы SIGIO и SIGURG. Первый сигнал генерируется, если для сокета включен управляемый сигналом ввод-вывод (см. главу 25), второй — когда для сокета приходят новые внеполосные (out-of-band data) данные (см. главу 24). Команда F_GETOWN возвращает текущего владельца сокета.
ПРИМЕЧАНИЕ
Термин «владелец сокета» определяется POSIX. Исторически реализации, происходящие от Беркли, называли его «идентификатор группы процессов сокета», потому что переменная, хранящая этот идентификатор, — это элемент so_pgid структуры socket [128, с. 438].
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* int arg */);
Возвращает: в случае успешного выполнения результат зависит от аргумента cmd, -1 в случае ошибки
Каждый дескриптор (включая сокет) имеет набор флагов, которые можно получить с помощью команды F_GETFL и установить с помощью команды F_SETFL. На сокет влияют следующие два флага:
? O_NONBLOCK — неблокируемый ввод-вывод;
? O_ASYNC — ввод-вывод, управляемый сигналом.
Позже мы опишем оба эти флага подробнее. Отметим, что типичный код, который устанавливает неблокируемый ввод-вывод с использованием функции fcntl, выглядит следующим образом:
int flags;
/* Делаем сокет неблокируемым */
if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
err_sys("F_GETFL error");
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) < 0)
err_sys("F_SETFL error");
Учтите, что вам может встретиться код, который просто устанавливает желаемый флаг:
/* Неправильный способ сделать сокет неблокируемым */
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
err_sys("F_SETFL error");
Хотя при этом и устанавливается флаг отключения блокировки, также снимаются все остальные флаги состояния файла. Единственный корректный способ установить один из этих флагов состояния файла — получить текущие флаги, с помощью операции логического ИЛИ добавить новый флаг, а затем установить флаги.
Следующий код сбрасывает флаг отключения блокировки в предположении, что переменная flags была задана с помощью вызова функции fcntl, показанного ранее:
flags &= ~O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) < 0)
err_sys("F_SETFL error");
Сигналы SIGIO и SIGURG отличаются от других тем, что они генерируются для сокета, только если сокету был присвоен владелец с помощью команды F_SETOWN. Целое значение аргумента arg для команды F_SETOWN может быть либо положительным, задающим идентификатор процесса, получающего сигнал, либо отрицательным, абсолютное значение которого — это идентификатор группы процессов, получающей сигнал. Команда F_GETOWN возвращает владельца сокета, так как возвращаемое значение функции fcntl — либо идентификатор процесса (положительное возвращаемое значение), либо идентификатор группы процессов (отрицательное значение, отличное от -1). Разница между заданием процесса и группы процессов, получающих сигнал, в том, что в первом случае сигнал будет получен только одиночным процессом, тогда как во втором случае его получают все процессы в группе.
Когда создается новый сокет с помощью функции socket, у него нет владельца. Сокет, создаваемый из прослушиваемого сокета, наследует от него принадлежность владельцу (как и многие другие параметры сокетов [128, с. 462-463].
Данный текст является ознакомительным фрагментом.