Date: Tue, 9 Apr 2002 22:17:00 +0000 (UTC)
From: Lev Walkin <vlm@netli.com>
Newsgroups: fido7.ru.unix.bsd
Subject: [FreeBSD] - ACCEPT_FILTER_* в конфигурации ядра
> что полезного можно извлечь при использовании следующих опций в конфигурации
> ядра? комментарии в LINT отсутствуют. читать исходники не предлагать.
>
> === Cut ===
> # Statically Link in accept filters
> options ACCEPT_FILTER_DATA
> options ACCEPT_FILTER_HTTP
> === Cut ===
Это поддержка фильтров для accept(2)'а. Позволяет процессу не
переключаться на обслуживание нового коннекта, пока по этому
коннекту не пришли данные или запрос HTTP, соответственно.
Позволяет выжать еще несколько процентов производительности
из тяжело нагруженных серверов, принимающий большое количество
коннектов в единицу времени. Технология придумана в Yahoo,
затем причесана и помещена в OS в качестве полезной фичи.
Используется, например, в web-сервере Apache. Механизм
использования этой фичи такой, что неважно, вкомпилирована
ли она в ядро с использованием вышеупомянутых опций, или нет:
если в ядре этого еще нет, то будет подгружен соответствующий
модуль.
Два года назад во FreeBSD появились accept-фильтры. Они позволяют не
передавать в accept() пришедшее соединение до тех пор, пока не придёт
первый пакет с данными (фильтр dataready) или заголовок HTTP-запроса
(фильтр httpready). Использование фильтров в Apache (а в нём они
поддерживаются, начиная с версии 1.3.14) позволяет уменьшить число
процессов. В серверах, использующих select(), poll() или kqueue(),
например, в thttpd-2.22, фильтры уменьшают число открытых файлов. И
наконец, в обоих случаях accept-фильтры уменьшают число переключений
контекста процесса.
Идея accept-фильтра принадлежит одному из основателей Yahoo! Дэвиду
Фило (David Filo). В Yahoo! использовался фильтр для запросов HTTP.
15.06.2000 Alfred Perlstein внёс во FreeBSD 5.0-CURRENT код этого
фильтра и добавил фильтр для первого пакета с данными. Для включения
фильтра нужно было с помощью setsockopt(2) установить опцию
SO_DELAYACCEPT или SO_HTTPACCEPT для сокета, который принимал
соединения. Но этот вариант просуществовал не больше трёх суток - уже
18.06.2002 он был удалён и 20.06.2000 во FreeBSD 5.0-CURRENT появилась
новая, более общая реализация accept-фильтров. Теперь фильтры
включались с помощью опции SO_ACCEPTFILTER, вместе с которой
передавался указатель на структуру, описывающую фильтр. Для
современных фильтров в этой структуре нужно передать только имя
фильтра - dataready или httpready. В отличие от первой реализации,
каждый фильтр нужно явно указать в конфигурации ядра:
или же загрузить как ядерный модуль. Именно это вариант появился
28.07.2000 во FreeBSD 4.1-STABLE. Подробнее об использовании
accept-фильтрах можно прочитать в setsockopt(2), accf_data(9) и
accf_http(9).
Надо заметить, что первоначальная реализация фильтра httpready была
далека от суровой реальности, так как поддерживала только протоколы
HTTP/1.x и не поддерживала протокол HTTP/0.9 - если первый пришедший
пакет начинался со строки "GET ", то фильтр не передавал соединение в
очередь завершённых соединений до тех пор, пока не находил во входном
потоке пустую строку. Однако 6.09.2000 во FreeBSD 5.0-CURRENT и
20.09.2000 во FreeBSD 4.1-STABLE (практически перед самым
4.1.1-RELEASE) фильтр httpready был переписан - при получении запроса,
непохожего на "^(GET|HEAD) .+ HTTP/1.x", соединение сразу же
помещается в очередь завершённых соединений.
Оба фильтра - dataready и httpready не предполагали никаких таймаутов,
но при переполнении очереди входящих соединений вызывалась функция
sodropablereq(), которая удаляла старые незавершённые соединения или
же соединения, завершённые с точки зрения TCP/IP, но незавершённые с
точки зрения фильтра.
После того, как во FreeBSD 5.0-CURRENT и 4.4-STABLE появился syncache,
с accept-фильтрами стали возникать некоторые проблемы. Первая из них,
по-видимому, наблюдалась только с httpready фильтром - после того, как
соединение было передано accept()'у, следущий за ним read() ничего не
мог прочитать. Эта ошибка была исправлена 21.12.2001 во FreeBSD
5.0-CURRENT и 27.12.2001 во FreeBSD 4.5-PRERELEASE. Недавно
разработчики обнаружили, что сервер, использующий accept-фильтры,
может быть легко подвержен DoS-атаке. Как уже говорилось, до syncache
избытком незавершённых соединений занималась функция sodropablereq(),
но в syncache эта функция не используется и в результате очередь легко
переполняется соединениями, незавершёнными с точки зрения фильтра.
26.04.2002 ошибка была исправлена во FreeBSD 5.0-CURRENT, а
1.05.2002 - во FreeBSD 4.5-STABLE.
Была идея на основе accept-фильтров реализовать ядерный http-сервер,
который бы возвращал в accept() те запросы, которые не мог обработать
сам, но она, по-видимому, так и осталась нереализованной.
Надо заметить, что accept-фильтры - не единственное решение отложить
accept() до прихода данных. В Linux 2.4.x можно установить опцию
SO_DEFER_ACCEPT, а в Windows NT 3.51 появилась функция AcceptEx().
(C) Igor Sysoev http://www.sysoev.ru
695 Прочтений • [[FreeBSD] - ACCEPT_FILTER_* в конфигурации ядра (socket filter bsd)] [08.05.2012] [Комментариев: 0]