Возможно вы искали: 'Piranha (1999)'

May 15 2025 18:20:36
  • Как сделать 8Gamers.Ru домашней страницей?
  • Игры
    • База данных по играх
    • Игровые новости
    • Игровая индустрия
    • Обзоры на игры
    • Прохождения игр
    • Гайды к играм
    • Превью о играх
    • Игровые тизеры
    • Игровые арты
    • Игровые обои
    • Игровые скриншоты
    • Игровые обложки
    • Игровые трейлеры
    • Игровое видео
    • Вышедшие игры
    • Ближайшие релизы игр
  • Кино и ТВ
    • База данных по кино
    • Статьи о кино
    • Постеры
    • Кадры из кино
    • Кино трейлеры
    • Сегодня в кино
    • Скоро в кино
  • Комиксы и манга
    • Манга по алфавиту
    • База данных по комиксах
    • Читать онлайн комиксы
    • Читать онлайн манга
    • База персонажей
  • Читы и коды
    • Чит-коды для PC игр
    • Чит-коды для консольных игр
    • Трейнеры
    • Коды Game Genie
  • Моддинг
    • Модификации
    • Карты к играм
    • Программы для моддинга
    • Статьи о моддинге
  • Геймдев
    • Всё о создании игр
    • Список движков
    • Утилиты в помощь игроделу
    • Конструкторы игр
    • Игровые движки
    • Библиотеки разработки
    • 3D-модели
    • Спрайты и тайлы
    • Музыка и звуки
    • Текстуры и фоны
  • Рецензии
    • Игры
    • Кино
    • Аниме
    • Комиксы
    • Мангу
    • Саундтреки
  • Саундтреки
    • Лирика
  • Файлы
    • Патчи к играм
    • Русификаторы к играм
    • Сохранения к играм
    • Субтитры к кино
  • Медиа
    • Видео
    • Фото
    • Аудио
    • Фан-арты
    • Косплей
    • Фото с виставок
    • Девушки из игр
    • Рисунки
    • Рисуем онлайн
    • Фотохостинг
  • Юмор
    • Анекдоты
    • Афоризмы
    • Истории
    • Стишки и эпиграммы
    • Тосты
    • Цитаты
  • Флеш
    • Азартные
    • Аркады
    • Бродилки
    • Гонки
    • Для девочек
    • Для мальчиков
    • Драки
    • Квесты
    • Леталки
    • Логические
    • Мультфильмы
    • Открытки
    • Приколы
    • Разное
    • Спорт
    • Стратегии
    • Стрелялки
Статистика

Статей: 87772
Просмотров: 96111483
Игры
Injustice:  Gods Among Us
Injustice: Gods Among Us
...
Dark Souls 2
Dark Souls 2
Dark Souls II - вторая часть самой хардкорной ролевой игры 2011-2012 года, с новым героем, сюжето...
Battlefield 4
Battlefield 4
Battlefield 4 - продолжение венценосного мультиплеер-ориентированного шутера от первого ли...
Кино
Steins;Gate
Steins;Gate
Любители японской анимации уже давно поняли ,что аниме сериалы могут дать порой гораздо больше пи...
Ку! Кин-дза-дза
Ку! Кин-дза-дза
Начинающий диджей Толик и всемирно известный виолончелист Владимир Чижов встречают на шумной моск...
Обзоры на игры
• Обзор Ibara [PCB/PS2] 18357
• Обзор The Walking ... 18801
• Обзор DMC: Devil M... 19879
• Обзор на игру Valk... 15877
• Обзор на игру Stars! 17764
• Обзор на Far Cry 3 17948
• Обзор на Resident ... 16024
• Обзор на Chivalry:... 17508
• Обзор на игру Kerb... 17981
• Обзор игры 007: Fr... 16619
Превью о играх
• Превью к игре Comp... 17960
• Превью о игре Mage... 14464
• Превью Incredible ... 14721
• Превью Firefall 13479
• Превью Dead Space 3 16334
• Превью о игре SimC... 14730
• Превью к игре Fuse 15442
• Превью Red Orche... 15542
• Превью Gothic 3 16343
• Превью Black & W... 17354
Главная » Статьи » Разное » Использование механизма kqueue/kevent в FreeBSD (select queue socket poll freebsd kqueue)

