Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Linux API с точки зрения разработчика веб-сервера / Валентин Бартенев (NGINX, Inc.)

0 views

Published on

РИТ++ 2017, Backend Conf
Зал Кейптаун, 6 июня, 15:00

Тезисы:
http://backendconf.ru/2017/abstracts/2710.html

В данном докладе я дам обзор системных интерфейсов, которые предоставляет Linux для эффективной обработки запросов. В частности, речь пойдет о мультиплексировании ввода-вывода, отправке файлов и многопоточной обработке входящих соединений. Расскажу о нюансах и недостатках в сравнении с аналогичными интерфейсами других unix-подобных операционных систем. Личный опыт показывает, что продуманность и качество реализации интерфейса для прикладных программ — это, к сожалению, довольно слабая сторона ядра Linux.

Published in: Engineering
  • Be the first to comment

  • Be the first to like this

Linux API с точки зрения разработчика веб-сервера / Валентин Бартенев (NGINX, Inc.)

  1. 1. с точки зрения разработчика высокопроизводительного веб сервера Валентин Бартенев
  2. 2. Сверх краткое введение • набор системных вызовов ядра и библиотек • Ядро • Взаимодействие с оборудование • Управление памятью • Управление процессами • Работа с файловыми системами • Сетевые протоколы •
  3. 3. Обработка соединений Традиционный подход Мультиплексирование I/O
  4. 4. Цикл обработки соединений connections[] массив соединений handle_connction() обработчик соединения loop { for (i = 0; i < count; i = i + 1) { handle_connection(connections[i]); } }
  5. 5. Цикл с задержкой loop { for (i = 0; i < count; i = i + 1) { handle_connection(connections[i]); } sleep(200ms) }
  6. 6. Эффективный цикл loop { count = wait_for_events(connections); for (i = 0; i < count; i = i + 1) { handle_connection(connections[i]); } }
  7. 7. Существующие механизмы • Неэффективны при большом количестве соединений Эффективные механизмы • • •
  8. 8. Базовый интерфейс Создание экземпляра int epoll_create(); Регистрация дескрипторов int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); Ожидание событий int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
  9. 9. Параметры int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); экземпляр тип операции отслеживаемый дескриптор struct epoll_event { uint32_t events; битовая маска типов событий epoll_data_t data; пользовательские данные };
  10. 10. События struct epoll_event { uint32_t events; битовая маска типов событий epoll_data_t data; пользовательские данные }; Основные флаги событий чтение запись ошибка закрытие соединения
  11. 11. Детектирование закрытия соединения • В появился новый флаг • На ядрах старше необходимо вычитывать все данные и делать на один системный вызов больше • тихо игнорируется старыми ядрами • Невозможно отличить отсутствие события от неработоспособности флага • Приходится тестировать работоспособность
  12. 12. Странности int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); Типы операций добавление изменение удаление Можно ли было обойтись двумя А одной int epoll_ctl(epfd, fd, *event);
  13. 13. Статистика системных вызовов на примере nginx в режиме проксирования:
  14. 14. Хитрости и • После вызов не нужен удаление происходит автоматически • На ядрах до версии и после вызов перед вызовом повышает производительность
  15. 15. Интерфейс против epoll_ctl() epoll_wait() epoll_pwait() epoll_create() epoll_create1() eventfd() eventfd2() timerfd_create() timerfd_settime() timerfd_gettime() signalfd() signalfd4() inotify_init() inotify_init1() inotify_add_watch() inotify_rm_watch() kqueue() kevent()~=
  16. 16. Линус о • и связанные • слов слов
  17. 17. Асинхронная работа с файлами и т д в • Обертка от в пользовательском пространстве • Плохо масштабируется высокие накладные расходы и т д • Требования к выравниванию • Работает только в режиме
  18. 18. Собственный пул потоков в • Пулы потоков ускоряем в и более раз • Лишние накладные расходы если данные в памяти • Предложенные решения так и не включены в ядро • с флагом •
  19. 19. Отправка файлов Обычный способ: loop { read(file -> buf); write(buf -> connection); } Эффективный способ sendfile(file -> connection);
  20. 20. Интерфейс ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count); int sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr, off_t *sbytes, int flags); ssize_t sendfilev(int fildes, struct sendfilevec *vec, int sfvcnt, size_t *xferred);
  21. 21. Особенности в свежих ядрах ret = sendfile(out_fd, in_fd, offset, count); до • только в самом начале • эквивалентно после • в произвольный момент • Не отличить от при • Требуется на один вызов больше
  22. 22. и • В и работает иначе чем в большинстве других подобных системах и позволяет • Избежать борьбы за лок на сокете • Равномерно распределять соединения • Складывать пакеты от одного отправителя в один и тот же процесс • Теряет соединения при закрытии • В при реализации консультировались с разработчиками и проблему решили
  23. 23. Спасибо за внимание Валентин Бартенев

×