Книга: UNIX: разработка сетевых приложений
11.19. Функции gethostbyname_r и gethostbyaddr_r
11.19. Функции gethostbyname_r и gethostbyaddr_r
Чтобы превратить функцию, не допускающую повторное вхождение, такую как gethostbyname
, в повторно входимую, можно воспользоваться двумя способами.
1. Вместо заполнения и возвращения статической структуры вызывающий процесс размещает структуру в памяти, и функция, допускающая повторное вхождение, заполняет эту структуру. Эта технология используется для перехода от функции gethostbyname
(которая не допускает повторное вхождение) к функции gethostbyname_r
(которая допускает повторное вхождение). Но это решение усложняется, поскольку помимо того, что вызывающий процесс должен предоставить структуру hostent
для заполнения, эта структура также указывает на другую информацию: каноническое имя, массив указателей на псевдонимы, строки псевдонимов, массив указателей на адреса и сами адреса (см., например, рис. 11.2). Вызывающий процесс должен предоставить один большой буфер, используемый для дополнительной информации, и заполняемая структура hostent
будет содержать различные указатели на этот буфер. При этом добавляется как минимум три аргумента функции: указатель на заполняемую структуру hostent
, указатель на буфер, используемый для всей прочей информации, и размер этого буфера. Требуется также четвертый дополнительный аргумент — указатель на целое число, в котором будет храниться код ошибки, поскольку глобальная целочисленная переменная h_errno
больше не может использоваться. (Глобальная целочисленная переменная h_errno
создает ту же проблему повторного вхождения, которая описана нами для переменной errno
.)
Эта технология также используется функциями getnameinfo
и inet_ntop
.
2. Входящая функция вызывает функцию malloc
и динамически выделяет память. Это технология, используемая функцией getaddrinfo
. Проблема при таком подходе заключается в том, что приложение, вызывающее эту функцию, должно вызвать также функцию freeaddrinfo
, чтобы освободить динамическую память. Если эта функция не вызывается, происходит утечка памяти: каждый раз, когда процесс вызывает функцию, выделяющую память, объем памяти, задействованной процессом, возрастает. Если процесс выполняется в течение длительного времени (что свойственно сетевым серверам), то потребление памяти этим процессом с течением времени неуклонно растет.
Обсудим функции Solaris 2.x, допускающие повторное вхождение, не используемые для сопоставления имен с адресами, и наоборот (то есть для разрешения имен).
#include <netdb.h>
struct hostent *gethostbyname_r(const char *hostname,
struct hostent *result, char *buf, int buflen, int *h_errnop);
struct hostent *gethostbyaddr_r(const char *addr, int len,
int type, struct hostent *result, char *buf, int buflen,
int *h_errnop);
Обе функции возвращают: непустой указатель в случае успешного выполнения, NULL в случае ошибки
Для каждой функции требуется четыре дополнительных аргумента. Аргумент result
— это структура hostent
, размещенная в памяти вызывающим процессом и заполняемая данной функцией. При успешном выполнении функции этот указатель также является возвращаемым значением.
Аргумент buf
— это буфер, размещенный в памяти вызывающим процессом, a buflen
— его размер. Буфер будет содержать каноническое имя, массив указателей на псевдонимы, строки псевдонимов, массив указателей на адреса и сами адреса. Все указатели в структуре hostent
, на которую указывает result
, указывают на этот буфер. Насколько большим должен быть этот буфер? К сожалению, все, что сказано в большинстве руководств, это что-то неопределенное вроде «Буфер должен быть достаточно большим, чтобы содержать все данные, связанные с записью узла». Текущие реализации функции gethostbyname
могут возвращать до 35 указателей на альтернативные имена (псевдонимы), до 35 указателей на адреса и использовать буфер размером 8192 байт для хранения альтернативных имен (псевдонимов) и адресов. Поэтому буфер размером 8192 байт можно считать подходящим.
Если происходит ошибка, код ошибки возвращается через указатель h_errnop
, а не через глобальную переменную h_errno
.
ПРИМЕЧАНИЕ
К сожалению, проблема повторного вхождения гораздо серьезнее, чем может показаться. Во-первых, не существует стандарта относительно повторного вхождения и функций gethostbyname и gethostbyaddr. POSIX утверждает, что эти две функции не обязаны быть безопасными в многопоточной среде.
Во-вторых, не существует стандарта для функций _r. В этом разделе (в качестве примера) мы привели две функции _r, предоставляемые Solaris 2.x. В Linux присутствуют аналогичные функции, возвращающие hostent в качестве аргумента типа значение-результат. В Digital Unix и HP-UX имеются версии этих функций с другими аргументами. Первые два аргумента функции gethostbyname_r такие же, как и в версии Solaris, но оставшиеся три аргумента версии Solaris объединены в новую структуру hostent_data (которая должна быть размещена в памяти вызывающим процессом), а указатель на эту структуру — это третий и последний аргумент. Обычные функции gethostbyname и gethostbyaddr в Digital Unix 4.0 и в HP-UX 10.30 допускают повторное вхождение при использовании собственных данных потоков (см. раздел 23.5). Интересный рассказ о разработке функций _r Solaris 2.x содержится в [70].
Наконец, хотя версия функции gethostbyname, допускающая повторное вхождение, может обеспечить безопасность, когда ее одновременно вызывают несколько различных потоков, это ничего не говорит нам о возможности повторного вхождения для лежащих в ее основе функций распознавателя.
- 11.1. Введение
- 11.2. Система доменных имен
- 11.3. Функция gethostbyname
- 11.4 Функция gethostbyaddr
- 11.5. Функции getservbyname и getservbyport
- 11.6. Функция getaddrinfo
- 11.7. Функция gai_strerror
- 11.8. Функция freeaddrinfo
- 11.9. Функция getaddrinfo: IPv6
- 11.10. Функция getaddrinfo: примеры
- 11.11. Функция host_serv
- 11.12. Функция tcp_connect
- 11.13. Функция tcp_listen
- 11.14. Функция udp_client
- 11.15. Функция udp_connect
- 11.16. Функция udp_server
- 11.17. Функция getnameinfo
- 11.18. Функции, допускающие повторное вхождение
- 11.19. Функции gethostbyname_r и gethostbyaddr_r
- 11.20. Устаревшие функции поиска адресов IPv6
- 11.21. Другая информация о сетях
- 11.22. Резюме
- Упражнения
- Dynamic Host Configuration Protocol
- Add-On Prefix
- Сетевые функции
- 5.4.3 Host Configuration
- 11.18. Функции, допускающие повторное вхождение
- 11.3. Функция gethostbyname
- 11.4 Функция gethostbyaddr
- Аргументы функции в Python
- 3. Функции
- Новые функции API для работы с Blob и массивами
- Математические функции
- Размытые функции