Использование механизма kqueue/kevent в FreeBSD (select queue socket poll freebsd kqueue)

Ключевые слова: select, queue, socket, poll, freebsd, kqueue, (найти похожие документы)

From: Andrey L. Kalinin <andrey@kalinin.ru>
Newsgroups: http://www.kalinin.ru
Date: Mon, 17 May 2004 18:21:07 +0000 (UTC)
Subject: Использование механизма kqueue/kevent в FreeBSD

Оригинал: http://www.kalinin.ru/programming/network/16_07_01.shtml


События ядра в FreeBSD.

Обработка большого количества сетевых соединений всегда
затруднительна. Мало того, не существует стандартных решений,
подходящих для проблем любого вида, в которых возникает большое
количество соединений. В этой статье пойдет речь о новом программном
интерфейсе, появившемся в FreeBSD, на примере организации web-сервера
при условии, что один компьютер все еще в состоянии обработать
поступающие к нему запросы.

Введение в проблему

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

Если для маленьких сайтов можно выделить некоторые стандартные
решения, такие как PHP или ASP, то большие сайты вынуждены
разрабатывать подобные решения самостоятельно. В этой статье будет
рассмотрен достаточно частный случай обработки запросов --- раздача
статической части сайта (к примеру, картинок) посетителям.

Кстати, на самом деле редко можно встретить сайт, который не
изменяется во времени, следовательно говорить о статичности можно
только с оговоркой о времени, в течение которого эта статичность
соблюдается. Практически любой сайт в интернете можно представить в
виде фиксированного и неизменного в течение некоторого промежутка
времени набора страниц, который отдается посетителю. Исключениями
являются несколько сильно нагруженных сайтов, вид страниц которых
зависит не от времени, когда пришел посетитель (к примеру, новости на
заглавной странице), а от личных предпочтений посетителя (как это
сделано в проекте "Мой Яндекс"), там действительно требуется чтобы
каждая страница создавалась бы по запросу посетителя. В остальных
случаях важно кеширование данных, временное представление их в
статическом виде, для того, чтобы серверам не требовалось бы
производить лишние вычисления при создании одной и той же страницы.

Другим ярким примером статических данных являются картинки. Мало того,
картинки, в отличие от текста, не требуют для себя никаких действий по
перекодировке "на лету" для посетителя (как это делает web-сервер
"Русский Apache"), поэтому обработка запросов посетителей web-сайтов
на картинки, составляющие часть содержимого страниц, значительно
проще, чем обработка запросов на текст тех же самых страниц. При этом
важно понять, что web-сервер с большим количеством возможностей, таких
как виртуальные сервера, исполнение cgi-скриптов, перекодировка
страниц, подключение различных модулей и т.д. не может эффективно
обрабатывать запросы на те же самые картинки, причем именно в
следствие слишком большого количества неиспользуемых возможностей. В
то же время, web-сервер, который способен без обработки отдавать
некоторую часть содержимого, очень прост в реализации. Именно о
способах реализации подобных web-серверов и пойдет речь далее в
статье. Понятно, что практически все, написанное ниже, справедливо для
любых сетевых приложений, картинки выбраны только в качестве примера.

Надо понимать, что внутреннее устройство web-сервера (программы)
должно предполагать, что несколько запросов могут поступить
одновременно. То есть, на сайт может обратиться посетитель из офиса
какой-нибудь крупной компании в Москве с хорошим подключением к
интернету и посетитель, подключившийся к сети по модему со скоростью,
к примеру, 19200, провайдер которого очень сильно экономит на своих
каналах. Посетитель с хорошим соединением не должен ждать, пока
web-сервер обслужит посетителя с плохим соединением, как, впрочем, и
наоборот. Таким образом, web-сервер должен "помнить" о по крайней мере
о том, какой посетитель какой документ запросил и сколько байт этого
документа он уже получил.

