Александр Крижановский, NatSys Lab

1,813 views

Published on

HighLoad++ 2013

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,813
On SlideShare
0
From Embeds
0
Number of Embeds
1,017
Actions
Shares
0
Downloads
14
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Александр Крижановский, NatSys Lab

  1. 1. Про сокеты и миллионы пакетов в секунду с одного CPU ядра Александр Крижановский
  2. 2. О сокетах (серверных) ● ● ● ● ● ● путь пакета в Linux с адаптера в TCP-сокет; установление новых соединений, мультиплексирование и чтение из сокетов; как ускорить прикладной сервер (оптимизация accept(2), MSI-X и RPS/RFS, GRO); можно быстрее: Oracle Reliable Datagram Sockets еще быстрее: переход к полностью синхронным сокетам В основном интересовала скорость установления соединений
  3. 3. Путь пакета (recv) 1.RSS (Receive Side Scaling) 2.MSI-X очереди 3.DCA (Direct Cache Access) => User/kernel: копирования => асинхронность получения и чтения
  4. 4. Типовой сценарий     listen(listen_sd, 5);     epoll_ctl(wd, EPOLL_CTL_ADD, listen_sd, &ev);     while (1) {         n = epoll_wait(wd, ev, 64, ­1);         for (int i = 0; i < n; ++i) {             if (ev[i].data.fd == listen_sd) {                 new_sd = accept(listen_sd, NULL, NULL);             } else {                 recv(ev[i].data.fd, msg, READ_SZ, 0);             }         }     }
  5. 5. Чтение из сокета: recvfrom(2) ● ● sockfd_lookup_light() - захватывает файловый дескриптор, получает соответствующий сокет sock­>ops­>recvmsg() => tcp_recvmsg() – skb_queue_walk(&sk­>sk_receive_queue, skb) — проходим по списку буферов ● skb_copy_datagram_iovec() - копируем ● tcp_cleanup_rbuf() - очищаем место в буфере и отправляем ACK ● tcp_rcv_space_adjust() - пересчитываем свободное место в сокетном буфере
  6. 6. Мультиплексирование: epoll_wait(2) ● ● ● epoll_wait() → ep_poll() →  __add_wait_queue_exclusive() — встаем в очередь ожидания и засыпаем epoll_ctl(EPOLL_CTL_ADD) → ep_insert() →  tcp_poll() — добавляет сокет в очередь ожидания tcp_v4_rcv() → tcp_v4_do_rcv() →  tcp_rcv_state_process() → tcp_data_queue() →  sk­>sk_data_ready() – ep_poll_callback() - разбудить процессы в очереди ожидания
  7. 7. Установление новых соединений: listen(2) & accept(2) ● ● ● inet_csk_listen_start() - аллоцирует место в очереди, равное backlog (второй аргумент listen(2)) inet_csk_accept() →  inet_csk_wait_for_connect() → wait (schedule) tcp_v4_do_rcv() → tcp_child_process() – – tcp_rcv_state_process() ● tcp_set_state(sk, TCP_ESTABLISHED); sk­>sk_state_change(sk); parent­>sk_data_ready(parent, 0);
  8. 8. Пример переключения контекста: «Наш клиент?» 1.epoll_wait(2) 2.accept(2) 3.getpeername(2) 4.=> не наш клиент: close(2) 7 контекст свитчей Хорошо: user/kernel context switch не инвалидирует кэши
  9. 9. Пример переключения контекста: «Наш клиент?» 1.epoll_wait(2) 2.accept(2) 3.getpeername(2) 4.=> не наш клиент: close(2) 7 контекст свитчей Хорошо: user/kernel context switch не инвалидирует кэши
  10. 10. Что если приходит сразу много клиентов?     listen(listen_sd, 5);     epoll_ctl(wd, EPOLL_CTL_ADD, listen_sd, &ev);     while (1) {         n = epoll_wait(wd, ev, 64, ­1);         for (int i = 0; i < n; ++i) {             if (ev[i].data.fd == listen_sd)                 new_sd = accept(listen_sd, NULL, NULL);         }     }
  11. 11. Оптимизация accept(2) BRECHT T., PARIAG D., GAMMO L. «accept()able strategies for improving web server performance.»     listen(listen_sd, 1000);     fcntl(sd, F_SETFL, flags | O_NONBLOCK);     epoll_ctl(wd, EPOLL_CTL_ADD, listen_sd, &ev)       while (1) {         n = epoll_wait(wd, ev, 64, ­1);         for (int i = 0; i < n; ++i) {             if (ev[i].data.fd == listen_sd)                 do {                     new_sd = accept(listen_sd, NULL, NULL);                 } while (new_sd >= 0);         }     }
  12. 12. MSI-X, RPS, RFS 1. RSS (Receive Packet Steering) 2. RFS (Receive Flow Steering) 3. MSI-X не всегда хорошо балансирует трафик => RPS 4. Но MSI-X - «железный»
  13. 13. Сокетные каллбеки ● ● ● ● ● ● sk_data_ready — вызывается при получении данных на сокете sk_state_change — изменение состояния сокета sk_write_space — у сокета появилось место в буфере записи sk_error_report — ошибка на сокете sk_backlog_rcv — чтение из очереди отложенных сегментов (!tcp_low_latency) sk_destruct — вызывается на удалении сокета
  14. 14. Oracle Reliable Datagram Sockets (RDS) Ядерная реализация сокетов (linux/net/rds): ● нет копирований и переключений контекстов ● основная работа происходит на калбеках сокетов Функции, необходимые accept(), могут спать => wait queue
  15. 15. RDS accept     int rds_tcp_accept_one(struct socket *sock) {         sock­>ops­>accept(sock, new_sock, O_NONBLOCK);     }     void rds_tcp_accept_worker(struct work_struct *work) {         while (rds_tcp_accept_one(rds_tcp_listen_sock) == 0);     }     DECLARE_WORK(rds_tcp_listen_work, rds_tcp_accept_worker);     void rds_tcp_listen_data_ready(struct sock *sk, int bytes) {         if (sk­>sk_state == TCP_LISTEN)             queue_work(rds_wq, &rds_tcp_listen_work);     }     sock­>sk­>sk_data_ready = rds_tcp_listen_data_ready;
  16. 16. Synchronous Sockets ● ● ● ● Всё делается только в softirq Хорошо подходят для большого числа короткоживущих соединений Нужен патч ядра Могут быть вынесены в некрасивую библиотеку ядра: – – upcalls & downcalls Но код с ней в ~2 раза короче (200 vs 364)
  17. 17. Synchronous Sockets: пример     struct { SsProto proto;} my_proto;      struct socket *listen_sock;     int my_read(void *proto, char *data, int len) {         /* Read application level data. */     }     int my_conn_new(struct sock *sock) {         /* Handle a new TCP connection */     }     SsHooks ssocket_hooks = {.connection_new = my_connection_new};     int my_init(void) {         ss_hooks_register(&ssocket_hooks);         ss_proto_push_handler((SsProto *)&my_proto, my_read);         ss_tcp_set_listen(listen_sock­>sk, (SsProto *)&my_proto);     }
  18. 18. Скорость установления соединений
  19. 19. Запросы в скунду в зависимости от числа соединений
  20. 20. Спасибо! Synchronous Sockets (+патч): https://github.com/krizhanovsky/sync_socket ak@natsys-lab.com

×