При использовании стека протоколов TCP/IP посетитель идентифицируется
web-сервером по своему ip-адресу, порту на клиентской машине и
ip-адрему с портом на серверной машине (у сервера может быть несколько
ip-адресов). Обычно, на серверной машине выделяется по одному порту на
каждого из посетителей сайта в текущий момент, следовательно работать
с ними можно пользуясь номерами портов и используемым ip-адресом. Вся
эта информация скрывается внутри сокетов, которыми можно оперировать
как обычными файлами.

Можно создавать для каждого посетителя сайта отдельный процесс с
копией web-сервера. Если это делать каждый раз по приходу посетителя и
потом процесс уничтожать, то такой подход становится чрезвычайно
расточительным, в связи с тем, что вызов для создания процессов,
fork(), является очень дорогостоящим: происходит создание копии
процесса-родителя (который вызвал fork()), что приводит к перемещениям
больших объемов данных из одного места оперативной памяти в другое.
Можно сразу же создать некоторое количество web-серверов в отдельных
процессах и передавать им управление при поступлении новых запросов.
Этот способ значительно лучше, тем не менее он хорошо подходит для
процессов, в которых время обработки запросов само по себе
значительно, а в данный момент мы рассматриваем обработку статических
данных, когда большую часть затраченного времени составляет именно
работа с сетью.

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


Недостатки select() и poll()

Остается действовать в пределах одного процесса и организовывать цикл
обработки сообщений от сетевого окружения операционной системы. Чаще
всего используются системные вызовы poll() или select(). Они во многом
похожи, поэтому я только рассмотрю select() в качестве примера, тем
более что poll() медленнее, чем select() (передается больше
параметров).

Обычно организуется бесконечный цикл вида:

fd_set rfds, wfds;
struct timeval tv;
tv.tv_sec = 0; tv.tv_usec = 500;

for( ; ; )
{
FD_ZERO(&rfds);
FD_ZERO(&wfds);

int max_fd = -1;

Необходимо поместить в rfds те файловые дескрипторы, из которых
требуется прочитать данные, а в wfds --- те, в которые требуется
записать данные. При этом max_fd --- максимальный файловый дескриптор,
помещенный в эти множества. Сам вызов select() выглядит примерно таким
образом:

select(max_fd + 1, &rfds, &wfds, NULL, &tv);

select() ждет максимум столько времени, сколько указано в tv, после
чего возвращает модифицированные множества rfds и wfds (и еще одно
множество, вместо которого в данном случае передан NULL), которые
теперь содержат в себе только файловые дескрипторы, готовые для чтения
или записи соответственно. Таким образом, часть цикла после select()
выглядит как проверка соответствующих дескрипторов на принадлежность
множествам, примерно вот так:

for( ... )
if(FD_ISSET(fd, &rfds)) { ... }

for( ... )
if(FD_ISSET(fd, &wfds)) { ... }

/*
* ...
*/
}

При этом fd это переменная, принимающая значения каждого из
дескрипторов, которыми были инициализированы множества rfds и wfds.

Необходимо обратить внимание на то, что конкретно происходит каждый
раз, когда выполняется внутренность цикла. Во-первых, инициализация
множеств. fd_set является битовым массивом, где индексом служит
числовое значение файлового дескриптора. Эти массивы копируются из
адресного пространства процесса в адресное пространство ядра при
вызове select() и потом копируются обратно при возврате. Надо
отметить, что подобные операции достаточно длительны и для их
сокращения, в select() передается максимальный дескриптор --- на самом
деле копируется часть массивов от нулевого до максимального
дескриптора.

Кстати сказать, принципиальное отличие poll() от select() лишь в том,
как передаются аргументы. В случае poll() это массив структур, а не
битовый массив (множество).

Понятно, что при большом количестве соединений количество открытых
дескрипторов увеличивается и, тем самым, копируется все большее и
большее количество данных из процесса в ядро и обратно. При этом,
внутри ядра точно так же производятся какие-то действия для
преобразования битовых массивов в свои внутренние структуры (и
последующего их удаления, когда приходит время возврата результатов
вызывающему процессу). Собственно, poll() будет медленнее при большом
количестве открытых дескрипторов, потому что размер копируемых данных
на порядок больше, чем в случае select().

Затем следует "пробежка" по множествам с целью поиска "готовых"
дескрипторов. Опять же, в случае большого количества соединений эта
операция достаточно длительна.

Если на каждую запись или чтение выполняется какая-то своя длительная
операция, со своими внутренними циклами, ветвлениями и прочим, то все
это не особенно важно. Но если обработка полученных дескрипторов
сводится к копированию данных из внутренних буферов в дескрипторы и
обратно, то выполнение бесконечного цикла обработки select(),
копирования данных при вызове, а так же пробежка по битовым массивам
при помощи макросов FD_ISSET() становится, по сути, единственными
действиями, которые выполняет web-сервер. На самом деле, именно эти
операции ограничивают его производительность в случае обработки
запросов на "простое, не изменяемое во времени содержимое", например,
картинки.

Понятно, что проблема опять же в количестве открытых соединений (как и
в случае обработки динамического содержимого), но тут можно произвести
некоторую оптимизацию, например при помощи такой возможности HTTP, как
keep-alive. Эта возможность позволяет обрабатывать несколько
HTTP-запросов в пределах одного TCP-соединения (в то время как обычные
запросы разрывают соединение). Очень полезная возможность, так как
обычно на одной странице находится несколько картинок, которые
необходимо выкачать посетителю web-сайта и если на каждую из них
каждый посетитель установит несколько соединений с сервером, то
количество дескрипторов в цикле выше возрастет, в то же время, если на
все картинки посетитель установит только одно соединение, то
количество соединений упадет (в идеале) во столько раз, сколько в
среднем располагается картинок на страницах сайта. И при этом не стоит
забывать, что сократится количество данных внутри ядра, отвечающих за
передачу информации через стек протоколов TCP/IP, а так же уменьшится
объем передаваемой служебной информации TCP/IP.

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

Напрашивается решение в виде внесения web-сервера внутрь ядра
операционной системы. Этот подход реализован в khttpd или tux для
Linux. На мой взгляд, решение совершенно некрасивое и, кроме того,
неудобное, потому что, к примеру, перезапустить подобный сервер можно
только рестартом ядра (или при перезагрузке компьютера). Кроме того,
писать подобное программное обеспечение самостоятельно (потому что
такие задачи возникают постоянно и для их решения обычно берется за
основу какой-нибудь существующий простой и быстрый web-сервер)
затруднительно в силу неудобства отладки. И, наконец, последний довод:
любое изменение ядра операционной системы, которое нужно только одному
пользователю этой операционной системы, никогда не войдет в
официальную и поддерживаемую версию операционной системы. А это
чревато тем, что пользователь так и останется со старой операционной
системой еще долгое время или будет изменять ядро в каждой новой
версии.


Краткое описание механизма kqueue

Другое решение заключается в предоставлении программного интерфейса
оповещения процесса о событиях иного вида, чем описанный выше. Именно
этот путь был реализован в FreeBSD при помощи введения нового
механизма оповещения процесса о событиях, произошедших в ядре
операционной системы, называемого kqueue. Идея состоит в том, что
процесс в какой-то момент времени оповещает ядро о том, события какого
вида и для каких объектов он хотел бы отслеживать, после чего
посредством специального вызова получает список объектов,
удовлетворяющих этому требованию.

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

Кроме того, механизм kqueue позволяет отслеживать значительно большое
количество событий в операционной системе, чем select() или poll() и
является расширяемым таким образом, что в будущем, вполне вероятно,
будет возможно получение напрямую событий от драйверов устройств.

Использовать kqueue не сложнее, чем select(). Сама очередь событий
создается при помощи вызова kqueue():

int kq;

...

kq = kqueue();

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

kevent kq_change_list[NCHANGE];
int kq_chlist_used = 0;
kevent kq_events[NEVENTS];
int n, i;


...

EV_SET(kq_change_list + kq_chlist_used, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
kq_chlist_used++;
...

n = kevent(kq, kq_change_list, kq_chlist, kq_events, NEVENTS, &tv);

if(n <= 0) { /* Ошибка */ }

for(i = 0; i < n; i++)
{
if(kq_events[i].flags & EV_ERROR) { /* Ошибка */ }

switch(kq_events[i].filter)
{
case EVFILT_READ: /* чтение из дескриптора kq_events[i].ident */
case EVFILT_WRITE: /* запись в дескриптор kq_events[i].ident */

...

}
}

kq_chlist_used = 0;

По сравнению с select(), можно видеть, что исчезла обязательная
инициализация массивов в начале цикла (предварительный EV_SET перед
kevent() приведен только в качестве примера установки события, событие
регистрируется только один раз, а не при каждой итерации цикла) и
исчезла "пробежка" по всем интересующим дескрипторам, так как в
kq_events не будет содержаться лишних дескрипторов.

Механизм kqueue специфичен для FreeBSD и в других операционных
системах его нет, хотя в некоторых коммерческих представителях
семейства Unix есть аналогичные решения (точнее, решающие похожие
задачи). Тем не менее, так как изначально задача стояла как обработка
большого числа запросов на статическую информацию, для ее решения
вполне возможно выделить отдельный сервер, на котором была бы
установлена операционная система FreeBSD и работал бы web-сервер на
основе kqueue.

На сегодняшний день только у одного публично доступного web-сервера
thttpd есть поддержка kqueue. Кроме того, мне известна модификация
web-сервера mathpod для kqueue.

Загрузка процессора при смене select() на kqueue уменьшается на
порядок. Это очень хороший результат, хотя стоит помнить о том, что
зависимость загрузки процессора от количества обрабатываемых
соединений не является линейной. Впрочем, численное сравнение этих
методов не является целью данной статьи, интересующиеся могут
воспользоваться ссылками в конце для более подробного сравнения
использования select()/poll() и kqueue.


Подведение итогов

Итак, при обработке запросов на неизменяемую часть содержимого
web-сайта, необходимо использовать иной web-сервер, чем тот, который
обрабатывает тексты и генерирует страницы сайта. Если физический
сервер один, то логично разделить обработку запросов на страницы сайта
и картинки (к примеру) по портам. Например, Apache на 80 порту для
текстов и thttpd или mathpod на 8080 порту для картинок. Если же
нагрузка настолько велика, что основной web-сервер "мешается"
web-серверу для картинок, то под обработку подобных запросов придется
выделить отдельный физический сервер (таким образом, разделение
запросов будет происходить по доменным именам или ip-адресам).

Список маленьких web-серверов, а так же их сравнительные
характеристики, можно найти на официальной страничке web-сервера
thttpd (ссылка в конце статьи). Для демонстрации сравнительных
характеристик приведу сокращенную версию этой таблицы (данные на 1998
год, но интересны не абсолютные, а относительные единицы измерения):
http://www.acme.com/software/thttpd/benchmarks.html


thttpd
http://www.acme.com/software/thttpd/
Apache
http://www.apache.org/
mathopd
http://mathop.diva.nl/
Roxen
http://www.roxen.com/
Boa
http://www.boa.org/
Jigsaw
http://www.w3.org/Jigsaw/
Acme.Serve
http://www.acme.com/java/software/Acme.Serve.Serve.html
CERN
http://www.w3.org/hypertext/WWW/Daemon/Status.html
NCSA
http://hoohoo.ncsa.uiuc.edu/
Netscape FastTrack, Netscape Enterprise
http://home.netscape.com/
Zeus
http://www.zeus.co.uk/


Полную версию этой таблицы можно найти здесь (http://www.acme.com/software/thttpd/benchmarks.html).

Еще раз напоминаю, что данные на 1998 год и, следовательно, thttpd
тестировался без kqueue. Кроме того, все подобные тесты проводятся "в
вакууме", то есть с одного-двух ip-адресов в локальной сети и,
следовательно, нагрузки на реализацию стека протоколов TCP/IP не
такие большие, как в реальной жизни. Таким образом, в 1998 реальные
цифры были меньше, чем приведенные в таблице и отличаться они могли в
несколько раз при росте количества одновременных соединений (для
маленького количества соединений цифры отличались бы не сильно).

Еще одно интересное место в этой таблице, на которое хочется обратить
внимание --- производительность web-серверов, написанных на Java. Это
место я оставлю без комментариев.

На мой взгляд, для сервера раздачи картинок лучше всего подходят:
* thttpd, так как в нем есть поддержка kqueue изначально.
* mathpod, потому что в нем есть нормальная реализация HTTP
keep-alive. В случае замены select() на kqueue становится более
предпочтительным, чем thttpd. Кроме того, он проще чем thttpd (в
два раза меньше строчек исходного кода), что тоже является
несомненным плюсом. Если из него изъять кучу ненужных
возможностей, таких как поддержка виртуальных серверов, то лучше
варианта не найти.

Резюме

Реальное применение kqueue на загруженном веб-сервере помогло
увеличить его производительность как минимум в два раза (учитывая то,
что он не только отдает картинки, но и предварительно их обсчитывает).
Точное увеличение при замене его на сервере, отдающего уже сжатые
картинки, неизвестно, потому что даже в локальной сети не удалось
нагрузить компьютер на все 100%. Так что --- рекомендую.

PS

В свое время Gregory Liokumovich рассказывал
(http://www.kalinin.ru/programming/network/12_12_00.shtml)
о событийной модели в WinSock, которая очень похожа, как я себе
это понимаю, на kqueue. С другой стороны, я не знаком с внутренним
устройством Windows так, чтобы утверждать насколько велика разница
в производительности при использовании select() или
WSAWaitForMultipleEvents(). Gregory использовал в качестве примера
многопоточную модель TCP-сервера, так что там вообще сложно говорить
о необходимости в высокой производительности непосредственно TCP.


* man 2 kqueue - Страница оперативной документации FreeBSD kqueue(2).
* http://people.freebsd.org/~jlemon/kqueue.pdf - "Kqueue: A generic and
scalable event notification facility", статья автора kqueue Джонотана Лемона.
* http://www.monkeys.com/freeware/kqueue-echo.c - пример использования kqueue
в реализации сервера echo.
885 Прочтений •  [Использование механизма kqueue/kevent в FreeBSD (select queue socket poll freebsd kqueue)] [08.05.2012] [Комментариев: 0]
Добавил: Ukraine Vova
Ссылки
HTML: 
[BB Url]: 
Похожие статьи
Название Добавил Добавлено
• Использование механизма kqueue/keve... Ukraine Vova 08.05.2012
Ни одного комментария? Будешь первым :).
Пожалуйста, авторизуйтесь для добавления комментария.

Проект входит в сеть сайтов «8Gamers Network»

Все права сохранены. 8Gamers.NET © 2011 - 2025

Статьи
Рецензия на Pressure
Рецензия на Pressure
Чтобы обратить на себя внимание, начинающие маленькие разработчики, как правило, уходят в жанры, ...
Рецензия на Lost Chronicles of Zerzura
Рецензия на Lost Chron...
Игры, сделанные без любви и старания, похожи на воздушный шар – оболочка есть, а внутри пусто. Lo...
Рецензия на The Bridge
Рецензия на The Bridge
«Верх» и «низ» в The Bridge — понятия относительные. Прогуливаясь под аркой, можно запросто перей...
Рецензия на SimCity
Рецензия на SimCity
Когда месяц назад состоялся релиз SimCity, по Сети прокатилось цунами народного гнева – глупые ош...
Рецензия на Strategy & Tactics: World War 2
Рецензия на Strategy &...
Название Strategy & Tactics: World War II вряд ли кому-то знакомо. Зато одного взгляда на ее скри...
Рецензия на игру Scribblenauts Unlimited
Рецензия на игру Scrib...
По сложившейся традиции в информационной карточке игры мы приводим в пример несколько похожих игр...
Рецензия на игру Walking Dead: Survival Instinct, The
Рецензия на игру Walki...
Зомби и продукция-по-лицензии — которые и сами по себе не лучшие представители игровой биосферы —...
Обратная связь | RSS | Донейт | Статистика | Команда | Техническая поддержка